diff --git a/README.MD b/README.MD index 6fad0cd..72149b0 100644 --- a/README.MD +++ b/README.MD @@ -17,50 +17,50 @@ systems and divide them up into readable, manageable, customizable segments. ### Anvil Enchanting Anvil enchanting is primarily accessed via the -[`AnvilOperation`](src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilOperation.java). +[`VanillaAnvil`](src/main/java/com/github/jikoo/planarenchanting/anvil/VanillaAnvil.java). This is a class containing several simple ways to modify anvil behaviors. -For basic vanilla-style combination, all you need is an ordinary `AnvilOperation`. +For basic vanilla-style combination, all you need is an ordinary `VanillaAnvil`. ```java class MyAnvilHandler implements Listener { @EventHandler private void onPrepareAnvil(PrepareAnvilEvent event) { - AnvilView view = event.getView(); - AnvilOperation operation = new AnvilOperation(); - AnvilResult result = operation.apply(view); + VanillaAnvil anvil = new VanillaAnvil(); + AnvilResult result = anvil.getResult(event.getView()); event.setResult(result.item()); inventory.setRepairCostAmount(result.materialCost()); // Note: depending on how anvil actually functions, may need to update cost on a 0-tick delay. - // May also need to set relevant window property for client. inventory.setRepairCost(result.levelCost()); } } ``` -For specific use cases (i.e. removing or changing enchantment level cap) you can provide the -`AnvilOperation` with different functions which will be used by the default `AnvilFunction` -implementations. +For specific tweaks (i.e. removing or changing enchantment level cap) you can provide the +`VanillaAnvil` with a different `AnvilBehavior` implementation. Allowing conflicting enchantments to be added: + ```java -AnvilOperation operation = new AnvilOperation(); -operation.setEnchantsConflict((enchant1, enchant2) -> false); +VanillaAnvil anvil = new VanillaAnvil(new AnvilBehavior() { + @Override + public boolean getEnchantsConflict(Enchantment enchant1, Enchantment enchant2) { + return false; + } +}); ``` If you have even more specific needs but still want to leverage certain vanilla-style functionality, -you can write your own `AnvilOperation` and override `AnvilOperation#apply` to set your own -operation order. +you can write your own anvil functionality. -A custom `AnvilOperation` that only performs rename operations: +An implementation that only allows the input to be renamed: ```java -class RenameOperation extends AnvilOperation { - @Override - public void apply(@NotNull AnvilView view) { - AnvilOperationState state = new AnvilOperationState(this, view); +class RenameOnlyAnvil { + public void getResult(@NotNull AnvilView view) { + AnvilState state = new AnvilState(VanillaAnvil.BEHAVIOR, view); // Require first item to be set, second item to be unset. if (ItemUtil.isEmpty(state.getBase().getItem()) @@ -69,9 +69,9 @@ class RenameOperation extends AnvilOperation { } // Apply base cost. - state.apply(AnvilFunction.PRIOR_WORK_LEVEL_COST); + state.apply(AnvilFunctions.PRIOR_WORK_LEVEL_COST); // Apply rename. - state.apply(AnvilFunction.RENAME); + state.apply(AnvilFunctions.RENAME); return state.forge(); } diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilBehavior.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilBehavior.java new file mode 100644 index 0000000..5edc9f1 --- /dev/null +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilBehavior.java @@ -0,0 +1,54 @@ +package com.github.jikoo.planarenchanting.anvil; + +import com.github.jikoo.planarenchanting.util.MetaCachedStack; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface AnvilBehavior { + + /** + * Get whether an {@link Enchantment} is applicable for a wrapped {@link ItemStack}. + * + * @param enchantment the {@code Enchantment} to check for applicability + * @param base the item that may be enchanted + * @return whether the {@code Enchantment} can be applied + */ + boolean enchantApplies(@NotNull Enchantment enchantment, @NotNull MetaCachedStack base); + + /** + * Get whether two {@link Enchantment Enchantments} conflict. + * + * @return whether the {@code Enchantments} conflict + */ + boolean enchantsConflict(@NotNull Enchantment enchant1, @NotNull Enchantment enchant2); + + /** + * Get the maximum level for an {@link Enchantment}. + * + * @return the maximum level for an {@code Enchantment} + */ + int getEnchantMaxLevel(@NotNull Enchantment enchantment); + + /** + * Get whether an item should combine its {@link Enchantment Enchantments} with another item. + * + * @param base the base item + * @param addition the item added + * @return whether items should combine {@code Enchantments} + */ + boolean itemsCombineEnchants(@NotNull MetaCachedStack base, @NotNull MetaCachedStack addition); + + /** + * Get whether an item is repaired by another item. This is not the same as a repair via + * combination of like items! Like items always attempt to combine durability. If you require + * different behavior, override {@link VanillaAnvil#getResult(org.bukkit.inventory.view.AnvilView)} + * and do not call {@link AnvilFunctions#REPAIR_WITH_COMBINATION}. + * + * @param repaired the item repaired + * @param repairMat the item used to repair + * @return the method determining whether an item is repaired by another item + */ + public boolean itemRepairedBy(@NotNull MetaCachedStack repaired, @NotNull MetaCachedStack repairMat); + +} diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java index 93cf5b3..7a2c89a 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunction.java @@ -1,16 +1,6 @@ package com.github.jikoo.planarenchanting.anvil; -import com.github.jikoo.planarenchanting.enchant.EnchantData; -import com.github.jikoo.planarenchanting.util.ItemUtil; -import com.github.jikoo.planarenchanting.util.MetaCachedStack; -import java.util.Objects; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.meta.Damageable; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.Repairable; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * An interface representing a portion of the functionality of an anvil. By using several in @@ -18,275 +8,28 @@ */ public interface AnvilFunction { - /** Constant for adding the level cost from prior work. */ - AnvilFunction PRIOR_WORK_LEVEL_COST = new AnvilFunction() { - @Override - public boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperationState state) { - return true; - } - - @Override - public @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { - return new AnvilFunctionResult() { - @Override - public int getLevelCostIncrease() { - return ItemUtil.getRepairCost(state.getBase().getMeta()) - + ItemUtil.getRepairCost(state.getAddition().getMeta()); - } - }; - } - }; - - /** Constant for adding the level cost for a renaming operation. */ - AnvilFunction RENAME = new AnvilFunction() { - @Override - public boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperationState state) { - var itemMeta = state.getBase().getMeta(); - - // If names are not the same, can be applied. - return itemMeta != null - && !Objects.equals(itemMeta.getDisplayName(), state.getAnvil().getRenameText()); - } - - @Override - public @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { - return new AnvilFunctionResult() { - @Override - public int getLevelCostIncrease() { - // Renames always apply a level cost of 1. - return 1; - } - - @Override - public void modifyResult(@Nullable ItemMeta itemMeta) { - if (itemMeta == null) { - return; - } - - itemMeta.setDisplayName(state.getAnvil().getRenameText()); - if (itemMeta instanceof Repairable repairable) { - int repairCost = Math.max( - ItemUtil.getRepairCost(state.getBase().getMeta()), - ItemUtil.getRepairCost(state.getAddition().getMeta())); - repairable.setRepairCost(repairCost); - } - } - }; - } - }; - - /** Constant for updating prior work to new value. */ - AnvilFunction UPDATE_PRIOR_WORK_COST = new AnvilFunction() { - @Override - public boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperationState state) { - return state.getBase().getMeta() instanceof Repairable; - } - - @Override - public @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { - - return new AnvilFunctionResult() { - @Override - public int getLevelCostIncrease() { - return 0; - } - - @Override - public void modifyResult(@Nullable ItemMeta itemMeta) { - if (itemMeta instanceof Repairable repairable) { - int priorRepairCost = Math.max( - ItemUtil.getRepairCost(state.getBase().getMeta()), - ItemUtil.getRepairCost(state.getAddition().getMeta())); - repairable.setRepairCost(priorRepairCost * 2 + 1); - } - } - }; - } - }; - - /** Constant for using materials to restore durability to the base item. */ - AnvilFunction REPAIR_WITH_MATERIAL = new AnvilFunction() { - @Override - public boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperationState state) { - MetaCachedStack base = state.getBase(); - return operation.itemRepairedBy(base.getItem(), state.getAddition().getItem()) - && base.getItem().getType().getMaxDurability() > 0 - && base.getMeta() instanceof Damageable damageable - && damageable.getDamage() > 0; - } - - @Override - public @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { - if (!(state.getBase().getMeta() instanceof Damageable damageable)) { - // If result is not damageable, it cannot be repaired. - return AnvilFunctionResult.EMPTY; - } - - int missingDurability = damageable.getDamage(); - - if (missingDurability < 1) { - // If result is not damaged, no repair. - return AnvilFunctionResult.EMPTY; - } - - int repairs = 0; - // Each repair removes up to 1/4 max durability in damage. - int damageRepairedPerMaterial = state.getBase().getItem().getType().getMaxDurability() / 4; - - while (missingDurability > 0 && repairs < state.getAddition().getItem().getAmount()) { - missingDurability -= damageRepairedPerMaterial; - ++repairs; - } - - // Finalize for later use. - int totalRepairs = repairs; - int resultDamage = Math.max(0, missingDurability); - - return new AnvilFunctionResult() { - @Override - public int getLevelCostIncrease() { - return totalRepairs; - } - - @Override - public int getMaterialCostIncrease() { - return totalRepairs; - } - - @Override - public void modifyResult(@Nullable ItemMeta itemMeta) { - if (itemMeta instanceof Damageable damageable) { - damageable.setDamage(resultDamage); - } - } - }; - } - }; - - /** Constant for using identical materials to restore durability. */ - AnvilFunction REPAIR_WITH_COMBINATION = new AnvilFunction() { - @Override - public boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperationState state) { - Material baseType = state.getBase().getItem().getType(); - return baseType == state.getAddition().getItem().getType() - && baseType.getMaxDurability() > 0 - && state.getBase().getMeta() instanceof Damageable damageable - && damageable.getDamage() > 0; - } - - @Override - public @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { - if (!(state.getBase().getMeta() instanceof Damageable baseDamageable - && state.getAddition().getMeta() instanceof Damageable additionDamageable)) { - return AnvilFunctionResult.EMPTY; - } - - int missingDurability = baseDamageable.getDamage(); - - if (missingDurability < 1) { - return AnvilFunctionResult.EMPTY; - } - - int maxDurability = state.getBase().getItem().getType().getMaxDurability(); - // Restore durability remaining in added item. - int restoredDurability = maxDurability - additionDamageable.getDamage(); - // Add a bonus 12% total tool durability to the repair. - restoredDurability += maxDurability * .12; - - // Finalize for later use. - int resultDamage = Math.max(0, missingDurability - restoredDurability); - - return new AnvilFunctionResult() { - @Override - public int getLevelCostIncrease() { - return 2; - } - - @Override - public void modifyResult(@Nullable ItemMeta itemMeta) { - if (itemMeta instanceof Damageable damageable) { - damageable.setDamage(resultDamage); - } - } - }; - } - }; - - /** Constant for combining enchantments from source and target like Java Edition. */ - AnvilFunction COMBINE_ENCHANTMENTS_JAVA_EDITION = new CombineEnchantments() { - @Override - protected int getTotalCost(int baseCost, int oldLevel, int newLevel) { - return baseCost * newLevel; - } - - @Override - protected int getNonApplicableCost() { - return 1; - } - }; - - /** Constant for combining enchantments from source and target like Bedrock Edition. */ - AnvilFunction COMBINE_ENCHANTMENTS_BEDROCK_EDITION = new CombineEnchantments() { - @Override - protected int getAnvilCost(Enchantment enchantment, boolean isFromBook) { - EnchantData enchantData = EnchantData.of(enchantment); - if (!enchantData.getSecondaryItems().isTagged(Material.TRIDENT)) { - return super.getAnvilCost(enchantment, isFromBook); - } - - int base = enchantData.getAnvilCost(); - - // Bedrock Edition rarity is 1 tier lower for trident enchantments. - return Math.max(1, isFromBook ? base / 4 : base / 2); - } - - @Override - protected int getTotalCost(int baseCost, int oldLevel, int newLevel) { - if (oldLevel >= newLevel) { - return 0; - } - - return baseCost * (newLevel - oldLevel); - } - - @Override - protected int getNonApplicableCost() { - return 0; - } - }; - /** * Check if the function is capable of generating a usable result. Note that this may be a quick * cursory check - the function may yield an empty result even if it initially reported itself * applicable. The only guarantee this method makes is that retrieving and using a result will not * cause an error if its return value is respected. * - * @param operation the {@link AnvilOperation} being performed - * @param state the {@link AnvilOperationState} the state of the {@code AnvilOperation} in use + * @param behavior the definition of behaviors for the anvil + * @param state the {@link AnvilState} the state of the {@code AnvilOperation} in use * @return whether the {@link AnvilFunction} can generate an {@link AnvilFunctionResult} */ - boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperationState state); + boolean canApply(@NotNull AnvilBehavior behavior, @NotNull AnvilState state); /** * Get an {@link AnvilFunctionResult} used to apply the changes from the function based on the * provided anvil operation state and settings. * - * @param operation the {@link AnvilOperation} being performed - * @param state the {@link AnvilOperationState} the state of the anvil in use + * @param behavior the definition of behaviors for the anvil + * @param state the {@link AnvilState} the state of the anvil in use * @return the resulting applicable changes */ @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state); + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state); } diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctions.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctions.java new file mode 100644 index 0000000..0ccf9a2 --- /dev/null +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctions.java @@ -0,0 +1,265 @@ +package com.github.jikoo.planarenchanting.anvil; + +import com.github.jikoo.planarenchanting.enchant.EnchantData; +import com.github.jikoo.planarenchanting.util.ItemUtil; +import com.github.jikoo.planarenchanting.util.MetaCachedStack; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Repairable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Objects; + +public enum AnvilFunctions { + ; // Empty enum to hold constants. + + /** Constant for adding the level cost from prior work. */ + public static AnvilFunction PRIOR_WORK_LEVEL_COST = new AnvilFunction() { + @Override + public boolean canApply(@NotNull AnvilBehavior behavior, @NotNull AnvilState state) { + return true; + } + + @Override + public @NotNull AnvilFunctionResult getResult( + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { + return new AnvilFunctionResult() { + @Override + public int getLevelCostIncrease() { + return ItemUtil.getRepairCost(state.getBase().getMeta()) + + ItemUtil.getRepairCost(state.getAddition().getMeta()); + } + }; + } + }; + + /** Constant for adding the level cost for a renaming operation. */ + public static AnvilFunction RENAME = new AnvilFunction() { + @Override + public boolean canApply(@NotNull AnvilBehavior behavior, @NotNull AnvilState state) { + var itemMeta = state.getBase().getMeta(); + + // If names are not the same, can be applied. + return itemMeta != null + && !Objects.equals(itemMeta.getDisplayName(), state.getAnvil().getRenameText()); + } + + @Override + public @NotNull AnvilFunctionResult getResult( + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { + return new AnvilFunctionResult() { + @Override + public int getLevelCostIncrease() { + // Renames always apply a level cost of 1. + return 1; + } + + @Override + public void modifyResult(@Nullable ItemMeta itemMeta) { + if (itemMeta == null) { + return; + } + + itemMeta.setDisplayName(state.getAnvil().getRenameText()); + if (itemMeta instanceof Repairable repairable) { + int repairCost = Math.max( + ItemUtil.getRepairCost(state.getBase().getMeta()), + ItemUtil.getRepairCost(state.getAddition().getMeta())); + repairable.setRepairCost(repairCost); + } + } + }; + } + }; + + /** Constant for updating prior work to new value. */ + public static AnvilFunction UPDATE_PRIOR_WORK_COST = new AnvilFunction() { + @Override + public boolean canApply(@NotNull AnvilBehavior behavior, @NotNull AnvilState state) { + return state.getBase().getMeta() instanceof Repairable; + } + + @Override + public @NotNull AnvilFunctionResult getResult( + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { + + return new AnvilFunctionResult() { + @Override + public int getLevelCostIncrease() { + return 0; + } + + @Override + public void modifyResult(@Nullable ItemMeta itemMeta) { + if (itemMeta instanceof Repairable repairable) { + int priorRepairCost = Math.max( + ItemUtil.getRepairCost(state.getBase().getMeta()), + ItemUtil.getRepairCost(state.getAddition().getMeta())); + repairable.setRepairCost(priorRepairCost * 2 + 1); + } + } + }; + } + }; + + /** Constant for using materials to restore durability to the base item. */ + public static AnvilFunction REPAIR_WITH_MATERIAL = new AnvilFunction() { + @Override + public boolean canApply(@NotNull AnvilBehavior behavior, @NotNull AnvilState state) { + MetaCachedStack base = state.getBase(); + return behavior.itemRepairedBy(base, state.getAddition()) + && base.getItem().getType().getMaxDurability() > 0 + && base.getMeta() instanceof Damageable damageable + && damageable.getDamage() > 0; + } + + @Override + public @NotNull AnvilFunctionResult getResult( + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { + if (!(state.getBase().getMeta() instanceof Damageable damageable)) { + // If result is not damageable, it cannot be repaired. + return AnvilFunctionResult.EMPTY; + } + + int missingDurability = damageable.getDamage(); + + if (missingDurability < 1) { + // If result is not damaged, no repair. + return AnvilFunctionResult.EMPTY; + } + + int repairs = 0; + // Each repair removes up to 1/4 max durability in damage. + int damageRepairedPerMaterial = state.getBase().getItem().getType().getMaxDurability() / 4; + + while (missingDurability > 0 && repairs < state.getAddition().getItem().getAmount()) { + missingDurability -= damageRepairedPerMaterial; + ++repairs; + } + + // Finalize for later use. + int totalRepairs = repairs; + int resultDamage = Math.max(0, missingDurability); + + return new AnvilFunctionResult() { + @Override + public int getLevelCostIncrease() { + return totalRepairs; + } + + @Override + public int getMaterialCostIncrease() { + return totalRepairs; + } + + @Override + public void modifyResult(@Nullable ItemMeta itemMeta) { + if (itemMeta instanceof Damageable damageable) { + damageable.setDamage(resultDamage); + } + } + }; + } + }; + + /** Constant for using identical materials to restore durability. */ + public static AnvilFunction REPAIR_WITH_COMBINATION = new AnvilFunction() { + @Override + public boolean canApply(@NotNull AnvilBehavior behavior, @NotNull AnvilState state) { + Material baseType = state.getBase().getItem().getType(); + return baseType == state.getAddition().getItem().getType() + && baseType.getMaxDurability() > 0 + && state.getBase().getMeta() instanceof Damageable damageable + && damageable.getDamage() > 0; + } + + @Override + public @NotNull AnvilFunctionResult getResult( + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { + if (!(state.getBase().getMeta() instanceof Damageable baseDamageable + && state.getAddition().getMeta() instanceof Damageable additionDamageable)) { + return AnvilFunctionResult.EMPTY; + } + + int missingDurability = baseDamageable.getDamage(); + + if (missingDurability < 1) { + return AnvilFunctionResult.EMPTY; + } + + int maxDurability = state.getBase().getItem().getType().getMaxDurability(); + // Restore durability remaining in added item. + int restoredDurability = maxDurability - additionDamageable.getDamage(); + // Add a bonus 12% total tool durability to the repair. + restoredDurability += maxDurability * .12; + + // Finalize for later use. + int resultDamage = Math.max(0, missingDurability - restoredDurability); + + return new AnvilFunctionResult() { + @Override + public int getLevelCostIncrease() { + return 2; + } + + @Override + public void modifyResult(@Nullable ItemMeta itemMeta) { + if (itemMeta instanceof Damageable damageable) { + damageable.setDamage(resultDamage); + } + } + }; + } + }; + + /** Constant for combining enchantments from source and target like Java Edition. */ + public static AnvilFunction COMBINE_ENCHANTMENTS_JAVA_EDITION = new CombineEnchantments() { + @Override + protected int getTotalCost(int baseCost, int oldLevel, int newLevel) { + return baseCost * newLevel; + } + + @Override + protected int getNonApplicableCost() { + return 1; + } + }; + + /** Constant for combining enchantments from source and target like Bedrock Edition. */ + public static AnvilFunction COMBINE_ENCHANTMENTS_BEDROCK_EDITION = new CombineEnchantments() { + @Override + protected int getAnvilCost(Enchantment enchantment, boolean isFromBook) { + EnchantData enchantData = EnchantData.of(enchantment); + if (!enchantData.getSecondaryItems().isTagged(Material.TRIDENT)) { + return super.getAnvilCost(enchantment, isFromBook); + } + + int base = enchantData.getAnvilCost(); + + // Bedrock Edition rarity is 1 tier lower for trident enchantments. + return Math.max(1, isFromBook ? base / 4 : base / 2); + } + + @Override + protected int getTotalCost(int baseCost, int oldLevel, int newLevel) { + if (oldLevel >= newLevel) { + return 0; + } + + return baseCost * (newLevel - oldLevel); + } + + @Override + protected int getNonApplicableCost() { + return 0; + } + }; + +} diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilOperation.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilOperation.java deleted file mode 100644 index 294dd94..0000000 --- a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilOperation.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.github.jikoo.planarenchanting.anvil; - -import com.github.jikoo.planarenchanting.util.ItemUtil; -import java.util.function.BiPredicate; -import java.util.function.ToIntFunction; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.view.AnvilView; -import org.jetbrains.annotations.NotNull; - -/** - * A container for data required to calculate an anvil combination. - */ -public class AnvilOperation { - - private @NotNull BiPredicate<@NotNull Enchantment, @NotNull ItemStack> enchantApplies; - private @NotNull BiPredicate<@NotNull Enchantment, @NotNull Enchantment> enchantsConflict; - private @NotNull ToIntFunction<@NotNull Enchantment> enchantMaxLevel; - private @NotNull BiPredicate<@NotNull ItemStack, @NotNull ItemStack> itemRepairedBy; - private @NotNull BiPredicate<@NotNull ItemStack, @NotNull ItemStack> itemsCombineEnchants; - - /** - * Construct a new {@code AnvilOperation}. - */ - public AnvilOperation() { - this.enchantApplies = Enchantment::canEnchantItem; - this.enchantsConflict = Enchantment::conflictsWith; - this.enchantMaxLevel = Enchantment::getMaxLevel; - this.itemRepairedBy = RepairMaterial::repairs; - this.itemsCombineEnchants = (base, addition) -> - base.getType() == addition.getType() - || addition.getType() == Material.ENCHANTED_BOOK; - } - - /** - * Get whether an {@link Enchantment} is applicable for an {@link ItemStack}. - * - * @param enchantment the {@code Enchantment} to check for applicability - * @param itemStack the item that may be enchanted - * @return whether the {@code Enchantment} can be applied - */ - public boolean enchantApplies(@NotNull Enchantment enchantment, @NotNull ItemStack itemStack) { - return this.enchantApplies.test(enchantment, itemStack); - } - - /** - * Set the method for determining if an {@link Enchantment} is applicable for an - * {@link ItemStack}. - * - * @param enchantApplies the method for determining if an {@code Enchantment} is applicable - */ - public void setEnchantApplies( - @NotNull BiPredicate<@NotNull Enchantment, @NotNull ItemStack> enchantApplies) { - this.enchantApplies = enchantApplies; - } - - /** - * Get the method for determining if {@link Enchantment Enchantments} conflict. - * - * @return whether the {@code Enchantments} conflict - */ - public boolean enchantsConflict(@NotNull Enchantment enchant1, @NotNull Enchantment enchant2) { - return this.enchantsConflict.test(enchant1, enchant2); - } - - /** - * Set the method for determining if {@link Enchantment Enchantments} conflict. - * - * @param enchantsConflict the method for determining if {@code Enchantments} conflict - */ - public void setEnchantsConflict( - @NotNull BiPredicate<@NotNull Enchantment, @NotNull Enchantment> enchantsConflict) { - this.enchantsConflict = enchantsConflict; - } - - /** - * Get the method supplying maximum level for an {@link Enchantment}. - * - * @return the method supplying maximum level for an {@code Enchantment} - */ - public int getEnchantMaxLevel(@NotNull Enchantment enchantment) { - return this.enchantMaxLevel.applyAsInt(enchantment); - } - - /** - * Set the method supplying maximum level for an {@link Enchantment}. - * - * @param enchantMaxLevel the method supplying maximum level for an {@code Enchantment} - */ - public void setEnchantMaxLevel(@NotNull ToIntFunction<@NotNull Enchantment> enchantMaxLevel) { - this.enchantMaxLevel = enchantMaxLevel; - } - - /** - * Get whether an item should combine its {@link Enchantment Enchantments} with another item. - * - * @param base the base item - * @param addition the item added - * @return whether items should combine {@code Enchantments} - */ - public boolean itemsCombineEnchants(@NotNull ItemStack base, @NotNull ItemStack addition) { - return this.itemsCombineEnchants.test(base, addition); - } - - /** - * Set the method determining whether an item should combine its {@link Enchantment Enchantments} - * with another item. - * - * @param itemsCombineEnchants the method determining whether an item should combine its - * {@code Enchantments} - */ - public void setItemsCombineEnchants( - @NotNull BiPredicate<@NotNull ItemStack, @NotNull ItemStack> itemsCombineEnchants) { - this.itemsCombineEnchants = itemsCombineEnchants; - } - - /** - * Get whether an item is repaired by another item. - * - * @see #setItemRepairedBy - * @param repaired the item repaired - * @param repairMat the item used to repair - * @return the method determining whether an item is repaired by another item - */ - public boolean itemRepairedBy(@NotNull ItemStack repaired, @NotNull ItemStack repairMat) { - return this.itemRepairedBy.test(repaired, repairMat); - } - - /** - * Set the method determining whether an item is repaired by another item. This is not the same as - * a repair via combination of like items! Like items always attempt to combine durability. If - * you require different behavior, override {@link #apply(AnvilView)} and do not call - * {@link AnvilFunction#REPAIR_WITH_COMBINATION}. - * - *

