diff --git a/.gradle/8.8/executionHistory/executionHistory.lock b/.gradle/8.8/executionHistory/executionHistory.lock
index 3d7bdde..6e7a580 100644
Binary files a/.gradle/8.8/executionHistory/executionHistory.lock and b/.gradle/8.8/executionHistory/executionHistory.lock differ
diff --git a/.gradle/8.8/fileHashes/fileHashes.lock b/.gradle/8.8/fileHashes/fileHashes.lock
index 2022636..610ab4f 100644
Binary files a/.gradle/8.8/fileHashes/fileHashes.lock and b/.gradle/8.8/fileHashes/fileHashes.lock differ
diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock
index a0e16bd..b603c89 100644
Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ
diff --git a/.idea/modules/ArmorSMP.main.iml b/.idea/modules/ArmorSMP.main.iml
index 746f1fd..bbeeb3e 100644
--- a/.idea/modules/ArmorSMP.main.iml
+++ b/.idea/modules/ArmorSMP.main.iml
@@ -11,8 +11,4 @@
-
-
-
-
\ No newline at end of file
diff --git a/build/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin
index 32aaaa5..7975c6b 100644
Binary files a/build/tmp/compileJava/previous-compilation-data.bin and b/build/tmp/compileJava/previous-compilation-data.bin differ
diff --git a/src/main/java/me/trouper/armorsmp/data/Unique.java b/src/main/java/me/trouper/armorsmp/data/Unique.java
index 7afd006..0ac2ff0 100644
--- a/src/main/java/me/trouper/armorsmp/data/Unique.java
+++ b/src/main/java/me/trouper/armorsmp/data/Unique.java
@@ -44,7 +44,8 @@ public enum Unique {
DRAGON_EGG(ItemBuilder.create()
.material(Material.NETHERITE_CHESTPLATE)
.lore(Text.legacyColor("&bAbilities:"))
- .lore(Text.legacyColor("&3| &7Dragon's Breath"))
+ .lore(Text.legacyColor("&3| &7Bad Breath"))
+ .lore(Text.legacyColor(" &1- &7Shoot Dragon's Breath for 7 Seconds"))
.lore(Text.legacyColor("&3| &7Resistence I"))
.lore(Text.legacyColor("&3| &7Strength I"))
.enchant(Enchantment.BINDING_CURSE,1)
@@ -78,7 +79,8 @@ public enum Unique {
LEGGINGS(ItemBuilder.create()
.material(Material.NETHERITE_LEGGINGS)
.lore(Text.legacyColor("&bAbilities:"))
- .lore(Text.legacyColor("&3| &7Knockback Shield"))
+ .lore(Text.legacyColor("&3| &7Back-Up"))
+ .lore(Text.legacyColor(" &1- &7Create a shield around you knocking players away"))
.lore(Text.legacyColor("&3| &7Resistence I"))
.enchant(Enchantment.BINDING_CURSE,1)
.enchant(Enchantment.PROTECTION,4)
@@ -90,8 +92,6 @@ public enum Unique {
"Netherite Leggings", (p) -> {
p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE,21,0,true,false,false));
}, (p) -> {
- // TODO: Test Shield
-
Stream toKnockBack = p.getNearbyEntities(10,10,10).stream().filter(target -> {
if (!(target instanceof Player v)) return false;
boolean tooClose = target.getLocation().distance(p.getLocation()) < 10;
@@ -140,6 +140,7 @@ public enum Unique {
.material(Material.NETHERITE_BOOTS)
.lore(Text.legacyColor("&bAbilities:"))
.lore(Text.legacyColor("&3| &7Dash"))
+ .lore(Text.legacyColor(" &1- &7Launch yourself forwards"))
.lore(Text.legacyColor("&3| &7Speed 1"))
.enchant(Enchantment.BINDING_CURSE,1)
.enchant(Enchantment.PROTECTION,4)
@@ -175,6 +176,9 @@ public enum Unique {
}, 50),
SWORD(ItemBuilder.create()
.material(Material.NETHERITE_SWORD)
+ .lore(Text.legacyColor("&bAbilities:"))
+ .lore(Text.legacyColor("&3| &7Go Wild"))
+ .lore(Text.legacyColor(" &1- &7Strength 2 for 7 seconds"))
.enchant(Enchantment.UNBREAKING,3)
.enchant(Enchantment.MENDING,1)
.enchant(Enchantment.SHARPNESS,5)
@@ -182,10 +186,13 @@ public enum Unique {
.build(),
"Netherite Sword", (p) -> {
}, (p) -> {
- p.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH,140,1,true,false,false));
+ p.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH,20*7,1,true,false,false));
}, 50),
AXE(ItemBuilder.create()
.material(Material.NETHERITE_AXE)
+ .lore(Text.legacyColor("&bAbilities:"))
+ .lore(Text.legacyColor("&3| &7Rampage"))
+ .lore(Text.legacyColor(" &1- &7Haste 6 for 5 seconds"))
.enchant(Enchantment.UNBREAKING,3)
.enchant(Enchantment.MENDING,1)
.enchant(Enchantment.SHARPNESS,5)
@@ -194,7 +201,7 @@ public enum Unique {
.build(),
"Netherite Axe", (p) -> {
}, (p) -> {
- p.addPotionEffect(new PotionEffect(PotionEffectType.HASTE,140,5,true,false,false));
+ p.addPotionEffect(new PotionEffect(PotionEffectType.HASTE,20*5,5,true,false,false));
}, 50);
private final ItemStack inGame;
@@ -217,10 +224,12 @@ public enum Unique {
public static boolean isUnique(ItemStack i) {
if (i == null) return false;
+ Verbose.send("Checking if item is unique %s",i.getType());
for (Unique value : values()) {
if (ItemUtils.isSimilar(value.getInGameItem(),i)) return true;
if (i.getType().equals(Material.DRAGON_EGG)) return true;
}
+ Verbose.send("Item did not match any unique material...");
return false;
}
diff --git a/src/main/java/me/trouper/armorsmp/server/Manager.java b/src/main/java/me/trouper/armorsmp/server/Manager.java
index 6d0f95c..8059967 100644
--- a/src/main/java/me/trouper/armorsmp/server/Manager.java
+++ b/src/main/java/me/trouper/armorsmp/server/Manager.java
@@ -19,6 +19,7 @@ public class Manager {
public TierBackend tiers;
public Broadcaster broadcaster;
public UniquesBackend uniques;
+ public ArmorUpgrade upgrade;
public Manager() {
io = new IO();
@@ -33,7 +34,8 @@ public class Manager {
//uniques = new UniquesBackend();
broadcaster = new Broadcaster();
uniques = new UniquesBackend();
-
+ upgrade = new ArmorUpgrade();
+
registerCommands();
registerEvents();
registerCrafting();
@@ -66,9 +68,8 @@ public class Manager {
private void registerCrafting() {
ArmorSMP.getInstance().getLogger().info("Registering Crafts");
- ArmorUpgrade armorUpgrade = new ArmorUpgrade();
- armorUpgrade.removeRecipe();
- armorUpgrade.addRecipe();
+ upgrade.removeRecipe();
+ upgrade.addRecipe();
}
}
diff --git a/src/main/java/me/trouper/armorsmp/server/commands/AbilityCommand.java b/src/main/java/me/trouper/armorsmp/server/commands/AbilityCommand.java
index 8c20cf5..5337a87 100644
--- a/src/main/java/me/trouper/armorsmp/server/commands/AbilityCommand.java
+++ b/src/main/java/me/trouper/armorsmp/server/commands/AbilityCommand.java
@@ -19,7 +19,7 @@ import java.util.UUID;
@CommandRegistry(value = "ability", printStackTrace = true, playersOnly = true)
public class AbilityCommand implements CustomCommand {
- private final Cooldown> abilityCooldown = new Cooldown<>();
+ private final Cooldown abilityCooldown = new Cooldown<>();
@Override
public void dispatchCommand(CommandSender sender, Command command, String label, Args args) {
@@ -33,12 +33,12 @@ public class AbilityCommand implements CustomCommand {
Text.sendMessage(false, Text.Pallet.WARNING, sender, "You do not own the {0}.",piece.getCanonical());
return;
}
- if (abilityCooldown.isOnCooldown(Pair.of(piece,p.getUniqueId()))) {
- Text.sendMessage(false, Text.Pallet.WARNING, sender, "The ability for your {0} is on cooldown for {1} seconds.",piece.getCanonical(),abilityCooldown.getCooldownSec(Pair.of(piece,p.getUniqueId())));
+ if (abilityCooldown.isOnCooldown(getCooldownString(p.getUniqueId(),piece))) {
+ Text.sendMessage(false, Text.Pallet.WARNING, sender, "The ability for your {0} is on cooldown for {1} seconds.",piece.getCanonical(),abilityCooldown.getCooldownSec(getCooldownString(p.getUniqueId(),piece)));
return;
}
piece.getAbility().accept(p);
- abilityCooldown.addCooldown(Pair.of(piece,p.getUniqueId()),piece.getAbilityCooldownSeconds() * 1000L);
+ abilityCooldown.addCooldown(getCooldownString(p.getUniqueId(),piece),piece.getAbilityCooldownSeconds() * 1000L);
Text.sendMessage(false, Text.Pallet.SUCCESS,sender,"Successfully used your {0}'s ability!",piece.getCanonical());
}
@@ -46,4 +46,8 @@ public class AbilityCommand implements CustomCommand {
public void dispatchCompletions(CommandSender commandSender, Command command, String label, CompletionBuilder b) {
b.then(b.argEnum(Unique.class));
}
+
+ public String getCooldownString(UUID who, Unique what) {
+ return "%s:%s".formatted(who,what);
+ }
}
diff --git a/src/main/java/me/trouper/armorsmp/server/commands/AdminCommand.java b/src/main/java/me/trouper/armorsmp/server/commands/AdminCommand.java
index 8eb6e8c..d1a4721 100644
--- a/src/main/java/me/trouper/armorsmp/server/commands/AdminCommand.java
+++ b/src/main/java/me/trouper/armorsmp/server/commands/AdminCommand.java
@@ -9,7 +9,6 @@ import me.trouper.armorsmp.ArmorSMP;
import me.trouper.armorsmp.data.ArmorTier;
import me.trouper.armorsmp.data.io.Config;
import me.trouper.armorsmp.data.Unique;
-import me.trouper.armorsmp.server.crafting.ArmorUpgrade;
import me.trouper.armorsmp.utils.Text;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@@ -51,9 +50,15 @@ public class AdminCommand implements CustomCommand {
b.arg("toggle")
)
.then(
- b.arg("include","exclude")
+ b.arg("exclude")
.then(
b.arg("Class.method")))
+ .then(
+ b.arg("include")
+ .then(
+ b.arg(ArmorSMP.getInstance().getManager().io.config.debuggerExclusions)
+ )
+ )
).then(
b.arg("change")
.then(
@@ -181,7 +186,7 @@ public class AdminCommand implements CustomCommand {
return;
}
- target.getInventory().addItem(ArmorUpgrade.ARMOR_UGPRADE);
+ target.getInventory().addItem(ArmorSMP.getInstance().getManager().upgrade.getItem());
Text.sendMessage(false, Text.Pallet.SUCCESS, sender, "Given and Upgrader to {0}", target.getName());
}
diff --git a/src/main/java/me/trouper/armorsmp/server/crafting/ArmorUpgrade.java b/src/main/java/me/trouper/armorsmp/server/crafting/ArmorUpgrade.java
index 52ee0a0..309f5b8 100644
--- a/src/main/java/me/trouper/armorsmp/server/crafting/ArmorUpgrade.java
+++ b/src/main/java/me/trouper/armorsmp/server/crafting/ArmorUpgrade.java
@@ -2,23 +2,28 @@ package me.trouper.armorsmp.server.crafting;
import io.github.itzispyder.pdk.plugin.builders.ItemBuilder;
import me.trouper.armorsmp.ArmorSMP;
+import me.trouper.armorsmp.utils.Text;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ShapedRecipe;
public class ArmorUpgrade {
- public static final ItemStack ARMOR_UGPRADE = ItemBuilder.create()
- .material(Material.NETHER_STAR)
- .name("§b§lArmor Upgrader")
- .lore("§9| §3Right click to upgrade your gear")
- .count(1)
- .customModelData(1)
- .build();
+ private ItemStack armorUpgrade;
+ public final NamespacedKey key;
+ public final ShapedRecipe recipe;
- public static final NamespacedKey KEY = new NamespacedKey(ArmorSMP.getInstance(), "armor_upgrade_recipe");
-
- public static ShapedRecipe recipe = new ShapedRecipe(KEY, ARMOR_UGPRADE);
+ public ArmorUpgrade() {
+ this.key = new NamespacedKey(ArmorSMP.getInstance(), "armor_upgrade_recipe");
+ this.armorUpgrade = ItemBuilder.create()
+ .material(Material.NETHER_STAR)
+ .name(Text.legacyColor("&b&lArmor Upgrade"))
+ .lore(Text.legacyColor("&9| &3Right click to upgrade your gear"))
+ .count(1)
+ .customModelData(1)
+ .build();
+ this.recipe = new ShapedRecipe(key, armorUpgrade);
+ }
public void addRecipe() {
recipe.shape("ABC", "DEF", "GHI");
@@ -39,6 +44,26 @@ public class ArmorUpgrade {
}
public void removeRecipe() {
- ArmorSMP.getInstance().getServer().removeRecipe(KEY);
+ ArmorSMP.getInstance().getServer().removeRecipe(key);
+ }
+
+ public ItemStack getItem() {
+ if (!armorUpgrade.getType().equals(Material.NETHER_STAR)) {
+ armorUpgrade = ItemBuilder.create()
+ .material(Material.NETHER_STAR)
+ .name(Text.legacyColor("&b&lArmor Upgrade"))
+ .lore(Text.legacyColor("&9| &3Right click to upgrade your gear"))
+ .count(1)
+ .customModelData(1)
+ .build();
+ return ItemBuilder.create()
+ .material(Material.NETHER_STAR)
+ .name(Text.legacyColor("&b&lArmor Upgrade"))
+ .lore(Text.legacyColor("&9| &3Right click to upgrade your gear"))
+ .count(1)
+ .customModelData(1)
+ .build();
+ }
+ return armorUpgrade;
}
}
diff --git a/src/main/java/me/trouper/armorsmp/server/events/DeathEvents.java b/src/main/java/me/trouper/armorsmp/server/events/DeathEvents.java
index d5b91ec..406cc60 100644
--- a/src/main/java/me/trouper/armorsmp/server/events/DeathEvents.java
+++ b/src/main/java/me/trouper/armorsmp/server/events/DeathEvents.java
@@ -3,15 +3,17 @@ package me.trouper.armorsmp.server.events;
import io.github.itzispyder.pdk.events.CustomListener;
import me.trouper.armorsmp.ArmorSMP;
import me.trouper.armorsmp.data.ArmorTier;
-import me.trouper.armorsmp.server.crafting.ArmorUpgrade;
+import me.trouper.armorsmp.data.Unique;
import me.trouper.armorsmp.utils.Text;
import me.trouper.armorsmp.utils.Verbose;
import me.trouper.armorsmp.utils.ItemUtils;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
+import org.bukkit.inventory.ItemStack;
public class DeathEvents implements CustomListener {
@@ -21,6 +23,9 @@ public class DeathEvents implements CustomListener {
final ArmorTier tier = ArmorSMP.getInstance().getManager().tiers.getTier(p);
Verbose.send("Handling death event for %s, their tier is %s",p.getName(),tier);
e.getDrops().removeIf(ItemUtils::notDroppable);
+ if (ArmorSMP.getInstance().getManager().uniques.isOwner(p,Unique.DRAGON_EGG)) {
+ e.getDrops().add(new ItemStack(Material.DRAGON_EGG));
+ }
if (tier.equals(ArmorTier.NONE)) {
Verbose.send("Tier was none");
@@ -29,7 +34,7 @@ public class DeathEvents implements CustomListener {
}
if (ArmorSMP.getInstance().getManager().tiers.downTier(p)) {
Verbose.send("They have been down-tiered");
- e.getDrops().add(ArmorUpgrade.ARMOR_UGPRADE);
+ e.getDrops().add(ArmorSMP.getInstance().getManager().upgrade.getItem());
e.deathMessage(Text.getMessage(false, Text.Pallet.INFO,"{0} has died, and dropped an {1}!", LegacyComponentSerializer.legacyAmpersand().serialize(p.name()),"Armor Upgrader"));
}
ArmorSMP.getInstance().getManager().uniques.dropAllUniques(p);
@@ -38,5 +43,6 @@ public class DeathEvents implements CustomListener {
@EventHandler
public void onRespawn(PlayerRespawnEvent e) {
ArmorSMP.getInstance().getManager().tiers.queueUpdate(e.getPlayer(),false);
+ ArmorSMP.getInstance().getManager().uniques.queueUpdate(e.getPlayer());
}
}
diff --git a/src/main/java/me/trouper/armorsmp/server/events/UpgradeRedeemEvent.java b/src/main/java/me/trouper/armorsmp/server/events/UpgradeRedeemEvent.java
index 234124e..d2411d5 100644
--- a/src/main/java/me/trouper/armorsmp/server/events/UpgradeRedeemEvent.java
+++ b/src/main/java/me/trouper/armorsmp/server/events/UpgradeRedeemEvent.java
@@ -3,7 +3,7 @@ package me.trouper.armorsmp.server.events;
import io.github.itzispyder.pdk.events.CustomListener;
import me.trouper.armorsmp.ArmorSMP;
import me.trouper.armorsmp.data.ArmorTier;
-import me.trouper.armorsmp.server.crafting.ArmorUpgrade;
+import me.trouper.armorsmp.utils.ItemUtils;
import me.trouper.armorsmp.utils.Text;
import me.trouper.armorsmp.utils.Verbose;
import org.bukkit.entity.Player;
@@ -15,21 +15,24 @@ public class UpgradeRedeemEvent implements CustomListener {
@EventHandler
public void onClick(PlayerInteractEvent e) {
+ Verbose.send("Detected player clicking.");
Player p = e.getPlayer();
- ItemStack holding = e.getItem();
+ ItemStack holding = e.getPlayer().getInventory().getItemInMainHand();
- if (holding == null || holding.isEmpty()) return;
- Verbose.send("Detected Interact Event Holding: %s", holding.getType());
- if (!holding.getType().equals(ArmorUpgrade.ARMOR_UGPRADE.getType())) return;
- if (!holding.hasItemMeta()) return;
- if (holding.getItemMeta().getCustomModelData() != ArmorUpgrade.ARMOR_UGPRADE.getItemMeta().getCustomModelData()) return;
+ if (holding.isEmpty()) return;
+ if (!ItemUtils.isSimilar(holding,ArmorSMP.getInstance().getManager().upgrade.getItem())) {
+ Verbose.send("Player was not holding an Armor Upgrader");
+ return;
+ }
final ArmorTier tier = ArmorSMP.getInstance().getManager().tiers.getTier(p);
if (ArmorSMP.getInstance().getManager().tiers.upTier(p)) {
- holding.setAmount(holding.getAmount() - 1);
+ holding.setAmount(holding.getAmount() - 1);
+ Verbose.send("Successfully updated player");
Text.sendMessage(true, Text.Pallet.INFO,p,"Successfully redeemed armor upgrade! Tier {0} -> Tier {1}",tier,ArmorSMP.getInstance().getManager().tiers.getTier(p));
} else {
+ Verbose.send("Failed to update player (tier manager refused)");
Text.sendMessage(true, Text.Pallet.ERROR,p,"Unable to upgrade. You are already at the maximum Armor Tier!");
}
}
diff --git a/src/main/java/me/trouper/armorsmp/utils/ItemUtils.java b/src/main/java/me/trouper/armorsmp/utils/ItemUtils.java
index be1fb19..65bc8f7 100644
--- a/src/main/java/me/trouper/armorsmp/utils/ItemUtils.java
+++ b/src/main/java/me/trouper/armorsmp/utils/ItemUtils.java
@@ -1,22 +1,37 @@
package me.trouper.armorsmp.utils;
+import com.google.common.collect.Multimap;
import me.trouper.armorsmp.data.Unique;
import org.bukkit.Material;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.attribute.AttributeModifier;
+import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
public class ItemUtils {
public static boolean notDroppable(ItemStack i) {
- return isArmor(i) && !isUnique(i) && !isDragonEggEquivalent(i);
+ boolean armor = isArmor(i);
+ boolean unique = isUnique(i);
+ boolean isEgg = isDragonEggEquivalent(i);
+ Verbose.send("""
+ Item Type: %s
+ Unique: %b
+ Armor: %b
+ Egg Equivalent: %b
+ And: %b
+ """,i.getType().name(),unique,armor,isEgg,(!unique && armor));
+ return (!unique && armor) || isEgg;
}
private static boolean isDragonEggEquivalent(ItemStack i) {
Material m = i.getType();
- return m.equals(Unique.DRAGON_EGG);
+ return m.equals(Unique.DRAGON_EGG.getInGameItem().getType());
}
public static boolean isArmor(ItemStack i) {
@@ -62,16 +77,91 @@ public class ItemUtils {
}
public static boolean isSimilar(ItemStack item1, ItemStack item2) {
- if (item1 == null || item2 == null) return false;
- if (item1.getType() != item2.getType()) return false;
- if (item1.hasItemMeta() != item2.hasItemMeta()) return false;
+ if (item1 == null || item2 == null) {
+ Verbose.send("One of the items is null: item1: %s, item2: %s", item1, item2);
+ return false;
+ }
+ // Check material/type
+ Material type1 = item1.getType();
+ Material type2 = item2.getType();
+ boolean typeEqual = type1 == type2;
+ Verbose.send("Checking Material: item1 type: %s, item2 type: %s, equal: %s", type1, type2, typeEqual);
+ if (!typeEqual) return false;
+
+ // Check existence of ItemMeta
+ boolean hasMeta1 = item1.hasItemMeta();
+ boolean hasMeta2 = item2.hasItemMeta();
+ boolean metaExistEqual = (hasMeta1 == hasMeta2);
+ Verbose.send("Checking ItemMeta existence: item1 has meta: %s, item2 has meta: %s, equal: %s", hasMeta1, hasMeta2, metaExistEqual);
+ if (!metaExistEqual) return false;
+
+ // If neither item has meta, they are similar (nothing else to compare)
+ if (!hasMeta1 && !hasMeta2) {
+ return true;
+ }
+
+ // Both items have meta
ItemMeta meta1 = item1.getItemMeta();
ItemMeta meta2 = item2.getItemMeta();
- return meta1 == null || meta2 == null || meta1.equals(meta2);
+ // Custom Name Check with null handling
+ String name1 = meta1.hasDisplayName() ? meta1.getDisplayName() : null;
+ String name2 = meta2.hasDisplayName() ? meta2.getDisplayName() : null;
+ if (name1 == null ^ name2 == null) { // one is null and the other is not
+ Verbose.send("Custom Name mismatch: item1 name: %s, item2 name: %s", name1, name2);
+ return false;
+ }
+ boolean nameEqual = (name1 == null || name1.equals(name2));
+ Verbose.send("Checking Custom Name: item1: %s, item2: %s, equal: %s", name1, name2, nameEqual);
+ if (!nameEqual) return false;
+
+ // Lore Check with null handling
+ List lore1 = meta1.hasLore() ? meta1.getLore() : null;
+ List lore2 = meta2.hasLore() ? meta2.getLore() : null;
+ if (lore1 == null ^ lore2 == null) {
+ Verbose.send("Lore mismatch: item1 lore: %s, item2 lore: %s", lore1, lore2);
+ return false;
+ }
+ boolean loreEqual = (lore1 == null || lore1.equals(lore2));
+ Verbose.send("Checking Lore: item1: %s, item2: %s, equal: %s", lore1, lore2, loreEqual);
+ if (!loreEqual) return false;
+
+ // Custom Model Data Check: Using -1 as the default if not set
+ int cmd1 = meta1.hasCustomModelData() ? meta1.getCustomModelData() : -1;
+ int cmd2 = meta2.hasCustomModelData() ? meta2.getCustomModelData() : -1;
+ boolean cmdEqual = (cmd1 == cmd2);
+ Verbose.send("Checking Custom Model Data: item1: %d, item2: %d, equal: %s", cmd1, cmd2, cmdEqual);
+ if (!cmdEqual) return false;
+
+ // Enchantments Check with null handling (should not be null by API, but checking for safety)
+ Map enchants1 = meta1.getEnchants();
+ Map enchants2 = meta2.getEnchants();
+ if (enchants1 == null ^ enchants2 == null) {
+ Verbose.send("Enchantments mismatch: item1 enchants: %s, item2 enchants: %s", enchants1, enchants2);
+ return false;
+ }
+ boolean enchantsEqual = (enchants1 == null || enchants1.equals(enchants2));
+ Verbose.send("Checking Enchantments: item1: %s, item2: %s, equal: %s", enchants1, enchants2, enchantsEqual);
+ if (!enchantsEqual) return false;
+
+ // Attribute Modifiers Check with null handling
+ Multimap modifiers1 = meta1.getAttributeModifiers();
+ Multimap modifiers2 = meta2.getAttributeModifiers();
+ if (modifiers1 == null ^ modifiers2 == null) {
+ Verbose.send("Attribute Modifiers mismatch: item1 modifiers: %s, item2 modifiers: %s", modifiers1, modifiers2);
+ return false;
+ }
+ boolean modifiersEqual = (modifiers1 == null || modifiers1.equals(modifiers2));
+ Verbose.send("Checking Attribute Modifiers: item1: %s, item2: %s, equal: %s", modifiers1, modifiers2, modifiersEqual);
+ if (!modifiersEqual) return false;
+
+ // All checks passed
+ return true;
}
+
+
/** @excludes unique enchants */
public static void transferEnchants(ItemStack oldItem, ItemStack newItem) {
if (oldItem != null && !isUnique(oldItem)) {