Changes ignored salvage into trash salvage according to #22

This commit is contained in:
Kristian Knarvik 2024-05-05 18:02:12 +02:00
parent c71d664a79
commit 21d55563b7
6 changed files with 77 additions and 67 deletions

View File

@ -59,10 +59,10 @@ annoying:
A scrapper will produce salvage for a damage-able item by calculating the amount of items returned based on items in the A scrapper will produce salvage for a damage-able item by calculating the amount of items returned based on items in the
recipe, and the percentage of durability left on the item. To avoid returning relatively worthless items instead of recipe, and the percentage of durability left on the item. To avoid returning relatively worthless items instead of
valuable items, `ignoredSalvage` can be configured. If the item is fully repaired, the worthless items will be returned valuable items, `trashSalvage` can be configured. Trash salvage will only be returned if all non-trash items are
as well, but otherwise, only the valuable items are considered as possible salvage. A scrapper will by default only returned as well. A scrapper will by default only salvage damage-able items (same as blacksmiths), but enabling
salvage damage-able items (same as blacksmiths), but enabling `extendedSalvageEnabled` for a scrapper will allow it to `extendedSalvageEnabled` for a scrapper will allow it to salvage any crafting table recipe. Note that to salvage for
salvage any crafting table recipe. Note that to salvage for example planks into wood, four wood will be taken. example planks into wood, four wood will be taken.
When an item is salvaged, EXP will be returned based on enchantments on the item. When an item is salvaged, EXP will be returned based on enchantments on the item.
@ -213,12 +213,12 @@ All currently supported presets, and available filters for each preset:
### Scrapper global-only options ### Scrapper global-only options
| Key | Value type | Description | | Key | Value type | Description |
|----------------|----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |----------------|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| basePrice | positive decimal number | The cost of using a scrapper | | basePrice | positive decimal number | The cost of using a scrapper |
| showExactTime | true/false | If true, scrappers will display exact time remaining in minutes and seconds, instead of vague expressions | | showExactTime | true/false | If true, scrappers will display exact time remaining in minutes and seconds, instead of vague expressions |
| giveExperience | true/false | If true, each enchantment level on the salvaged item will give one EXP level as salvage | | giveExperience | true/false | If true, each enchantment level on the salvaged item will give one EXP level as salvage |
| ignoredSalvage | TARGET_MATERIAL:IGNORED_MATERIAL | The items that should be ignored when calculating partial salvage. Because receiving just the sticks when salvaging a diamond pickaxe is kind of sad, this allows specifying for example: `*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD:STICK` (the default) for ignoring sticks in salvage for shovels, pickaxes, axes, hoes and swords. A `:` character splits selected items and the ignored salvage. Different item specifications are split by a `;` character. Use `,` to split separate ignored salvages, like: `SHIELD:STICK,BOW_STRING` | | trashSalvage | TARGET_MATERIAL:IGNORED_MATERIAL\[,\*_TARGET2:\*_IGNORED2] | The items that should be deferred when calculating partial salvage. Because receiving just the sticks when salvaging a diamond pickaxe is kind of sad, this allows specifying for example: `*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK` (the default) for deferring sticks in salvage for shovels, pickaxes, axes, hoes and swords. A `:` character splits selected items and the trash salvage. Different item specifications are split by a `;` character. Use `,` to split separate trash salvages when using commands, like: `SHIELD:STICK,BOW_STRING` |
### Scrapper per-npc (with default values set in config.yml) ### Scrapper per-npc (with default values set in config.yml)

View File

