diff --git a/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerAttributeTracker.java b/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerAttributeTracker.java index 82e0ef562..279cf99c8 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerAttributeTracker.java +++ b/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerAttributeTracker.java @@ -208,10 +208,11 @@ public class PlayerAttributeTracker implements IPlayerAttributes { * Checks if the player's equipment has changed and recalculates attributes if needed. */ public void updateIfChanged(EntityPlayer player) { - PlayerEquipmentTracker currentEquip = new PlayerEquipmentTracker(); - currentEquip.updateFrom(player); - if (!equipmentTracker.equals(player)) { + PlayerEquipmentTracker current = new PlayerEquipmentTracker(); + current.updateFrom(player); + if (!equipmentTracker.equals(current)) { recalcAttributes(player); + equipmentTracker = current; } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerEquipmentTracker.java b/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerEquipmentTracker.java index e21e70d47..961fd1563 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerEquipmentTracker.java +++ b/src/main/java/kamkeel/npcs/controllers/data/attribute/tracker/PlayerEquipmentTracker.java @@ -5,6 +5,10 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import java.util.Objects; + +import static kamkeel.npcs.util.AttributeItemUtil.TAG_RPGCORE; + public class PlayerEquipmentTracker { public ItemStack heldItem; public ItemStack helmet; @@ -25,19 +29,35 @@ public class PlayerEquipmentTracker { helmet = copyIfNotNull(armor[3]); } - /** - * Compares the stored equipment with the player's current equipment. - * Ignores changes like durability by comparing the item and the CNPCAttributes NBT only. - */ - public boolean equals(EntityPlayer player) { - if (player == null) return false; - if (!areItemStacksEquivalent(heldItem, player.getHeldItem())) return false; - ItemStack[] armor = player.inventory.armorInventory; - if (!areItemStacksEquivalent(boots, armor[0])) return false; - if (!areItemStacksEquivalent(leggings, armor[1])) return false; - if (!areItemStacksEquivalent(chestplate, armor[2])) return false; - if (!areItemStacksEquivalent(helmet, armor[3])) return false; - return true; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PlayerEquipmentTracker)) return false; + PlayerEquipmentTracker other = (PlayerEquipmentTracker)o; + return areItemStacksEquivalent(heldItem, other.heldItem) + && areItemStacksEquivalent(boots, other.boots) + && areItemStacksEquivalent(leggings, other.leggings) + && areItemStacksEquivalent(chestplate, other.chestplate) + && areItemStacksEquivalent(helmet, other.helmet); + } + + @Override + public int hashCode() { + return Objects.hash( + itemHash(heldItem), + itemHash(boots), + itemHash(leggings), + itemHash(chestplate), + itemHash(helmet) + ); + } + + private int itemHash(ItemStack s) { + if (s == null) return 0; + NBTTagCompound root = s.stackTagCompound; + if (root == null || !root.hasKey(TAG_RPGCORE)) return 0; + NBTTagCompound tag = root.getCompoundTag(TAG_RPGCORE); + return Objects.hash(s.getItem(), tag); } /** @@ -55,12 +75,16 @@ public class PlayerEquipmentTracker { if (a == b) return true; if (a == null || b == null) return false; if (a.getItem() != b.getItem()) return false; - NBTTagCompound tagA = a.stackTagCompound; - NBTTagCompound tagB = b.stackTagCompound; - String attrA = (tagA != null && tagA.hasKey(AttributeItemUtil.TAG_RPGCORE)) - ? tagA.getCompoundTag(AttributeItemUtil.TAG_RPGCORE).toString() : ""; - String attrB = (tagB != null && tagB.hasKey(AttributeItemUtil.TAG_RPGCORE)) - ? tagB.getCompoundTag(AttributeItemUtil.TAG_RPGCORE).toString() : ""; - return attrA.equals(attrB); + + NBTTagCompound tagA = a.stackTagCompound != null + ? a.stackTagCompound.getCompoundTag(TAG_RPGCORE) + : null; + NBTTagCompound tagB = b.stackTagCompound != null + ? b.stackTagCompound.getCompoundTag(TAG_RPGCORE) + : null; + if (tagA == null ^ tagB == null) return false; + if (tagA != null && !tagA.equals(tagB)) return false; + + return true; } } diff --git a/src/main/java/kamkeel/npcs/util/AttributeAttackUtil.java b/src/main/java/kamkeel/npcs/util/AttributeAttackUtil.java index d7f48788f..918d74157 100644 --- a/src/main/java/kamkeel/npcs/util/AttributeAttackUtil.java +++ b/src/main/java/kamkeel/npcs/util/AttributeAttackUtil.java @@ -36,12 +36,14 @@ public class AttributeAttackUtil { private static AllocationResult allocateMagicDamage(float physicalDamage, Map magicData) { Map allocation = new HashMap<>(); float totalSplit = 0f; - for (Map.Entry entry : magicData.entrySet()) { - int magicId = entry.getKey(); - float splitVal = entry.getValue().split; - float allocated = (physicalDamage * splitVal) + entry.getValue().damage; - allocation.put(magicId, allocated); - totalSplit += splitVal; + if(magicData != null){ + for (Map.Entry entry : magicData.entrySet()) { + int magicId = entry.getKey(); + float splitVal = entry.getValue().split; + float allocated = (physicalDamage * splitVal) + entry.getValue().damage; + allocation.put(magicId, allocated); + totalSplit += splitVal; + } } // Ensure double arithmetic for subtraction, then cast back to float. float leftover = physicalDamage * (float) Math.max(0d, 1d - totalSplit); @@ -59,7 +61,7 @@ public class AttributeAttackUtil { int magicId = entry.getKey(); float damage = entry.getValue(); float boost = magicBoost.getOrDefault(magicId, 0f); - damage *= (1 + (boost / 100)); + damage *= (1 + (boost / 100f)); allocation.put(magicId, allocation.getOrDefault(magicId, 0f) + damage); } } @@ -164,8 +166,8 @@ public class AttributeAttackUtil { if (ConfigMain.AttributesEnabled) { float critChance = tracker.getAttributeValue(CustomAttributes.CRITICAL_CHANCE); float critBonus = tracker.getAttributeValue(CustomAttributes.CRITICAL_DAMAGE); - if (random.nextFloat() < (critChance / 100)) - damage = (damage * (1 + (float) ConfigMain.AttributesCriticalBoost / 100)) + critBonus; + if (random.nextFloat() < (critChance / 100f)) + damage = (damage * (1 + (float) ConfigMain.AttributesCriticalBoost / 100f)) + critBonus; } return damage; } @@ -173,8 +175,8 @@ public class AttributeAttackUtil { public static float applyMainAttack(float damage, PlayerAttributeTracker tracker) { if (ConfigMain.AttributesEnabled) { float mainAttack = tracker.getAttributeValue(CustomAttributes.MAIN_ATTACK); - float mainBoost = tracker.getAttributeValue(CustomAttributes.MAIN_BOOST) + 1; - damage = (damage * mainBoost) + mainAttack; + float mainBoost = tracker.getAttributeValue(CustomAttributes.MAIN_BOOST) / 100f; + damage = (damage * (1 + mainBoost)) + mainAttack; } return damage; } @@ -183,7 +185,7 @@ public class AttributeAttackUtil { if (ConfigMain.AttributesEnabled) { float neutralDamage = tracker.getAttributeValue(CustomAttributes.NEUTRAL_ATTACK); float neutralBoost = tracker.getAttributeValue(CustomAttributes.NEUTRAL_BOOST); - leftover += neutralDamage * (1 + (neutralBoost / 100)); + leftover += neutralDamage * (1 + (neutralBoost / 100f)); } return leftover; } @@ -213,7 +215,7 @@ public class AttributeAttackUtil { */ public static float calculateGearOutput(PlayerAttributeTracker attacker) { float neutralDamage = attacker.getAttributeValue(CustomAttributes.NEUTRAL_ATTACK); - float neutralBoost = attacker.getAttributeValue(CustomAttributes.NEUTRAL_BOOST) + 1; + float neutralBoost = 1 + (attacker.getAttributeValue(CustomAttributes.NEUTRAL_BOOST) / 100f); float neutralTotal = neutralDamage * neutralBoost; float magicTotal = 0f; @@ -223,22 +225,31 @@ public class AttributeAttackUtil { if (magicController.getMagic(magicId) != null) { float attackMagic = entry.getValue(); float boostMagic = attacker.magicBoost.getOrDefault(magicId, 0f); - magicTotal += Math.max(0, attackMagic * (boostMagic + 1)); + magicTotal += Math.max(0, attackMagic * ((boostMagic / 100f) + 1f)); } } return neutralTotal + magicTotal; } /** - * Calculates a player's outgoing damage based on base damage and main attack attributes. + * Calculates a player's outgoing damage based on weapon damage + main-attack attributes, + * plus all neutral/magic damage from gear. */ - public static float calculateOutgoing(EntityPlayer entityPlayer, float baseDamage) { - if (ConfigMain.AttributesEnabled) { - PlayerAttributeTracker attacker = getTracker(entityPlayer); - float mainAttack = attacker.getAttributeValue(CustomAttributes.MAIN_ATTACK); - float mainBoost = attacker.getAttributeValue(CustomAttributes.MAIN_BOOST) + 1; - return attacker.gearOutput + (baseDamage * mainBoost) + mainAttack; + public static float calculateOutgoing(EntityPlayer player, float baseDamage) { + if (!ConfigMain.AttributesEnabled) { + return baseDamage; } - return baseDamage; + + PlayerAttributeTracker tracker = getTracker(player); + + // 1) Main-attack component + float mainAttackFlat = tracker.getAttributeValue(CustomAttributes.MAIN_ATTACK); + float mainBoostPercent = tracker.getAttributeValue(CustomAttributes.MAIN_BOOST) / 100f; + float mainDamage = (baseDamage * (1 + mainBoostPercent)) + mainAttackFlat; + + // 2) Gear component (neutral + magic) – gearOutput must itself divide its boosts by 100 internally + float gearDamage = tracker.gearOutput; + + return mainDamage + gearDamage; } } diff --git a/src/main/java/kamkeel/npcs/util/AttributeItemUtil.java b/src/main/java/kamkeel/npcs/util/AttributeItemUtil.java index 2cfd15089..943821428 100644 --- a/src/main/java/kamkeel/npcs/util/AttributeItemUtil.java +++ b/src/main/java/kamkeel/npcs/util/AttributeItemUtil.java @@ -249,6 +249,9 @@ public class AttributeItemUtil { // For non–magic we include everything. Float value = attrTag.getFloat(key); AttributeDefinition def = AttributeController.getAttribute(key); + if(def == null) + continue; + AttributeDefinition.AttributeSection section = def != null ? def.getSection() : AttributeDefinition.AttributeSection.EXTRA; String plainName = getTranslatedAttributeName(key, def); // unformatted name String formattedLine = formatAttributeLine(def, section, value, plainName); diff --git a/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java b/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java index e4c4ab516..4a7472192 100644 --- a/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java +++ b/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java @@ -570,6 +570,7 @@ public class ScriptPlayerEventHandler { if (event.player.worldObj instanceof WorldServer&& event.player instanceof EntityPlayerMP) { PlayerDataScript handler = ScriptController.Instance.getPlayerScripts(event.player); IPlayer scriptPlayer = (IPlayer) NpcAPI.Instance().getIEntity(event.player); + AttributeController.removeTracker(event.player.getUniqueID()); EventHooks.onPlayerLogout(handler, scriptPlayer); } }