diff --git a/bukkit-loader/src/main/resources/resources/default/configuration/items.yml b/bukkit-loader/src/main/resources/resources/default/configuration/items.yml index 54c02a507..ba4034232 100644 --- a/bukkit-loader/src/main/resources/resources/default/configuration/items.yml +++ b/bukkit-loader/src/main/resources/resources/default/configuration/items.yml @@ -2,6 +2,9 @@ items: default:topaz_rod: material: fishing_rod custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -15,6 +18,9 @@ items: default:topaz_bow: material: bow custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -32,6 +38,9 @@ items: default:topaz_crossbow: material: crossbow custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -53,6 +62,9 @@ items: default:topaz_pickaxe: material: golden_pickaxe custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -68,6 +80,9 @@ items: default:topaz_axe: material: golden_axe custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -83,6 +98,9 @@ items: default:topaz_hoe: material: golden_hoe custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -98,6 +116,9 @@ items: default:topaz_shovel: material: golden_shovel custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -113,6 +134,9 @@ items: default:topaz_sword: material: golden_sword custom-model-data: 1000 + settings: + tags: + - "topaz_tools" data: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz @@ -154,6 +178,8 @@ templates: display-name: "<#FF8C00>" tooltip-style: minecraft:topaz settings: + tags: + - "topaz_tools" equippable: slot: "{slot}" asset-id: topaz diff --git a/bukkit-loader/src/main/resources/resources/default/configuration/ores.yml b/bukkit-loader/src/main/resources/resources/default/configuration/ores.yml index 19fd10f4c..f1bdfb981 100644 --- a/bukkit-loader/src/main/resources/resources/default/configuration/ores.yml +++ b/bukkit-loader/src/main/resources/resources/default/configuration/ores.yml @@ -28,6 +28,11 @@ items: default:topaz: material: paper custom-model-data: 1012 + settings: + anvil-repair-item: + - target: + - "#topaz_tools" + percent: 0.25 data: display-name: "<#FF8C00>" model: diff --git a/bukkit/legacy/src/main/java/net/momirealms/craftengine/bukkit/util/LegacyInventoryUtils.java b/bukkit/legacy/src/main/java/net/momirealms/craftengine/bukkit/util/LegacyInventoryUtils.java index 7abee98f0..ddbad77d5 100644 --- a/bukkit/legacy/src/main/java/net/momirealms/craftengine/bukkit/util/LegacyInventoryUtils.java +++ b/bukkit/legacy/src/main/java/net/momirealms/craftengine/bukkit/util/LegacyInventoryUtils.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.util; import org.bukkit.entity.Player; +import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.Inventory; public class LegacyInventoryUtils { @@ -8,4 +9,9 @@ public class LegacyInventoryUtils { public static Inventory getTopInventory(Player player) { return player.getOpenInventory().getTopInventory(); } + + public static void setRepairCost(AnvilInventory anvilInventory, int repairCost, int amount) { + anvilInventory.setRepairCost(repairCost); + anvilInventory.setRepairCostAmount(amount); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index bdd1a5d52..e6106e8d3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -6,17 +6,16 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.ItemUtils; +import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.core.item.CustomItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.ItemManager; +import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.item.recipe.CustomCampfireRecipe; import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem; import net.momirealms.craftengine.core.item.recipe.Recipe; import net.momirealms.craftengine.core.item.recipe.RecipeTypes; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; +import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.plugin.config.ConfigManager; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -36,11 +35,13 @@ import org.bukkit.event.block.*; import org.bukkit.event.inventory.*; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.*; +import org.bukkit.inventory.view.AnvilView; import java.util.ArrayList; import java.util.List; import java.util.Optional; +@SuppressWarnings("DuplicatedCode") public class RecipeEventListener implements Listener { private static final OptimizedIDItem EMPTY = new OptimizedIDItem<>(null, null); private final ItemManager itemManager; @@ -366,6 +367,7 @@ public class RecipeEventListener implements Listener { } // for 1.21.2+ + @SuppressWarnings("UnstableApiUsage") @EventHandler(ignoreCancelled = true) public void onCampfireCook(CampfireStartEvent event) { if (!ConfigManager.enableRecipeSystem()) return; @@ -404,6 +406,8 @@ public class RecipeEventListener implements Listener { Material type = event.getBlock().getType(); if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return; CampfireRecipe recipe = (CampfireRecipe) event.getRecipe(); + if (recipe == null) return; + Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value()); boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); @@ -432,7 +436,7 @@ public class RecipeEventListener implements Listener { // Paper only @EventHandler public void onPrepareResult(PrepareResultEvent event) { - if (!ConfigManager.enableRecipeSystem()) return; +// if (!ConfigManager.enableRecipeSystem()) return; if (event.getInventory() instanceof CartographyInventory cartographyInventory) { if (ItemUtils.hasCustomItem(cartographyInventory.getStorageContents())) { event.setResult(new ItemStack(Material.AIR)); @@ -440,10 +444,130 @@ public class RecipeEventListener implements Listener { } } + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onAnvilCombineItems(PrepareAnvilEvent event) { + AnvilInventory inventory = event.getInventory(); + ItemStack first = inventory.getFirstItem(); + ItemStack second = inventory.getSecondItem(); + if (first == null || second == null) return; + Item wrappedFirst = BukkitItemManager.instance().wrap(first); + boolean firstCustom = wrappedFirst.isCustomItem(); + Item wrappedSecond = BukkitItemManager.instance().wrap(second); + boolean secondCustom = wrappedSecond.isCustomItem(); + // both are vanilla items + if (!firstCustom && !secondCustom) { + return; + } + + // one of them is vanilla item + if (!firstCustom || !secondCustom) { + // block "vanilla + custom" recipes + if (wrappedFirst.vanillaId().equals(wrappedSecond.vanillaId())) { + event.setResult(null); + } + return; + } + + // both of them are custom items + // if the second is an enchanted book, then apply it + if (wrappedSecond.vanillaId().equals(ItemKeys.ENCHANTED_BOOK)) { + return; + } + + // not the same item + if (!wrappedFirst.customId().equals(wrappedSecond.customId())) { + event.setResult(null); + return; + } + + // can not repair + wrappedFirst.getCustomItem().ifPresent(it -> { + if (!it.settings().canRepair()) { + event.setResult(null); + } + }); + } + + @SuppressWarnings("UnstableApiUsage") + @EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL) + public void onAnvilRepairItems(PrepareAnvilEvent event) { + AnvilInventory inventory = event.getInventory(); + ItemStack first = inventory.getFirstItem(); + ItemStack second = inventory.getSecondItem(); + if (first == null || second == null) return; + + Item wrappedSecond = BukkitItemManager.instance().wrap(second); + // if the second slot is not a custom item, ignore it + Optional> customItemOptional = plugin.itemManager().getCustomItem(wrappedSecond.id()); + if (customItemOptional.isEmpty()) { + return; + } + + CustomItem customItem = customItemOptional.get(); + List repairItems = customItem.settings().repairItems(); + // if the second slot is not a repair item, ignore it + if (repairItems.isEmpty()) { + return; + } + + Item wrappedFirst = BukkitItemManager.instance().wrap(first); + int damage = wrappedFirst.damage().orElse(0); + int maxDamage = wrappedFirst.maxDamage().orElse(0); + // not a repairable item + if (maxDamage == 0 || damage == 0) return; + + Key firstId = wrappedFirst.id(); + Optional> optionalCustomTool = wrappedFirst.getCustomItem(); + // can not repair + if (optionalCustomTool.isPresent() && !optionalCustomTool.get().settings().canRepair()) { + return; + } + + AnvilRepairItem repairItem = null; + for (AnvilRepairItem item : repairItems) { + for (String target : item.targets()) { + if (target.charAt(0) == '#') { + Key tag = Key.of(target.substring(1)); + if (optionalCustomTool.isPresent() && optionalCustomTool.get().is(tag)) { + repairItem = item; + break; + } + if (wrappedFirst.is(tag)) { + repairItem = item; + break; + } + } else if (target.equals(firstId.toString())) { + repairItem = item; + break; + } + } + } + + // no repair item matching + if (repairItem == null) { + return; + } + + int realDurabilityPerItem = (int) (repairItem.amount() + repairItem.percent() * maxDamage); + int consumeMaxAmount = damage / realDurabilityPerItem + 1; + int actualConsumedAmount = Math.min(consumeMaxAmount, wrappedSecond.count()); + int actualRepairAmount = actualConsumedAmount * realDurabilityPerItem; + int damageAfter = Math.max(damage - actualRepairAmount, 0); + wrappedFirst.damage(damageAfter); + event.setResult(wrappedFirst.loadCopy()); + if (VersionHelper.isVersionNewerThan1_21()) { + AnvilView anvilView = event.getView(); + anvilView.setRepairCost(10); + anvilView.setRepairItemCountCost(actualConsumedAmount); + } else { + LegacyInventoryUtils.setRepairCost(inventory, 10, actualRepairAmount); + } + } + // only handle repair items for the moment @EventHandler(ignoreCancelled = true) public void onSpecialRecipe(PrepareItemCraftEvent event) { - if (!ConfigManager.enableRecipeSystem()) return; +// if (!ConfigManager.enableRecipeSystem()) return; org.bukkit.inventory.Recipe recipe = event.getRecipe(); if (recipe == null) return; @@ -499,7 +623,7 @@ public class RecipeEventListener implements Listener { } Optional> customItemOptional = plugin.itemManager().getCustomItem(left.id()); - if (!customItemOptional.isPresent()) { + if (customItemOptional.isEmpty()) { inventory.setResult(null); return; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AnvilRepairItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AnvilRepairItem.java new file mode 100644 index 000000000..8ea18261d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AnvilRepairItem.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.item; + +import java.util.List; + +public record AnvilRepairItem(List targets, int amount, double percent) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java index e3dabdc37..916f7a510 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java @@ -19,6 +19,10 @@ public interface CustomItem extends BuildableItem { ItemSettings settings(); + default boolean is(Key tag) { + return settings().tags().contains(tag); + } + default Item buildItem(Player player) { return buildItem(new ItemBuildContext(player, ContextHolder.EMPTY)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index c8bf5c432..c56197dc1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -17,13 +17,14 @@ public class ItemSettings { @Nullable EquipmentGeneration equipment; boolean canRepair = true; + List anvilRepairItems = List.of(); private ItemSettings() {} public List> modifiers() { ArrayList> modifiers = new ArrayList<>(); if (VersionHelper.isVersionNewerThan1_21_2() && this.equipment != null && this.equipment.modernData() != null) modifiers.add(new EquippableModifier<>(this.equipment.modernData())); - + // TODO 1.20 return modifiers; } @@ -41,6 +42,7 @@ public class ItemSettings { newSettings.tags = settings.tags; newSettings.equipment = settings.equipment; newSettings.canRepair = settings.canRepair; + newSettings.anvilRepairItems = settings.anvilRepairItems; return newSettings; } @@ -68,11 +70,20 @@ public class ItemSettings { return tags; } + public List repairItems() { + return anvilRepairItems; + } + @Nullable public EquipmentGeneration equipment() { return equipment; } + public ItemSettings repairItems(List items) { + this.anvilRepairItems = items; + return this; + } + public ItemSettings canRepair(boolean canRepair) { this.canRepair = canRepair; return this; @@ -113,6 +124,17 @@ public class ItemSettings { boolean bool = (boolean) value; return settings -> settings.canRepair(bool); })); + registerFactory("anvil-repair-item", (value -> { + @SuppressWarnings("unchecked") + List> materials = (List>) value; + List anvilRepairItemList = new ArrayList<>(); + for (Map material : materials) { + int amount = MiscUtils.getAsInt(material.getOrDefault("amount", 0)); + double percent = MiscUtils.getAsDouble(material.getOrDefault("percent", 0)); + anvilRepairItemList.add(new AnvilRepairItem(MiscUtils.getAsStringList(material.get("target")), amount, percent)); + } + return settings -> settings.repairItems(anvilRepairItemList); + })); registerFactory("fuel-time", (value -> { int intValue = MiscUtils.getAsInt(value); return settings -> settings.fuelTime(intValue); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java index fc810fa83..fbb6cf742 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java @@ -2,14 +2,14 @@ package net.momirealms.craftengine.core.pack; public class LoadingSequence { public static final int TEMPLATE = 0; - public static final int BLOCK = 10; - public static final int ITEM = 20; - public static final int FURNITURE = 30; - public static final int FONT = 40; - public static final int RECIPE = 50; - public static final int CATEGORY = 60; - public static final int TRANSLATION = 70; - public static final int LANG = 80; + public static final int LANG = 10; + public static final int TRANSLATION = 20; + public static final int BLOCK = 30; + public static final int ITEM = 40; + public static final int FURNITURE = 50; + public static final int FONT = 60; + public static final int RECIPE = 70; + public static final int CATEGORY = 80; public static final int SOUND = 90; public static final int JUKEBOX_SONG = 100; }