@ -25,7 +25,7 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
private final Map<ScrapperSetting, Object> settings = new HashMap<>(); private final Map<ScrapperSetting, Object> settings = new HashMap<>();
private final List<Material> defaultSalvageableMaterials = new ArrayList<>(); private final List<Material> defaultSalvageableMaterials = new ArrayList<>();
private final Map<Material, Set<Material>> ignoredSalvage = new HashMap<>(); private final Map<Material, Set<Material>> trashSalvage = new HashMap<>();
private final BlacksmithPlugin instance; private final BlacksmithPlugin instance;
@ -144,7 +144,7 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
} }
} }
loadSalvageAbleItems(); loadSalvageAbleItems();
loadIgnoredSalvage(); loadTrashSalvage();
} }
/** /**
@ -182,17 +182,17 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
} }
/** /**
* Gets ignored salvage for the given material * Gets trash salvage for the given material
* *
* <p>The ignored salvage should be ignored when calculating item salvage, in order to increase the probability of * <p>The trash salvage should be deferred when calculating item salvage, in order to increase the probability of
* getting the more expensive materials back.</p> * getting the more expensive materials back.</p>
* *
* @param material <p>The material to get ignored salvage for</p> * @param material <p>The material to get trash salvage for</p>
* @return <p>The ignored salvage</p> * @return <p>The trash salvage</p>
*/ */
@Nullable @Nullable
public Set<Material> getIgnoredSalvage(@NotNull Material material) { public Set<Material> getTrashSalvage(@NotNull Material material) {
return this.ignoredSalvage.get(material); return this.trashSalvage.get(material);
} }
/** /**
@ -207,40 +207,40 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
} }
/** /**
* Loads all ignored salvage from the configuration file * Loads all trash salvage from the configuration file
*/ */
private void loadIgnoredSalvage() { private void loadTrashSalvage() {
this.ignoredSalvage.clear(); this.trashSalvage.clear();
List<String> allIgnoredSalvage = ConfigHelper.asStringList(this.settings.get(ScrapperSetting.IGNORED_SALVAGE)); List<String> allTrashSalvage = ConfigHelper.asStringList(this.settings.get(ScrapperSetting.IGNORED_SALVAGE));
if (allIgnoredSalvage == null) { if (allTrashSalvage == null) {
return; return;
} }
for (String ignoredSalvageInfo : allIgnoredSalvage) { for (String trashSalvageInfo : allTrashSalvage) {
// Ignore invalid lines // Ignore invalid lines
if (!ignoredSalvageInfo.contains(":")) { if (!trashSalvageInfo.contains(":")) {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, String.format("The ignored salvage " + BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, String.format("The trash salvage " +
"configuration line %s is invalid", ignoredSalvageInfo)); "configuration line %s is invalid", trashSalvageInfo));
continue; continue;
} }
// Parse all material names // Parse all material names
String[] data = ignoredSalvageInfo.split(":"); String[] data = trashSalvageInfo.split(":");
String[] materialStrings = data[0].split(";"); String[] materialStrings = data[0].split(";");
List<Material> materials = new ArrayList<>(); List<Material> materials = new ArrayList<>();
for (String materialString : materialStrings) { for (String materialString : materialStrings) {
materials.addAll(ItemHelper.getWildcardMatch(materialString, true)); materials.addAll(ItemHelper.getWildcardMatch(materialString, true));
} }
String[] ignoredSalvageStrings = data[1].split(";"); String[] trashSalvageStrings = data[1].split(";");
List<Material> ignored = new ArrayList<>(); List<Material> ignored = new ArrayList<>();
for (String ignoredSalvageString : ignoredSalvageStrings) { for (String trashSalvageString : trashSalvageStrings) {
ignored.addAll(ItemHelper.getWildcardMatch(ignoredSalvageString, true)); ignored.addAll(ItemHelper.getWildcardMatch(trashSalvageString, true));
} }
// Add the ignored salvage to all the matched materials // Add the trash salvage to all the matched materials
for (Material material : materials) { for (Material material : materials) {
ignoredSalvage.computeIfAbsent(material, k -> new HashSet<>()); trashSalvage.computeIfAbsent(material, k -> new HashSet<>());
ignoredSalvage.get(material).addAll(ignored); trashSalvage.get(material).addAll(ignored);
} }
} }
} }

View File

@ -194,13 +194,13 @@ public enum ScrapperSetting implements Setting {
/** /**
* Which items are ignored when calculating salvage for a given material * Which items are ignored when calculating salvage for a given material
*/ */
IGNORED_SALVAGE("ignoredSalvage", SettingValueType.STRING_LIST, IGNORED_SALVAGE("trashSalvage", SettingValueType.STRING_LIST,
new ArrayList<>(List.of("*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD:STICK")), new ArrayList<>(List.of("*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK")),
"Items ignored during salvage calculation. This follows the format: " + "Items treated as trash during salvage calculation. This follows the format: " +
"\"MATERIAL[,MATERIAL2][,MATERIAL3]:IGNORED\", so the material or materials listed will ignore " + "\"MATERIAL[;MATERIAL2][;MATERIAL3]:TRASH_MATERIAL[;TRASH_MATERIAL2]\", so the material or " +
"the material specified after the \":\" when calculating salvage (* matches any character). This " + "materials listed will treat the material specified after the \":\" as trash when calculating " +
"causes the player to lose some items during salvaging, but can prevent cases where a diamond " + "salvage unless all non-trash items can be given as well(* matches any character).",
"pickaxe is salvaged and only sticks are returned.", false, false), false, false),
; ;
private final String path; private final String path;

View File

@ -144,14 +144,14 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
@Nullable @Nullable
private List<ItemStack> getSalvage(@NotNull ItemStack item, boolean extended) { private List<ItemStack> getSalvage(@NotNull ItemStack item, boolean extended) {
// Get the salvage, for the item, but ignore some materials if set, and the item isn't at full durability // Get the salvage, for the item, but ignore some materials if set, and the item isn't at full durability
Set<Material> ignoredSalvage = BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getIgnoredSalvage( Set<Material> trashSalvage = BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getTrashSalvage(
item.getType()); item.getType());
// Don't ignore salvage for fully repaired items // Don't ignore salvage for fully repaired items
if (ignoredSalvage == null || ItemHelper.getDamage(item) == 0) { if (trashSalvage == null || ItemHelper.getDamage(item) == 0) {
ignoredSalvage = new HashSet<>(); trashSalvage = new HashSet<>();
} }
return SalvageHelper.getSalvage(BlacksmithPlugin.getInstance().getServer(), item, ignoredSalvage, extended); return SalvageHelper.getSalvage(BlacksmithPlugin.getInstance().getServer(), item, trashSalvage, extended);
} }
} }