N.B. Only {@link org.bukkit.inventory.meta.Damageable Damageable} items can be repaired. - * A material repair restores 25% of the durability of an item per material consumed. - * - * @param itemRepairedBy the method determining whether an item is repaired by another item - */ - public void setItemRepairedBy( - @NotNull BiPredicate<@NotNull ItemStack, @NotNull ItemStack> itemRepairedBy) { - this.itemRepairedBy = itemRepairedBy; - } - - /** - * Get an {@link AnvilResult} for this anvil operation. - * - * @return the {@code AnvilResult} - */ - public @NotNull AnvilResult apply(@NotNull AnvilView view) { - AnvilOperationState state = new AnvilOperationState(this, view); - - if (ItemUtil.isEmpty(state.getBase().getItem())) { - return AnvilResult.EMPTY; - } - - // Apply base cost. - state.apply(AnvilFunction.PRIOR_WORK_LEVEL_COST); - - if (ItemUtil.isEmpty(state.getAddition().getItem())) { - if (state.apply(AnvilFunction.RENAME)) { - // If the only thing occurring is a renaming operation, it is always allowed. - state.setLevelCost(Math.min(state.getLevelCost(), state.getAnvil().getMaximumRepairCost() - 1)); - } - - // No addition means no additional operations to perform. - return state.forge(); - } - - - if (state.getBase().getItem().getAmount() != 1) { - // Multi-renames are allowed, multi-modifications are not. - // Vanilla allows multi-modifications "for creative" but the way it does it is problematic. - return AnvilResult.EMPTY; - } - - state.apply(AnvilFunction.RENAME); - // Apply prior work cost after rename. - // Rename also applies a prior work cost but does not increase it. - state.apply(AnvilFunction.UPDATE_PRIOR_WORK_COST); - - if (!state.apply(AnvilFunction.REPAIR_WITH_MATERIAL)) { - // Only do combination repair if this is not a material repair. - state.apply(AnvilFunction.REPAIR_WITH_COMBINATION); - } - - // Differing from vanilla - since we use a custom determination for whether enchantments should - // transfer (which defaults to indirectly mimicking vanilla), enchantments may need to be - // applied from a material repair. - state.apply(AnvilFunction.COMBINE_ENCHANTMENTS_JAVA_EDITION); - - return state.forge(); - } - -} diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationState.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilState.java similarity index 90% rename from src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationState.java rename to src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilState.java index 5d1da22..862c70e 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationState.java +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/AnvilState.java @@ -11,9 +11,9 @@ /** * A mutable data holder similar to an {@link AnvilResult}. */ -public class AnvilOperationState { +public class AnvilState { - private final @NotNull AnvilOperation operation; + private final @NotNull AnvilBehavior behavior; private final @NotNull AnvilView view; private final @NotNull MetaCachedStack base; private final @NotNull MetaCachedStack addition; @@ -24,11 +24,11 @@ public class AnvilOperationState { /** * Create an {@code AnvilOperationState} instance for the given operation and inventory. * - * @param operation the {@link AnvilOperation} mutating the state + * @param behavior the {@link AnvilBehavior} mutating the state * @param view the {@link AnvilView} the state is derived from */ - public AnvilOperationState(@NotNull AnvilOperation operation, @NotNull AnvilView view) { - this.operation = operation; + public AnvilState(@NotNull AnvilBehavior behavior, @NotNull AnvilView view) { + this.behavior = behavior; this.view = view; this.base = new MetaCachedStack(this.view.getItem(0)); this.addition = new MetaCachedStack(this.view.getItem(1)); @@ -103,16 +103,16 @@ public void setMaterialCost(int materialCost) { * Note that a function reporting itself applicable does not guarantee that the result or costs * will actually differ. * - * @see AnvilFunction#canApply(AnvilOperation, AnvilOperationState) + * @see AnvilFunction#canApply(AnvilBehavior, AnvilState) * @param function the {@code AnvilFunction} to apply * @return whether the {@link AnvilFunction} could apply */ public boolean apply(@NotNull AnvilFunction function) { - if (!function.canApply(this.operation, this)) { + if (!function.canApply(this.behavior, this)) { return false; } - AnvilFunctionResult anvilResult = function.getResult(this.operation, this); + AnvilFunctionResult anvilResult = function.getResult(this.behavior, this); anvilResult.modifyResult(this.result.getMeta()); this.levelCost += anvilResult.getLevelCostIncrease(); diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java index 3d74443..7c8b98a 100644 --- a/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantments.java @@ -14,14 +14,14 @@ abstract class CombineEnchantments implements AnvilFunction { @Override - public boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperationState state) { - return operation.itemsCombineEnchants(state.getBase().getItem(), state.getAddition().getItem()); + public boolean canApply(@NotNull AnvilBehavior behavior, @NotNull AnvilState state) { + return behavior.itemsCombineEnchants(state.getBase(), state.getAddition()); } @Override public final @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { Map baseEnchants = EnchantmentUtil.getEnchants( state.getBase().getMeta()); Map additionEnchants = EnchantmentUtil.getEnchants( @@ -39,15 +39,15 @@ public boolean canApply(@NotNull AnvilOperation operation, @NotNull AnvilOperati Enchantment newEnchantment = enchantEntry.getKey(); int oldLevel = baseEnchants.getOrDefault(newEnchantment, 0); int baseCost = getAnvilCost(newEnchantment, isFromBook); - if (operation.enchantApplies(newEnchantment, state.getBase().getItem()) + if (behavior.enchantApplies(newEnchantment, state.getBase()) && baseEnchants.keySet().stream() .noneMatch(existingEnchant -> !existingEnchant.getKey().equals(newEnchantment.getKey()) - && operation.enchantsConflict(existingEnchant, newEnchantment))) { + && behavior.enchantsConflict(existingEnchant, newEnchantment))) { int addedLevel = enchantEntry.getValue(); int newLevel = oldLevel == addedLevel ? addedLevel + 1 : Math.max(oldLevel, addedLevel); - newLevel = Math.min(newLevel, operation.getEnchantMaxLevel(newEnchantment)); + newLevel = Math.min(newLevel, behavior.getEnchantMaxLevel(newEnchantment)); newEnchants.put(newEnchantment, newLevel); levelCost += getTotalCost(baseCost, oldLevel, newLevel); diff --git a/src/main/java/com/github/jikoo/planarenchanting/anvil/VanillaAnvil.java b/src/main/java/com/github/jikoo/planarenchanting/anvil/VanillaAnvil.java new file mode 100644 index 0000000..c3da525 --- /dev/null +++ b/src/main/java/com/github/jikoo/planarenchanting/anvil/VanillaAnvil.java @@ -0,0 +1,100 @@ +package com.github.jikoo.planarenchanting.anvil; + +import com.github.jikoo.planarenchanting.util.ItemUtil; +import com.github.jikoo.planarenchanting.util.MetaCachedStack; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.view.AnvilView; +import org.jetbrains.annotations.NotNull; + +public class VanillaAnvil { + + public static final AnvilBehavior BEHAVIOR = new AnvilBehavior() { + @Override + public boolean enchantApplies(@NotNull Enchantment enchantment, @NotNull MetaCachedStack base) { + return enchantment.canEnchantItem(base.getItem()); + } + + @Override + public boolean enchantsConflict(@NotNull Enchantment enchant1, @NotNull Enchantment enchant2) { + return enchant1.conflictsWith(enchant2); + } + + @Override + public int getEnchantMaxLevel(@NotNull Enchantment enchantment) { + return enchantment.getMaxLevel(); + } + + @Override + public boolean itemsCombineEnchants(@NotNull MetaCachedStack base, @NotNull MetaCachedStack addition) { + Material additionType = addition.getItem().getType(); + return base.getItem().getType() == additionType || additionType == Material.ENCHANTED_BOOK; + } + + @Override + public boolean itemRepairedBy(@NotNull MetaCachedStack repaired, @NotNull MetaCachedStack repairMat) { + return RepairMaterial.repairs(repaired.getItem(), repairMat.getItem()); + } + }; + + private final @NotNull AnvilBehavior behavior; + + public VanillaAnvil() { + this(BEHAVIOR); + } + + public VanillaAnvil(@NotNull AnvilBehavior behavior) { + this.behavior = behavior; + } + + /** + * Get an {@link AnvilResult} for this anvil operation. + * + * @return the {@code AnvilResult} + */ + public @NotNull AnvilResult getResult(@NotNull AnvilView view) { + AnvilState state = new AnvilState(behavior, view); + + if (ItemUtil.isEmpty(state.getBase().getItem())) { + return AnvilResult.EMPTY; + } + + // Apply base cost. + state.apply(AnvilFunctions.PRIOR_WORK_LEVEL_COST); + + if (ItemUtil.isEmpty(state.getAddition().getItem())) { + if (state.apply(AnvilFunctions.RENAME)) { + // If the only thing occurring is a renaming operation, it is always allowed. + state.setLevelCost(Math.min(state.getLevelCost(), state.getAnvil().getMaximumRepairCost() - 1)); + } + + // No addition means no additional operations to perform. + return state.forge(); + } + + + if (state.getBase().getItem().getAmount() != 1) { + // Multi-renames are allowed, multi-modifications are not. + // Vanilla allows multi-modifications "for creative" but the way it does it is problematic. + return AnvilResult.EMPTY; + } + + state.apply(AnvilFunctions.RENAME); + // Apply prior work cost after rename. + // Rename also applies a prior work cost but does not increase it. + state.apply(AnvilFunctions.UPDATE_PRIOR_WORK_COST); + + if (!state.apply(AnvilFunctions.REPAIR_WITH_MATERIAL)) { + // Only do combination repair if this is not a material repair. + state.apply(AnvilFunctions.REPAIR_WITH_COMBINATION); + } + + // Differing from vanilla - since we use a custom determination for whether enchantments should + // transfer (which defaults to indirectly mimicking vanilla), enchantments may need to be + // applied from a material repair. + state.apply(AnvilFunctions.COMBINE_ENCHANTMENTS_JAVA_EDITION); + + return state.forge(); + } + +} diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java index 4891100..40bcf4c 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilFunctionTest.java @@ -12,9 +12,11 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import com.github.jikoo.planarenchanting.anvil.mock.ReadableResultState; @@ -79,15 +81,15 @@ void beforeAll() { @Nested class PriorWorkLevelCost { - private final AnvilFunction function = AnvilFunction.PRIOR_WORK_LEVEL_COST; + private final AnvilFunction function = AnvilFunctions.PRIOR_WORK_LEVEL_COST; @Test void testPriorWorkLevelCostApplies() { var anvil = getMockView(new ItemStack(BASE_MAT), new ItemStack(BASE_MAT)); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, anvil); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, anvil); - assertThat("Prior work level cost always applies", function.canApply(operation, state)); + assertThat("Prior work level cost always applies", function.canApply(behavior, state)); } @ParameterizedTest @@ -97,12 +99,12 @@ void testPriorWorkLevelCostValues(int baseWork, int addedWork) { var anvil = getMockView( prepareItem(baseItem, 0, baseWork), prepareItem(new ItemStack(BASE_MAT), 0, addedWork)); - var operation = new AnvilOperation(); - var state = new ReadableResultState(operation, anvil); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new ReadableResultState(behavior, anvil); - assertThat("Prior work level cost always applies", function.canApply(operation, state)); + assertThat("Prior work level cost always applies", function.canApply(behavior, state)); - var result = function.getResult(operation, state); + var result = function.getResult(behavior, state); assertThat( "Cost must be total prior work", result.getLevelCostIncrease(), @@ -135,18 +137,18 @@ void testPriorWorkLevelCostValues(int baseWork, int addedWork) { @TestInstance(Lifecycle.PER_CLASS) // Should be inherited but isn't for whatever reason class Rename { - private final AnvilFunction function = AnvilFunction.RENAME; + private final AnvilFunction function = AnvilFunctions.RENAME; @DisplayName("Rename requires ItemMeta") @Test void testRenameRequiresMeta() { var base = getNullMetaItem(); var inventory = getMockView(base, null); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat("Base meta is null", state.getBase().getMeta(), is(nullValue())); - assertThat("Rename requires meta", function.canApply(operation, state), is(false)); + assertThat("Rename requires meta", function.canApply(behavior, state), is(false)); } @DisplayName("Rename requires different name") @@ -157,14 +159,14 @@ void testRenameRequiresDifferentName( boolean canApply) { var base = new ItemStack(BASE_MAT); var inventory = getMockView(base, null); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat("Base meta is not null", state.getBase().getMeta(), is(notNullValue())); setup.accept(state.getBase().getMeta(), inventory); - assertThat("Rename requires different name", function.canApply(operation, state), is(canApply)); + assertThat("Rename requires different name", function.canApply(behavior, state), is(canApply)); } @DisplayName("Rename applies name and cost") @@ -179,10 +181,10 @@ void testRenameApplication(BiConsumer setup) { base.setItemMeta(baseMeta); inventory.setItem(0, base); - var operation = new AnvilOperation(); - var state = new ReadableResultState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new ReadableResultState(behavior, inventory); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Level cost increase is 1", result.getLevelCostIncrease(), is(1)); result.modifyResult(state.getResult().getMeta()); @@ -236,18 +238,18 @@ private Stream renameSituations() { @Nested class UpdatePriorWorkCost { - private final AnvilFunction function = AnvilFunction.UPDATE_PRIOR_WORK_COST; + private final AnvilFunction function = AnvilFunctions.UPDATE_PRIOR_WORK_COST; @Test void testBaseNotRepairable() { var base = getNullMetaItem(); var inventory = getMockView(base, null); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat( "Base must be repairable", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); } @@ -256,12 +258,12 @@ void testBaseRepairable() { var base = new ItemStack(BASE_MAT); var addition = getNullMetaItem(); var inventory = getMockView(base, addition); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat( "Repairable base is acceptable", - function.canApply(operation, state), + function.canApply(behavior, state), is(true)); } @@ -272,12 +274,12 @@ void testPriorWorkUpdate(int baseWork, int addedWork) { var anvil = getMockView( prepareItem(baseItem, 0, baseWork), prepareItem(new ItemStack(BASE_MAT), 0, addedWork)); - var operation = new AnvilOperation(); - var state = new ReadableResultState(operation, anvil); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new ReadableResultState(behavior, anvil); - assertThat("Prior work level cost always applies", function.canApply(operation, state)); + assertThat("Prior work level cost always applies", function.canApply(behavior, state)); - var result = function.getResult(operation, state); + var result = function.getResult(behavior, state); assertThat("Cost must be unchanged", result.getLevelCostIncrease(), is(0)); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); @@ -302,10 +304,10 @@ void testPriorWorkUpdate(int baseWork, int addedWork) { void testMetaNotRepairable() { var base = getNullMetaItem(); var inventory = getMockView(base, null); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); - var result = function.getResult(operation, state); + var result = function.getResult(behavior, state); assertDoesNotThrow(() -> result.modifyResult(base.getItemMeta())); } @@ -314,31 +316,31 @@ void testMetaNotRepairable() { @Nested class RepairWithMaterial { - private final AnvilFunction function = AnvilFunction.REPAIR_WITH_MATERIAL; + private final AnvilFunction function = AnvilFunctions.REPAIR_WITH_MATERIAL; @Test void testCanApplyNotRepairedBy() { var inventory = getMockView(null, null); - var operation = new AnvilOperation(); - operation.setItemRepairedBy((item1, item2) -> false); - var state = new AnvilOperationState(operation, inventory); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(false).when(behavior).itemRepairedBy(notNull(), notNull()); + var state = new AnvilState(behavior, inventory); assertThat( "Must be repairable by item", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); } @Test void testCanApplyNotDurable() { var inventory = getMockView(null, null); - var operation = new AnvilOperation(); - operation.setItemRepairedBy((item1, item2) -> true); - var state = new AnvilOperationState(operation, inventory); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(true).when(behavior).itemRepairedBy(notNull(), notNull()); + var state = new AnvilState(behavior, inventory); assertThat( "Must have durability", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); } @@ -347,34 +349,34 @@ void testCanApplyNotDamageable() { var base = getNullMetaItem(); base.setType(BASE_MAT); var inventory = getMockView(base, null); - var operation = new AnvilOperation(); - operation.setItemRepairedBy((item1, item2) -> true); - var state = new AnvilOperationState(operation, inventory); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(true).when(behavior).itemRepairedBy(notNull(), notNull()); + var state = new AnvilState(behavior, inventory); assertThat( "Must have Damageable meta", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); assertThat( "Non-Damageable must return empty result", - function.getResult(operation, state), + function.getResult(behavior, state), is(AnvilFunctionResult.EMPTY)); } @Test void testCanApplyNotDamaged() { var inventory = getMockView(new ItemStack(BASE_MAT), null); - var operation = new AnvilOperation(); - operation.setItemRepairedBy((item1, item2) -> true); - var state = new AnvilOperationState(operation, inventory); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(true).when(behavior).itemRepairedBy(notNull(), notNull()); + var state = new AnvilState(behavior, inventory); assertThat( "Must be damaged", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); assertThat( "Undamaged must return empty result", - function.getResult(operation, state), + function.getResult(behavior, state), is(AnvilFunctionResult.EMPTY)); } @@ -383,16 +385,16 @@ void testCanApplyNotDamaged() { void testRepair(int repairMats) { var baseItem = getMaxDamageItem(); var inventory = getMockView(baseItem, new ItemStack(REPAIR_MAT, repairMats)); - var operation = new AnvilOperation(); - operation.setItemRepairedBy((item1, item2) -> true); - var state = new ReadableResultState(operation, inventory); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(true).when(behavior).itemRepairedBy(notNull(), notNull()); + var state = new ReadableResultState(behavior, inventory); assertThat( "Must be applicable", - function.canApply(operation, state), + function.canApply(behavior, state), is(true)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); result.modifyResult(state.getResult().getMeta()); @@ -428,29 +430,29 @@ void testRepair(int repairMats) { @Nested class RepairWithCombination { - private final AnvilFunction function = AnvilFunction.REPAIR_WITH_COMBINATION; + private final AnvilFunction function = AnvilFunctions.REPAIR_WITH_COMBINATION; @Test void testCanApplyNotRepairedBy() { var inventory = getMockView(new ItemStack(BASE_MAT), new ItemStack(REPAIR_MAT)); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat( "Must be same item", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); } @Test void testCanApplyNotDurable() { var inventory = getMockView(null, null); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat( "Must have durability", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); } @@ -460,39 +462,38 @@ void testCanApplyNotDamageable() { nullMetaItem.setType(BASE_MAT); ItemStack normalItem = new ItemStack(BASE_MAT); var inventory = getMockView(nullMetaItem, normalItem); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat( "Must have Damageable meta", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); assertThat( "Non-Damageable base must return empty result", - function.getResult(operation, state), + function.getResult(behavior, state), is(AnvilFunctionResult.EMPTY)); inventory = getMockView(normalItem, nullMetaItem); - operation = new AnvilOperation(); - state = new AnvilOperationState(operation, inventory); + state = new AnvilState(behavior, inventory); assertThat( "Non-Damageable addition must return empty result", - function.getResult(operation, state), + function.getResult(behavior, state), is(AnvilFunctionResult.EMPTY)); } @Test void testCanApplyNotDamaged() { var inventory = getMockView(new ItemStack(BASE_MAT), new ItemStack(BASE_MAT)); - var operation = new AnvilOperation(); - var state = new AnvilOperationState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new AnvilState(behavior, inventory); assertThat( "Must be damaged", - function.canApply(operation, state), + function.canApply(behavior, state), is(false)); assertThat( "Undamaged must return empty result", - function.getResult(operation, state), + function.getResult(behavior, state), is(AnvilFunctionResult.EMPTY)); } @@ -500,15 +501,15 @@ void testCanApplyNotDamaged() { void testRepair() { var baseItem = getMaxDamageItem(); var inventory = getMockView(baseItem, baseItem.clone()); - var operation = new AnvilOperation(); - var state = new ReadableResultState(operation, inventory); + var behavior = VanillaAnvil.BEHAVIOR; + var state = new ReadableResultState(behavior, inventory); assertThat( "Must be applicable", - function.canApply(operation, state), + function.canApply(behavior, state), is(true)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); result.modifyResult(state.getResult().getMeta()); diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationStateTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilStateTest.java similarity index 88% rename from src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationStateTest.java rename to src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilStateTest.java index 429ecba..98e1b0a 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationStateTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilStateTest.java @@ -37,9 +37,9 @@ @DisplayName("Verify AnvilOperationState functionality") @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class AnvilOperationStateTest { +class AnvilStateTest { - private AnvilOperation operation; + private AnvilBehavior operation; private AnvilView view; @BeforeAll @@ -62,7 +62,7 @@ void beforeAll() { @BeforeEach void beforeEach() { - operation = new AnvilOperation(); + operation = VanillaAnvil.BEHAVIOR; var anvil = InventoryMocks.newAnvilMock(); view = mock(AnvilView.class); @@ -76,7 +76,7 @@ void beforeEach() { @Test void testGetAnvil() { - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat("Anvil must be provided instance", state.getAnvil(), is(view)); } @@ -84,7 +84,7 @@ void testGetAnvil() { void testBase() { var base = new ItemStack(Material.DIRT); view.setItem(0, base); - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat("Base item must match", state.getBase().getItem(), is(base)); } @@ -92,13 +92,13 @@ void testBase() { void testAddition() { var addition = new ItemStack(Material.DIRT); view.setItem(1, addition); - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat("Addition item must match", state.getAddition().getItem(), is(addition)); } @Test void testGetSetLevelCost() { - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat("Level cost starts at 0", state.getLevelCost(), is(0)); int value = 10; state.setLevelCost(value); @@ -107,7 +107,7 @@ void testGetSetLevelCost() { @Test void testGetSetMaterialCost() { - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat("Material cost starts at 0", state.getMaterialCost(), is(0)); int value = 10; state.setMaterialCost(value); @@ -119,15 +119,15 @@ void testApplyNonApplicable() { var function = new AnvilFunction() { @Override public boolean canApply( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { return false; } @Override public @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { return new AnvilFunctionResult() { @Override public int getLevelCostIncrease() { @@ -142,7 +142,7 @@ public int getMaterialCostIncrease() { } }; - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat( "Non-applicable function does not apply", state.apply(function), @@ -157,15 +157,15 @@ void testApply() { var function = new AnvilFunction() { @Override public boolean canApply( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { return true; } @Override public @NotNull AnvilFunctionResult getResult( - @NotNull AnvilOperation operation, - @NotNull AnvilOperationState state) { + @NotNull AnvilBehavior behavior, + @NotNull AnvilState state) { return new AnvilFunctionResult() { @Override public int getLevelCostIncrease() { @@ -180,7 +180,7 @@ public int getMaterialCostIncrease() { } }; - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat("Applicable function applies", state.apply(function)); assertThat("Level cost is added", state.getLevelCost(), is(value)); @@ -200,7 +200,7 @@ void testForgeNullBaseMeta() { } }); - var state = new AnvilOperationState(operation, view); + var state = new AnvilState(operation, view); assertThat("AnvilResult must be empty constant", state.forge(), is(AnvilResult.EMPTY)); } @@ -224,7 +224,7 @@ void testForgeNullResultMeta() { } }); - var state = new AnvilOperationState(operation, view) { + var state = new AnvilState(operation, view) { private final MetaCachedStack fakeBase = new MetaCachedStack(new ItemStack(Material.DIRT)); @Override public @NotNull MetaCachedStack getBase() { diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java index 548329b..a0385b6 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/CombineEnchantmentsTest.java @@ -11,9 +11,11 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; @@ -105,33 +107,33 @@ protected CombineEnchantsTest(AnvilFunction function) { @Test void testAppliesIfNotCombine() { var anvil = getMockView(new ItemStack(BASE_MAT), new ItemStack(BASE_MAT)); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> false)); - var state = new AnvilOperationState(operation, anvil); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(false).when(behavior).itemsCombineEnchants(notNull(), notNull()); + var state = new AnvilState(behavior, anvil); - assertThat("Combination must not apply", function.canApply(operation, state), is(false)); + assertThat("Combination must not apply", function.canApply(behavior, state), is(false)); } @Test void testAppliesIfCombine() { var anvil = getMockView(new ItemStack(BASE_MAT), new ItemStack(BASE_MAT)); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - var state = new AnvilOperationState(operation, anvil); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + var state = new AnvilState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); } @Test void testNoEnchantsAdded() { var anvil = getMockView(new ItemStack(BASE_MAT), new ItemStack(BASE_MAT)); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - var state = new AnvilOperationState(operation, anvil); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + var state = new AnvilState(behavior, anvil); assertThat( "No enchants added yields empty result", - function.getResult(operation, state), + function.getResult(behavior, state), is(AnvilFunctionResult.EMPTY)); } @@ -143,15 +145,15 @@ void testBasicAdd() { applyEnchantments(addition, enchantments); var anvil = getMockView(new ItemStack(BASE_MAT), addition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - var state = new ReadableResultState(operation, anvil); + var behavior = spy(VanillaAnvil.BEHAVIOR); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); + var state = new ReadableResultState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); @@ -183,22 +185,21 @@ void testBookHalfPrice() { var anvil = getMockView(base, addition); var bookAnvil = getMockView(base, bookAddition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - operation.setEnchantsConflict((enchantment1, enchantment2) -> false); + var behavior = mock(AnvilBehavior.class); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); - var state = new ReadableResultState(operation, anvil); - var bookState = new ReadableResultState(operation, bookAnvil); + var state = new ReadableResultState(behavior, anvil); + var bookState = new ReadableResultState(behavior, bookAnvil); - assertThat("Combination must apply", function.canApply(operation, state)); - assertThat("Book combination must apply", function.canApply(operation, bookState)); + assertThat("Combination must apply", function.canApply(behavior, state)); + assertThat("Book combination must apply", function.canApply(behavior, bookState)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); - AnvilFunctionResult bookResult = function.getResult(operation, bookState); + AnvilFunctionResult bookResult = function.getResult(behavior, bookState); assertThat("Book material cost is unchanged", bookResult.getMaterialCostIncrease(), is(0)); bookResult.modifyResult(bookState.getResult().getMeta()); @@ -227,16 +228,15 @@ void testCombineAdd() { applyEnchantments(addition, enchantments); var anvil = getMockView(base, addition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - operation.setEnchantsConflict((enchantment1, enchantment2) -> false); - var state = new ReadableResultState(operation, anvil); + var behavior = mock(AnvilBehavior.class); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); + var state = new ReadableResultState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); @@ -266,16 +266,16 @@ void testMergeInvalid() { applyEnchantments(addition, invalidEnchants); var anvil = getMockView(base, addition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - operation.setEnchantsConflict((enchantment1, enchantment2) -> true); - var state = new ReadableResultState(operation, anvil); + var behavior = mock(AnvilBehavior.class); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); + doReturn(true).when(behavior).enchantsConflict(notNull(), notNull()); + var state = new ReadableResultState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); @@ -300,16 +300,15 @@ void testCommonTridentEnchant() { ItemStack base = new ItemStack(BASE_MAT); var anvil = getMockView(base, addition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - operation.setEnchantsConflict((enchantment1, enchantment2) -> false); - var state = new ReadableResultState(operation, anvil); + var behavior = mock(AnvilBehavior.class); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); + var state = new ReadableResultState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); @@ -329,16 +328,15 @@ void testTridentEnchant() { ItemStack base = new ItemStack(BASE_MAT); var anvil = getMockView(base, addition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - operation.setEnchantsConflict((enchantment1, enchantment2) -> false); - var state = new ReadableResultState(operation, anvil); + var behavior = mock(AnvilBehavior.class); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); + var state = new ReadableResultState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); @@ -360,7 +358,7 @@ void testTridentEnchant() { class CombineEnchantsJavaEdition extends CombineEnchantsTest { protected CombineEnchantsJavaEdition() { - super(AnvilFunction.COMBINE_ENCHANTMENTS_JAVA_EDITION); + super(AnvilFunctions.COMBINE_ENCHANTMENTS_JAVA_EDITION); } @Override @@ -389,16 +387,15 @@ void testNegativeCost() { applyEnchantments(addition, enchantments); var anvil = getMockView(base, addition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - operation.setEnchantsConflict((enchantment1, enchantment2) -> false); - var state = new ReadableResultState(operation, anvil); + var behavior = mock(AnvilBehavior.class); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); + var state = new ReadableResultState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); @@ -418,7 +415,7 @@ void testNegativeCost() { class CombineEnchantsBedrockEdition extends CombineEnchantsTest { protected CombineEnchantsBedrockEdition() { - super(AnvilFunction.COMBINE_ENCHANTMENTS_BEDROCK_EDITION); + super(AnvilFunctions.COMBINE_ENCHANTMENTS_BEDROCK_EDITION); } @Override @@ -447,16 +444,15 @@ void testOldLevelHigher() { applyEnchantments(base, enchantments); var anvil = getMockView(base, addition); - var operation = new AnvilOperation(); - operation.setItemsCombineEnchants(((itemStack, itemStack2) -> true)); - operation.setEnchantApplies((enchantment, item) -> true); - operation.setEnchantMaxLevel(enchantment -> Short.MAX_VALUE); - operation.setEnchantsConflict((enchantment1, enchantment2) -> false); - var state = new ReadableResultState(operation, anvil); + var behavior = mock(AnvilBehavior.class); + doReturn(true).when(behavior).itemsCombineEnchants(notNull(), notNull()); + doReturn(true).when(behavior).enchantApplies(notNull(), notNull()); + doReturn((int) Short.MAX_VALUE).when(behavior).getEnchantMaxLevel(notNull()); + var state = new ReadableResultState(behavior, anvil); - assertThat("Combination must apply", function.canApply(operation, state)); + assertThat("Combination must apply", function.canApply(behavior, state)); - AnvilFunctionResult result = function.getResult(operation, state); + AnvilFunctionResult result = function.getResult(behavior, state); assertThat("Material cost is unchanged", result.getMaterialCostIncrease(), is(0)); result.modifyResult(state.getResult().getMeta()); diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationTest.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/VanillaAnvilTest.java similarity index 76% rename from src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationTest.java rename to src/test/java/com/github/jikoo/planarenchanting/anvil/VanillaAnvilTest.java index f0eea67..e492baa 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/AnvilOperationTest.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/VanillaAnvilTest.java @@ -13,6 +13,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.github.jikoo.planarenchanting.util.MetaCachedStack; import com.github.jikoo.planarenchanting.util.mock.ServerMocks; import com.github.jikoo.planarenchanting.util.mock.enchantments.EnchantmentMocks; import com.github.jikoo.planarenchanting.util.mock.inventory.InventoryMocks; @@ -33,7 +34,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -44,9 +44,9 @@ * Note: These tests are only supposed to cover the functionality of the AnvilOperation class. * Specific operations are not verified, that is handled in more specific and thorough tests. */ -@DisplayName("Verify AnvilOperation application") +@DisplayName("Verify VanillaAnvil application") @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class AnvilOperationTest { +class VanillaAnvilTest { private static final Material TOOL = Material.DIAMOND_SHOVEL; private static final Material TOOL_REPAIR = Material.DIAMOND; @@ -54,8 +54,6 @@ class AnvilOperationTest { private static final Material INCOMPATIBLE = Material.STONE; public static Enchantment toolEnchantment; - private AnvilOperation operation; - @BeforeAll void beforeAll() { Server server = ServerMocks.mockServer(); @@ -78,38 +76,27 @@ void beforeAll() { doReturn(Set.of(TOOL)).when(tag).getValues(); } - @BeforeEach - void beforeEach() { - operation = new AnvilOperation(); - } - @Test void testEnchantmentTarget() { - var item = new ItemStack(TOOL); - assertThat("Enchantment applies to tools", operation.enchantApplies(toolEnchantment, item)); - item.setType(INCOMPATIBLE); + var item = new MetaCachedStack(new ItemStack(TOOL)); + assertThat( + "Enchantment applies to tools", + VanillaAnvil.BEHAVIOR.enchantApplies(toolEnchantment, item)); + item.getItem().setType(INCOMPATIBLE); assertThat( "Enchantment does not apply to non-tools", - operation.enchantApplies(toolEnchantment, item), + VanillaAnvil.BEHAVIOR.enchantApplies(toolEnchantment, item), is(false)); - operation.setEnchantApplies((enchant, itemStack) -> true); - assertThat( - "Enchantment applies with alternate predicate", - operation.enchantApplies(toolEnchantment, item)); } @Test void testEnchantmentConflict() { - Enchantment conflict1 = Enchantment.SILK_TOUCH; - Enchantment conflict2 = Enchantment.FORTUNE; - assertThat( "Vanilla enchantments conflict", - operation.enchantsConflict(conflict1, conflict2)); - operation.setEnchantsConflict(((enchantment, enchantment2) -> false)); + VanillaAnvil.BEHAVIOR.enchantsConflict(Enchantment.SILK_TOUCH, Enchantment.FORTUNE)); assertThat( - "Enchantments do not conflict with alternate predicate", - operation.enchantsConflict(conflict1, conflict2), + "Nonconflicting enchantments do not conflict", + VanillaAnvil.BEHAVIOR.enchantsConflict(Enchantment.EFFICIENCY, Enchantment.FORTUNE), is(false)); } @@ -118,13 +105,8 @@ void testEnchantmentConflict() { void testEnchantmentMaxLevel(Enchantment enchantment) { assertThat( "Enchantment max level must be vanilla", - operation.getEnchantMaxLevel(enchantment), + VanillaAnvil.BEHAVIOR.getEnchantMaxLevel(enchantment), is(enchantment.getMaxLevel())); - operation.setEnchantMaxLevel(enchant -> Short.MAX_VALUE); - assertThat( - "Enchantment max level must set as expected", - operation.getEnchantMaxLevel(enchantment), - is((int) Short.MAX_VALUE)); } private static @NotNull Stream getEnchantments() { @@ -133,47 +115,43 @@ void testEnchantmentMaxLevel(Enchantment enchantment) { @Test void testSameMaterialEnchantCombination() { - ItemStack base = new ItemStack(TOOL); - ItemStack addition = new ItemStack(TOOL); + var base = new MetaCachedStack(new ItemStack(TOOL)); + var addition = new MetaCachedStack(new ItemStack(TOOL)); assertThat( "Same type combine enchantments", - operation.itemsCombineEnchants(base, addition)); + VanillaAnvil.BEHAVIOR.itemsCombineEnchants(base, addition)); } @Test void testEnchantedBookEnchantCombination() { - ItemStack base = new ItemStack(TOOL); - ItemStack addition = new ItemStack(BOOK); + var base = new MetaCachedStack(new ItemStack(TOOL)); + var addition = new MetaCachedStack(new ItemStack(BOOK)); assertThat( "Enchanted books combine enchantments", - operation.itemsCombineEnchants(base, addition)); + VanillaAnvil.BEHAVIOR.itemsCombineEnchants(base, addition)); } @Test void testDifferentMaterialEnchantCombination() { - ItemStack base = new ItemStack(TOOL); - ItemStack addition = new ItemStack(INCOMPATIBLE); + var base = new MetaCachedStack(new ItemStack(TOOL)); + var addition = new MetaCachedStack(new ItemStack(INCOMPATIBLE)); assertThat( "Incompatible materials do not combine enchantments", - operation.itemsCombineEnchants(base, addition), - not(true)); - operation.setItemsCombineEnchants((itemStack, itemStack2) -> true); - assertThat( - "Enchantments combine with alternate predicate", - operation.itemsCombineEnchants(base, addition)); + VanillaAnvil.BEHAVIOR.itemsCombineEnchants(base, addition), + is(false)); } @Test void testEmptyBaseIsEmpty() { var anvil = getMockView(null, null); - var result = operation.apply(anvil); + var result = new VanillaAnvil().getResult(anvil); assertThat("Result must be empty", result, is(AnvilResult.EMPTY)); } @Test void testEmptyAdditionIsEmpty() { var anvil = getMockView(new ItemStack(TOOL), null); - var result = operation.apply(anvil); + var result = new VanillaAnvil().getResult(anvil); assertThat("Result must be empty", result, is(AnvilResult.EMPTY)); } @@ -181,7 +159,7 @@ void testEmptyAdditionIsEmpty() { void testEmptyAdditionRenameNotEmpty() { var anvil = getMockView(new ItemStack(TOOL), null); when(anvil.getRenameText()).thenReturn("Sample Text"); - var result = operation.apply(anvil); + var result = new VanillaAnvil().getResult(anvil); assertThat("Result must not be empty", result, not(AnvilResult.EMPTY)); } @@ -191,7 +169,7 @@ void testMultipleBaseIsEmpty() { base.setAmount(2); var addition = new ItemStack(TOOL); var anvil = getMockView(base, addition); - var result = operation.apply(anvil); + var result = new VanillaAnvil().getResult(anvil); assertThat("Result must be empty", result, is(AnvilResult.EMPTY)); } @@ -209,10 +187,10 @@ void testRepairWithMaterial() { var addition = new ItemStack(TOOL_REPAIR); - assertThat("Base must be repairable by addition", operation.itemRepairedBy(base, addition)); + assertThat("Base must be repairable by addition", VanillaAnvil.BEHAVIOR.itemRepairedBy(new MetaCachedStack(base), new MetaCachedStack(addition))); var anvil = getMockView(base, addition); - var result = operation.apply(anvil); + var result = new VanillaAnvil().getResult(anvil); assertThat("Result must not be empty", result, is(not(AnvilResult.EMPTY))); @@ -240,7 +218,7 @@ void testRepairWithCombination() { var addition = base.clone(); var anvil = getMockView(base, addition); - var result = operation.apply(anvil); + var result = new VanillaAnvil().getResult(anvil); assertThat("Result must not be empty", result, is(not(AnvilResult.EMPTY))); diff --git a/src/test/java/com/github/jikoo/planarenchanting/anvil/mock/ReadableResultState.java b/src/test/java/com/github/jikoo/planarenchanting/anvil/mock/ReadableResultState.java index 29a57b7..bb0d5c0 100644 --- a/src/test/java/com/github/jikoo/planarenchanting/anvil/mock/ReadableResultState.java +++ b/src/test/java/com/github/jikoo/planarenchanting/anvil/mock/ReadableResultState.java @@ -1,17 +1,17 @@ package com.github.jikoo.planarenchanting.anvil.mock; -import com.github.jikoo.planarenchanting.anvil.AnvilOperation; -import com.github.jikoo.planarenchanting.anvil.AnvilOperationState; +import com.github.jikoo.planarenchanting.anvil.AnvilBehavior; +import com.github.jikoo.planarenchanting.anvil.AnvilState; import com.github.jikoo.planarenchanting.util.MetaCachedStack; import org.bukkit.inventory.view.AnvilView; import org.jetbrains.annotations.NotNull; -public class ReadableResultState extends AnvilOperationState { +public class ReadableResultState extends AnvilState { public ReadableResultState( - @NotNull AnvilOperation operation, + @NotNull AnvilBehavior behavior, @NotNull AnvilView view) { - super(operation, view); + super(behavior, view); } public @NotNull MetaCachedStack getResult() {