Fixed Armor Upgrader only works after plugin reload

Fixed Chestplate is not automatically turned into dragon egg
Fixed non-armor uniques not removed on death
Fixed Other uniques not automatically redeemed
Fixed Abilities cooldown non-functional
This commit is contained in:
thetrouper
2025-04-02 22:57:51 -05:00
parent 991ce83369
commit 6e86143252
13 changed files with 187 additions and 48 deletions

View File

@@ -11,8 +11,4 @@
</configuration> </configuration>
</facet> </facet>
</component> </component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module> </module>

View File

@@ -44,7 +44,8 @@ public enum Unique {
DRAGON_EGG(ItemBuilder.create() DRAGON_EGG(ItemBuilder.create()
.material(Material.NETHERITE_CHESTPLATE) .material(Material.NETHERITE_CHESTPLATE)
.lore(Text.legacyColor("&bAbilities:")) .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| &7Resistence I"))
.lore(Text.legacyColor("&3| &7Strength I")) .lore(Text.legacyColor("&3| &7Strength I"))
.enchant(Enchantment.BINDING_CURSE,1) .enchant(Enchantment.BINDING_CURSE,1)
@@ -78,7 +79,8 @@ public enum Unique {
LEGGINGS(ItemBuilder.create() LEGGINGS(ItemBuilder.create()
.material(Material.NETHERITE_LEGGINGS) .material(Material.NETHERITE_LEGGINGS)
.lore(Text.legacyColor("&bAbilities:")) .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")) .lore(Text.legacyColor("&3| &7Resistence I"))
.enchant(Enchantment.BINDING_CURSE,1) .enchant(Enchantment.BINDING_CURSE,1)
.enchant(Enchantment.PROTECTION,4) .enchant(Enchantment.PROTECTION,4)
@@ -90,8 +92,6 @@ public enum Unique {
"Netherite Leggings", (p) -> { "Netherite Leggings", (p) -> {
p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE,21,0,true,false,false)); p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE,21,0,true,false,false));
}, (p) -> { }, (p) -> {
// TODO: Test Shield
Stream<Entity> toKnockBack = p.getNearbyEntities(10,10,10).stream().filter(target -> { Stream<Entity> toKnockBack = p.getNearbyEntities(10,10,10).stream().filter(target -> {
if (!(target instanceof Player v)) return false; if (!(target instanceof Player v)) return false;
boolean tooClose = target.getLocation().distance(p.getLocation()) < 10; boolean tooClose = target.getLocation().distance(p.getLocation()) < 10;
@@ -140,6 +140,7 @@ public enum Unique {
.material(Material.NETHERITE_BOOTS) .material(Material.NETHERITE_BOOTS)
.lore(Text.legacyColor("&bAbilities:")) .lore(Text.legacyColor("&bAbilities:"))
.lore(Text.legacyColor("&3| &7Dash")) .lore(Text.legacyColor("&3| &7Dash"))
.lore(Text.legacyColor(" &1- &7Launch yourself forwards"))
.lore(Text.legacyColor("&3| &7Speed 1")) .lore(Text.legacyColor("&3| &7Speed 1"))
.enchant(Enchantment.BINDING_CURSE,1) .enchant(Enchantment.BINDING_CURSE,1)
.enchant(Enchantment.PROTECTION,4) .enchant(Enchantment.PROTECTION,4)
@@ -175,6 +176,9 @@ public enum Unique {
}, 50), }, 50),
SWORD(ItemBuilder.create() SWORD(ItemBuilder.create()
.material(Material.NETHERITE_SWORD) .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.UNBREAKING,3)
.enchant(Enchantment.MENDING,1) .enchant(Enchantment.MENDING,1)
.enchant(Enchantment.SHARPNESS,5) .enchant(Enchantment.SHARPNESS,5)
@@ -182,10 +186,13 @@ public enum Unique {
.build(), .build(),
"Netherite Sword", (p) -> { "Netherite Sword", (p) -> {
}, (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), }, 50),
AXE(ItemBuilder.create() AXE(ItemBuilder.create()
.material(Material.NETHERITE_AXE) .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.UNBREAKING,3)
.enchant(Enchantment.MENDING,1) .enchant(Enchantment.MENDING,1)
.enchant(Enchantment.SHARPNESS,5) .enchant(Enchantment.SHARPNESS,5)
@@ -194,7 +201,7 @@ public enum Unique {
.build(), .build(),
"Netherite Axe", (p) -> { "Netherite Axe", (p) -> {
}, (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); }, 50);
private final ItemStack inGame; private final ItemStack inGame;
@@ -217,10 +224,12 @@ public enum Unique {
public static boolean isUnique(ItemStack i) { public static boolean isUnique(ItemStack i) {
if (i == null) return false; if (i == null) return false;
Verbose.send("Checking if item is unique %s",i.getType());
for (Unique value : values()) { for (Unique value : values()) {
if (ItemUtils.isSimilar(value.getInGameItem(),i)) return true; if (ItemUtils.isSimilar(value.getInGameItem(),i)) return true;
if (i.getType().equals(Material.DRAGON_EGG)) return true; if (i.getType().equals(Material.DRAGON_EGG)) return true;
} }
Verbose.send("Item did not match any unique material...");
return false; return false;
} }

View File

@@ -19,6 +19,7 @@ public class Manager {
public TierBackend tiers; public TierBackend tiers;
public Broadcaster broadcaster; public Broadcaster broadcaster;
public UniquesBackend uniques; public UniquesBackend uniques;
public ArmorUpgrade upgrade;
public Manager() { public Manager() {
io = new IO(); io = new IO();
@@ -33,7 +34,8 @@ public class Manager {
//uniques = new UniquesBackend(); //uniques = new UniquesBackend();
broadcaster = new Broadcaster(); broadcaster = new Broadcaster();
uniques = new UniquesBackend(); uniques = new UniquesBackend();
upgrade = new ArmorUpgrade();
registerCommands(); registerCommands();
registerEvents(); registerEvents();
registerCrafting(); registerCrafting();
@@ -66,9 +68,8 @@ public class Manager {
private void registerCrafting() { private void registerCrafting() {
ArmorSMP.getInstance().getLogger().info("Registering Crafts"); ArmorSMP.getInstance().getLogger().info("Registering Crafts");
ArmorUpgrade armorUpgrade = new ArmorUpgrade(); upgrade.removeRecipe();
armorUpgrade.removeRecipe(); upgrade.addRecipe();
armorUpgrade.addRecipe();
} }
} }

View File

@@ -19,7 +19,7 @@ import java.util.UUID;
@CommandRegistry(value = "ability", printStackTrace = true, playersOnly = true) @CommandRegistry(value = "ability", printStackTrace = true, playersOnly = true)
public class AbilityCommand implements CustomCommand { public class AbilityCommand implements CustomCommand {
private final Cooldown<Pair<Unique, UUID>> abilityCooldown = new Cooldown<>(); private final Cooldown<String> abilityCooldown = new Cooldown<>();
@Override @Override
public void dispatchCommand(CommandSender sender, Command command, String label, Args args) { 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()); Text.sendMessage(false, Text.Pallet.WARNING, sender, "You do not own the {0}.",piece.getCanonical());
return; return;
} }
if (abilityCooldown.isOnCooldown(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(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(getCooldownString(p.getUniqueId(),piece)));
return; return;
} }
piece.getAbility().accept(p); 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()); 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) { public void dispatchCompletions(CommandSender commandSender, Command command, String label, CompletionBuilder b) {
b.then(b.argEnum(Unique.class)); b.then(b.argEnum(Unique.class));
} }
public String getCooldownString(UUID who, Unique what) {
return "%s:%s".formatted(who,what);
}
} }

View File

@@ -9,7 +9,6 @@ import me.trouper.armorsmp.ArmorSMP;
import me.trouper.armorsmp.data.ArmorTier; import me.trouper.armorsmp.data.ArmorTier;
import me.trouper.armorsmp.data.io.Config; import me.trouper.armorsmp.data.io.Config;
import me.trouper.armorsmp.data.Unique; import me.trouper.armorsmp.data.Unique;
import me.trouper.armorsmp.server.crafting.ArmorUpgrade;
import me.trouper.armorsmp.utils.Text; import me.trouper.armorsmp.utils.Text;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
@@ -51,9 +50,15 @@ public class AdminCommand implements CustomCommand {
b.arg("toggle") b.arg("toggle")
) )
.then( .then(
b.arg("include","exclude") b.arg("exclude")
.then( .then(
b.arg("Class.method"))) b.arg("Class.method")))
.then(
b.arg("include")
.then(
b.arg(ArmorSMP.getInstance().getManager().io.config.debuggerExclusions)
)
)
).then( ).then(
b.arg("change") b.arg("change")
.then( .then(
@@ -181,7 +186,7 @@ public class AdminCommand implements CustomCommand {
return; 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()); Text.sendMessage(false, Text.Pallet.SUCCESS, sender, "Given and Upgrader to {0}", target.getName());
} }

View File

@@ -2,23 +2,28 @@ package me.trouper.armorsmp.server.crafting;
import io.github.itzispyder.pdk.plugin.builders.ItemBuilder; import io.github.itzispyder.pdk.plugin.builders.ItemBuilder;
import me.trouper.armorsmp.ArmorSMP; import me.trouper.armorsmp.ArmorSMP;
import me.trouper.armorsmp.utils.Text;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapedRecipe;
public class ArmorUpgrade { public class ArmorUpgrade {
public static final ItemStack ARMOR_UGPRADE = ItemBuilder.create() private ItemStack armorUpgrade;
.material(Material.NETHER_STAR) public final NamespacedKey key;
.name("§b§lArmor Upgrader") public final ShapedRecipe recipe;
.lore("§9| §3Right click to upgrade your gear")
.count(1)
.customModelData(1)
.build();
public static final NamespacedKey KEY = new NamespacedKey(ArmorSMP.getInstance(), "armor_upgrade_recipe"); public ArmorUpgrade() {
this.key = new NamespacedKey(ArmorSMP.getInstance(), "armor_upgrade_recipe");
public static ShapedRecipe recipe = new ShapedRecipe(KEY, ARMOR_UGPRADE); 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() { public void addRecipe() {
recipe.shape("ABC", "DEF", "GHI"); recipe.shape("ABC", "DEF", "GHI");
@@ -39,6 +44,26 @@ public class ArmorUpgrade {
} }
public void removeRecipe() { 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;
} }
} }

View File

@@ -3,15 +3,17 @@ package me.trouper.armorsmp.server.events;
import io.github.itzispyder.pdk.events.CustomListener; import io.github.itzispyder.pdk.events.CustomListener;
import me.trouper.armorsmp.ArmorSMP; import me.trouper.armorsmp.ArmorSMP;
import me.trouper.armorsmp.data.ArmorTier; 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.Text;
import me.trouper.armorsmp.utils.Verbose; import me.trouper.armorsmp.utils.Verbose;
import me.trouper.armorsmp.utils.ItemUtils; import me.trouper.armorsmp.utils.ItemUtils;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.ItemStack;
public class DeathEvents implements CustomListener { public class DeathEvents implements CustomListener {
@@ -21,6 +23,9 @@ public class DeathEvents implements CustomListener {
final ArmorTier tier = ArmorSMP.getInstance().getManager().tiers.getTier(p); final ArmorTier tier = ArmorSMP.getInstance().getManager().tiers.getTier(p);
Verbose.send("Handling death event for %s, their tier is %s",p.getName(),tier); Verbose.send("Handling death event for %s, their tier is %s",p.getName(),tier);
e.getDrops().removeIf(ItemUtils::notDroppable); 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)) { if (tier.equals(ArmorTier.NONE)) {
Verbose.send("Tier was none"); Verbose.send("Tier was none");
@@ -29,7 +34,7 @@ public class DeathEvents implements CustomListener {
} }
if (ArmorSMP.getInstance().getManager().tiers.downTier(p)) { if (ArmorSMP.getInstance().getManager().tiers.downTier(p)) {
Verbose.send("They have been down-tiered"); 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")); 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); ArmorSMP.getInstance().getManager().uniques.dropAllUniques(p);
@@ -38,5 +43,6 @@ public class DeathEvents implements CustomListener {
@EventHandler @EventHandler
public void onRespawn(PlayerRespawnEvent e) { public void onRespawn(PlayerRespawnEvent e) {
ArmorSMP.getInstance().getManager().tiers.queueUpdate(e.getPlayer(),false); ArmorSMP.getInstance().getManager().tiers.queueUpdate(e.getPlayer(),false);
ArmorSMP.getInstance().getManager().uniques.queueUpdate(e.getPlayer());
} }
} }

View File

@@ -3,7 +3,7 @@ package me.trouper.armorsmp.server.events;
import io.github.itzispyder.pdk.events.CustomListener; import io.github.itzispyder.pdk.events.CustomListener;
import me.trouper.armorsmp.ArmorSMP; import me.trouper.armorsmp.ArmorSMP;
import me.trouper.armorsmp.data.ArmorTier; 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.Text;
import me.trouper.armorsmp.utils.Verbose; import me.trouper.armorsmp.utils.Verbose;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -15,21 +15,24 @@ public class UpgradeRedeemEvent implements CustomListener {
@EventHandler @EventHandler
public void onClick(PlayerInteractEvent e) { public void onClick(PlayerInteractEvent e) {
Verbose.send("Detected player clicking.");
Player p = e.getPlayer(); Player p = e.getPlayer();
ItemStack holding = e.getItem(); ItemStack holding = e.getPlayer().getInventory().getItemInMainHand();
if (holding == null || holding.isEmpty()) return; if (holding.isEmpty()) return;
Verbose.send("Detected Interact Event Holding: %s", holding.getType()); if (!ItemUtils.isSimilar(holding,ArmorSMP.getInstance().getManager().upgrade.getItem())) {
if (!holding.getType().equals(ArmorUpgrade.ARMOR_UGPRADE.getType())) return; Verbose.send("Player was not holding an Armor Upgrader");
if (!holding.hasItemMeta()) return; return;
if (holding.getItemMeta().getCustomModelData() != ArmorUpgrade.ARMOR_UGPRADE.getItemMeta().getCustomModelData()) return; }
final ArmorTier tier = ArmorSMP.getInstance().getManager().tiers.getTier(p); final ArmorTier tier = ArmorSMP.getInstance().getManager().tiers.getTier(p);
if (ArmorSMP.getInstance().getManager().tiers.upTier(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)); Text.sendMessage(true, Text.Pallet.INFO,p,"Successfully redeemed armor upgrade! Tier {0} -> Tier {1}",tier,ArmorSMP.getInstance().getManager().tiers.getTier(p));
} else { } 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!"); Text.sendMessage(true, Text.Pallet.ERROR,p,"Unable to upgrade. You are already at the maximum Armor Tier!");
} }
} }

View File

@@ -1,22 +1,37 @@
package me.trouper.armorsmp.utils; package me.trouper.armorsmp.utils;
import com.google.common.collect.Multimap;
import me.trouper.armorsmp.data.Unique; import me.trouper.armorsmp.data.Unique;
import org.bukkit.Material; 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.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
public class ItemUtils { public class ItemUtils {
public static boolean notDroppable(ItemStack i) { 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) { private static boolean isDragonEggEquivalent(ItemStack i) {
Material m = i.getType(); Material m = i.getType();
return m.equals(Unique.DRAGON_EGG); return m.equals(Unique.DRAGON_EGG.getInGameItem().getType());
} }
public static boolean isArmor(ItemStack i) { public static boolean isArmor(ItemStack i) {
@@ -62,16 +77,91 @@ public class ItemUtils {
} }
public static boolean isSimilar(ItemStack item1, ItemStack item2) { public static boolean isSimilar(ItemStack item1, ItemStack item2) {
if (item1 == null || item2 == null) return false; if (item1 == null || item2 == null) {
if (item1.getType() != item2.getType()) return false; Verbose.send("One of the items is null: item1: %s, item2: %s", item1, item2);
if (item1.hasItemMeta() != item2.hasItemMeta()) return false; 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 meta1 = item1.getItemMeta();
ItemMeta meta2 = item2.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<String> lore1 = meta1.hasLore() ? meta1.getLore() : null;
List<String> 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<Enchantment, Integer> enchants1 = meta1.getEnchants();
Map<Enchantment, Integer> 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<Attribute, AttributeModifier> modifiers1 = meta1.getAttributeModifiers();
Multimap<Attribute, AttributeModifier> 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 */ /** @excludes unique enchants */
public static void transferEnchants(ItemStack oldItem, ItemStack newItem) { public static void transferEnchants(ItemStack oldItem, ItemStack newItem) {
if (oldItem != null && !isUnique(oldItem)) { if (oldItem != null && !isUnique(oldItem)) {