View File

@ -61,14 +61,14 @@ public final class SalvageHelper {
* *
* <p>Note: Only items craft-able in a crafting table are salvageable. Netherite gear is therefore not salvageable.</p> * <p>Note: Only items craft-able in a crafting table are salvageable. Netherite gear is therefore not salvageable.</p>
* *
* @param server <p>The server to get recipes from</p> * @param server <p>The server to get recipes from</p>
* @param salvagedItem <p>The item stack to salvage</p> * @param salvagedItem <p>The item stack to salvage</p>
* @param ignoredSalvage <p>Any material which should not be returned as part of the salvage.</p> * @param trashSalvage <p>Any material treated as trash salvage</p>
* @param extended <p>Whether to enable extended salvage, ignoring the repairable restriction</p> * @param extended <p>Whether to enable extended salvage, ignoring the repairable restriction</p>
* @return <p>The items to return to the user, or null if not salvageable</p> * @return <p>The items to return to the user, or null if not salvageable</p>
*/ */
public static @Nullable List<ItemStack> getSalvage(@NotNull Server server, @Nullable ItemStack salvagedItem, public static @Nullable List<ItemStack> getSalvage(@NotNull Server server, @Nullable ItemStack salvagedItem,
@NotNull Collection<Material> ignoredSalvage, boolean extended) { @NotNull Collection<Material> trashSalvage, boolean extended) {
if (salvagedItem == null || salvagedItem.getAmount() < 1 || if (salvagedItem == null || salvagedItem.getAmount() < 1 ||
(!extended && !ItemHelper.isRepairable(salvagedItem))) { (!extended && !ItemHelper.isRepairable(salvagedItem))) {
return null; return null;
@ -76,7 +76,7 @@ public final class SalvageHelper {
for (Recipe recipe : server.getRecipesFor(new ItemStack(salvagedItem.getType(), salvagedItem.getAmount()))) { for (Recipe recipe : server.getRecipesFor(new ItemStack(salvagedItem.getType(), salvagedItem.getAmount()))) {
if (recipe instanceof ShapedRecipe || recipe instanceof ShapelessRecipe) { if (recipe instanceof ShapedRecipe || recipe instanceof ShapelessRecipe) {
List<ItemStack> salvage = getRecipeSalvage(recipe, salvagedItem, ignoredSalvage); List<ItemStack> salvage = getRecipeSalvage(recipe, salvagedItem, trashSalvage);
if (salvage != null && !salvage.isEmpty()) { if (salvage != null && !salvage.isEmpty()) {
return salvage; return salvage;
} }
@ -89,13 +89,13 @@ public final class SalvageHelper {
/** /**
* Gets the salvage resulting from the given recipe and the given item * Gets the salvage resulting from the given recipe and the given item
* *
* @param recipe <p>The recipe to get salvage for</p> * @param recipe <p>The recipe to get salvage for</p>
* @param salvagedItem <p>The item to be salvaged</p> * @param salvagedItem <p>The item to be salvaged</p>
* @param ignoredSalvage <p>Any material which should not be returned as part of the salvage.</p> * @param trashSalvage <p>Any material treated as trash salvage</p>
* @return <p>A list of items, or null if not a valid type of recipe</p> * @return <p>A list of items, or null if not a valid type of recipe</p>
*/ */
private static @Nullable List<ItemStack> getRecipeSalvage(@NotNull Recipe recipe, @NotNull ItemStack salvagedItem, private static @Nullable List<ItemStack> getRecipeSalvage(@NotNull Recipe recipe, @NotNull ItemStack salvagedItem,
@NotNull Collection<Material> ignoredSalvage) { @NotNull Collection<Material> trashSalvage) {
List<ItemStack> ingredients; List<ItemStack> ingredients;
if (recipe instanceof ShapedRecipe shapedRecipe) { if (recipe instanceof ShapedRecipe shapedRecipe) {
ingredients = getIngredients(shapedRecipe); ingredients = getIngredients(shapedRecipe);
@ -108,10 +108,7 @@ public final class SalvageHelper {
//Make things easier by eliminating identical stacks //Make things easier by eliminating identical stacks
ingredients = combineStacks(ingredients); ingredients = combineStacks(ingredients);
//Purge any ignored salvage to only calculate salvage using the remaining items return combineStacks(getSalvage(copyItems(ingredients), salvagedItem, trashSalvage));
ingredients.removeIf((item) -> ignoredSalvage.contains(item.getType()));
return combineStacks(getSalvage(copyItems(ingredients), salvagedItem));
} }
/** /**
@ -135,11 +132,13 @@ public final class SalvageHelper {
* *
* @param recipeItems <p>All items required for crafting the item to salvage</p> * @param recipeItems <p>All items required for crafting the item to salvage</p>
* @param salvagedItem <p>The item to be salvaged</p> * @param salvagedItem <p>The item to be salvaged</p>
* @param trashSalvage <p>The types of materials considered trash for this recipe</p>
* @return <p>The items to be returned to the user as salvage</p> * @return <p>The items to be returned to the user as salvage</p>
*/ */
@NotNull @NotNull
private static List<ItemStack> getSalvage(@NotNull List<ItemStack> recipeItems, private static List<ItemStack> getSalvage(@NotNull List<ItemStack> recipeItems,
@NotNull ItemStack salvagedItem) { @NotNull ItemStack salvagedItem,
@NotNull Collection<Material> trashSalvage) {
int durability = ItemHelper.getDurability(salvagedItem); int durability = ItemHelper.getDurability(salvagedItem);
int maxDurability = ItemHelper.getMaxDurability(salvagedItem); int maxDurability = ItemHelper.getMaxDurability(salvagedItem);
@ -156,16 +155,28 @@ public final class SalvageHelper {
int itemsToReturn = (int) Math.floor(percentageRemaining * totalItems); int itemsToReturn = (int) Math.floor(percentageRemaining * totalItems);
int bound = recipeItems.size(); int bound = recipeItems.size();
List<ItemStack> goodItems = copyItems(recipeItems);
goodItems.removeIf((item) -> trashSalvage.contains(item.getType()));
int goodSalvage = totalItemAmount(goodItems);
List<ItemStack> salvage = new ArrayList<>(); List<ItemStack> salvage = new ArrayList<>();
for (int i = 0; i < itemsToReturn; i++) { for (int i = 0; i < itemsToReturn; i++) {
// Pick random item
int itemIndex = SalvageHelper.random.nextInt(bound); int itemIndex = SalvageHelper.random.nextInt(bound);
ItemStack itemStack = recipeItems.get(itemIndex); ItemStack itemStack = recipeItems.get(itemIndex);
// The selected item is trash, so skip it
if (trashSalvage.contains(itemStack.getType()) && i < goodSalvage) {
i--;
continue;
}
//Make sure to never give more of one item than the amount which exists in the recipe //Make sure to never give more of one item than the amount which exists in the recipe
if (itemStack.getAmount() <= 0) { if (itemStack.getAmount() <= 0) {
i--; i--;
continue; continue;
} }
itemStack.setAmount(itemStack.getAmount() - 1); itemStack.setAmount(itemStack.getAmount() - 1);
salvage.add(new ItemStack(itemStack.getType(), 1)); salvage.add(new ItemStack(itemStack.getType(), 1));

View File

@ -123,13 +123,12 @@ scrapper:
# Whether enchanted salvaged items should return some amount of exp upon salvage # Whether enchanted salvaged items should return some amount of exp upon salvage
giveExperience: true giveExperience: true
# Items ignored during salvage calculation. This follows the format: # Items treated as trash during salvage calculation. This follows the format:
# "MATERIAL[;MATERIAL2][;MATERIAL3]:IGNORED_MATERIAL[;IGNORED_MATERIAL2]", # "MATERIAL[;MATERIAL2][;MATERIAL3]:TRASH_MATERIAL[;TRASH_MATERIAL2]", so the material or materials listed will
# so the material or materials listed will ignore the material specified after the ":" when calculating salvage # treat the material specified after the ":" as trash when calculating salvage unless all non-trash items can be
# (* matches any character). This causes the player to lose some items during salvaging, but can prevent cases # given as well(* matches any character).
# where a diamond pickaxe is salvaged and only sticks are returned. trashSalvage:
ignoredSalvage: - "*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK"
- "*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD:STICK"
# The cost of using the scrapper # The cost of using the scrapper
basePrice: 0 basePrice: 0