diff --git a/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java b/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java index 2247d4448..af06b8837 100644 --- a/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java +++ b/src/main/java/noppes/npcs/ScriptPlayerEventHandler.java @@ -23,7 +23,7 @@ import net.minecraftforge.event.entity.player.*; import net.minecraftforge.event.world.BlockEvent; import noppes.npcs.api.entity.IPlayer; import noppes.npcs.api.item.IItemCustomizable; -import noppes.npcs.attribute.PlayerAttributeManager; +import noppes.npcs.attribute.AttributeController; import noppes.npcs.constants.EnumQuestType; import noppes.npcs.controllers.CustomEffectController; import noppes.npcs.controllers.PartyController; @@ -61,7 +61,7 @@ public class ScriptPlayerEventHandler { } } - PlayerAttributeManager.getTracker(player.getUniqueID()).updateIfChanged(player); + AttributeController.getTracker(player.getUniqueID()).updateIfChanged(player); } if (PlayerDataController.Instance != null) { diff --git a/src/main/java/noppes/npcs/attribute/AttributeController.java b/src/main/java/noppes/npcs/attribute/AttributeController.java index 1cb762a6f..fbec3418b 100644 --- a/src/main/java/noppes/npcs/attribute/AttributeController.java +++ b/src/main/java/noppes/npcs/attribute/AttributeController.java @@ -1,8 +1,12 @@ package noppes.npcs.attribute; +import net.minecraft.entity.player.EntityPlayer; +import noppes.npcs.attribute.player.PlayerAttributeTracker; + import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.UUID; /** * AttributeController is the central registry for attribute definitions. @@ -13,11 +17,14 @@ public class AttributeController { public AttributeController Instance; public ModAttributes attributes; + // Tracking Player Attributes + private static final Map trackers = new HashMap<>(); private static final Map definitions = new HashMap<>(); public AttributeController(){ this.Instance = this; definitions.clear(); + trackers.clear(); attributes = new ModAttributes(); } @@ -38,4 +45,22 @@ public class AttributeController { public static Collection getAllAttributes() { return definitions.values(); } + + public static PlayerAttributeTracker getTracker(UUID playerId) { + return trackers.computeIfAbsent(playerId, id -> new PlayerAttributeTracker(id)); + } + + public static void removeTracker(UUID playerId) { + trackers.remove(playerId); + } + + /** + * Update all trackers (e.g. called every 10 ticks for all online players). + */ + public static void updateAllTrackers(Iterable players) { + for (EntityPlayer player : players) { + PlayerAttributeTracker tracker = getTracker(player.getUniqueID()); + tracker.updateIfChanged(player); + } + } } diff --git a/src/main/java/noppes/npcs/attribute/AttributeKeys.java b/src/main/java/noppes/npcs/attribute/AttributeKeys.java index 255b64e01..a59e356bb 100644 --- a/src/main/java/noppes/npcs/attribute/AttributeKeys.java +++ b/src/main/java/noppes/npcs/attribute/AttributeKeys.java @@ -1,17 +1,19 @@ package noppes.npcs.attribute; public final class AttributeKeys { - public static final String MAIN_ATTACK_FLAT = "main_attack_flat"; - public static final String MAIN_ATTACK_PERCENT = "main_attack_percent"; public static final String HEALTH = "health"; - public static final String CRITICAL_CHANCE_PERCENT = "critical_chance_percent"; - public static final String CRITICAL_DAMAGE_FLAT = "critical_damage_flat"; + + public static final String MAIN_ATTACK = "main_attack"; + public static final String MAIN_ATTACK_BOOST = "main_attack_boost"; + + public static final String CRITICAL_DAMAGE_FLAT = "critical_damage"; + public static final String CRITICAL_CHANCE = "critical_chance"; + // Magic–based attribute keys: - public static final String MAGIC_DAMAGE_FLAT = "magic_damage_flat"; - public static final String MAGIC_DAMAGE_PERCENT = "magic_damage_percent"; - public static final String MAGIC_DEFENSE_FLAT = "magic_defense_flat"; - public static final String MAGIC_RESISTANCE_PERCENT = "magic_resistance_percent"; + public static final String MAGIC_DAMAGE = "magic_damage"; + public static final String MAGIC_BOOST = "magic_boost"; - private AttributeKeys() { } + public static final String MAGIC_DEFENSE = "magic_defense"; + public static final String MAGIC_RESISTANCE = "magic_resistance"; } diff --git a/src/main/java/noppes/npcs/attribute/AttributeModifier.java b/src/main/java/noppes/npcs/attribute/AttributeModifier.java deleted file mode 100644 index ccaed7c15..000000000 --- a/src/main/java/noppes/npcs/attribute/AttributeModifier.java +++ /dev/null @@ -1,37 +0,0 @@ -package noppes.npcs.attribute; - -import java.util.UUID; - -/** - * An AttributeModifier represents a bonus (or penalty) that is applied to an attribute. - * It carries a UUID, a human–readable name, an amount, and a type (FLAT, PERCENT, etc). - */ -public class AttributeModifier { - private final UUID id; - private final String name; - private final double amount; - private final AttributeValueType valueType; - - public AttributeModifier(UUID id, String name, double amount, AttributeValueType valueType) { - this.id = id; - this.name = name; - this.amount = amount; - this.valueType = valueType; - } - - public UUID getId() { - return id; - } - - public String getName() { - return name; - } - - public double getAmount() { - return amount; - } - - public AttributeValueType getValueType() { - return valueType; - } -} diff --git a/src/main/java/noppes/npcs/attribute/DamageCalculator.java b/src/main/java/noppes/npcs/attribute/DamageCalculator.java index d776b1929..ff8795d6a 100644 --- a/src/main/java/noppes/npcs/attribute/DamageCalculator.java +++ b/src/main/java/noppes/npcs/attribute/DamageCalculator.java @@ -1,39 +1,15 @@ package noppes.npcs.attribute; +import noppes.npcs.attribute.player.PlayerAttributeTracker; + import java.util.Random; -/** - * Calculates damage using cached (aggregated) offensive and defensive ratings from players. - * The formula here is simple: baseDamage = attacker.getOffense() - defender.getDefense(), - * then a critical chance is applied (if a random roll is below the attacker's crit chance, - * damage is doubled and a flat crit bonus is added). - */ public class DamageCalculator { private static final Random random = new Random(); - /** - * Calculates damage given attacker and defender attribute trackers. - */ - public static double calculateDamage(PlayerAttributeTracker attacker, PlayerAttributeTracker defender) { - double offense = attacker.getOffense(); - double defense = defender.getDefense(); - double damage = offense - defense; - // Process critical hit chance from attacker's aggregated non-magic attributes. - double critChance = attacker.getAttributeValue(ModAttributes.CRITICAL_CHANCE_PERCENT); - double critBonus = attacker.getAttributeValue(ModAttributes.CRITICAL_DAMAGE_FLAT); - if (random.nextDouble() < critChance) { - damage = damage * 2 + critBonus; - } - return Math.max(damage, 0); + public static double calculateDamage(PlayerAttributeTracker attacker, PlayerAttributeTracker defender, int baseDamage) { + return 0; } - /** - * Optionally, calculates damage for a specific magic type. - */ - public static double calculateMagicDamage(PlayerAttributeTracker attacker, PlayerAttributeTracker defender, int magicId) { - double offense = attacker.getOffense(magicId); - double defense = defender.getDefense(magicId); - return Math.max(offense - defense, 0); - } } diff --git a/src/main/java/noppes/npcs/attribute/IAttributeInstance.java b/src/main/java/noppes/npcs/attribute/IAttributeInstance.java index 5aadb8efd..ed7d5669e 100644 --- a/src/main/java/noppes/npcs/attribute/IAttributeInstance.java +++ b/src/main/java/noppes/npcs/attribute/IAttributeInstance.java @@ -1,16 +1,10 @@ package noppes.npcs.attribute; -import java.util.Collection; - /** * IAttributeInstance represents an instance of an attribute (with a base value and applied modifiers). */ public interface IAttributeInstance { AttributeDefinition getAttribute(); - double getBaseValue(); - void setBaseValue(double value); - void applyModifier(AttributeModifier modifier); - void removeModifier(AttributeModifier modifier); - Collection getModifiers(); - double getAttributeValue(); // Final computed value after modifiers + double getValue(); + void setValue(double value); } diff --git a/src/main/java/noppes/npcs/attribute/ModAttributes.java b/src/main/java/noppes/npcs/attribute/ModAttributes.java index 1c1d6ae62..ec4b761a4 100644 --- a/src/main/java/noppes/npcs/attribute/ModAttributes.java +++ b/src/main/java/noppes/npcs/attribute/ModAttributes.java @@ -16,15 +16,15 @@ public class ModAttributes { public static AttributeDefinition MAGIC_RESISTANCE_PERCENT; public ModAttributes(){ - MAIN_ATTACK_FLAT = AttributeController.registerAttribute(AttributeKeys.MAIN_ATTACK_FLAT, "Main Attack Damage", AttributeValueType.FLAT); - MAIN_ATTACK_PERCENT = AttributeController.registerAttribute(AttributeKeys.MAIN_ATTACK_PERCENT, "Main Attack Damage (%)", AttributeValueType.PERCENT); + MAIN_ATTACK_FLAT = AttributeController.registerAttribute(AttributeKeys.MAIN_ATTACK, "Main Attack Damage", AttributeValueType.FLAT); + MAIN_ATTACK_PERCENT = AttributeController.registerAttribute(AttributeKeys.MAIN_ATTACK_BOOST, "Main Attack Damage (%)", AttributeValueType.PERCENT); HEALTH = AttributeController.registerAttribute(AttributeKeys.HEALTH, "Health", AttributeValueType.FLAT); - CRITICAL_CHANCE_PERCENT = AttributeController.registerAttribute(AttributeKeys.CRITICAL_CHANCE_PERCENT, "Critical Chance (%)", AttributeValueType.PERCENT); + CRITICAL_CHANCE_PERCENT = AttributeController.registerAttribute(AttributeKeys.CRITICAL_CHANCE, "Critical Chance (%)", AttributeValueType.PERCENT); CRITICAL_DAMAGE_FLAT = AttributeController.registerAttribute(AttributeKeys.CRITICAL_DAMAGE_FLAT, "Critical Damage Bonus", AttributeValueType.FLAT); - MAGIC_DAMAGE_FLAT = AttributeController.registerAttribute(AttributeKeys.MAGIC_DAMAGE_FLAT, "Magic Damage", AttributeValueType.MAGIC); - MAGIC_DAMAGE_PERCENT = AttributeController.registerAttribute(AttributeKeys.MAGIC_DAMAGE_PERCENT, "Magic Damage (%)", AttributeValueType.MAGIC); - MAGIC_DEFENSE_FLAT = AttributeController.registerAttribute(AttributeKeys.MAGIC_DEFENSE_FLAT, "Magic Defense", AttributeValueType.MAGIC); - MAGIC_RESISTANCE_PERCENT = AttributeController.registerAttribute(AttributeKeys.MAGIC_RESISTANCE_PERCENT, "Magic Resistance (%)", AttributeValueType.MAGIC); + MAGIC_DAMAGE_FLAT = AttributeController.registerAttribute(AttributeKeys.MAGIC_DAMAGE, "Magic Damage", AttributeValueType.MAGIC); + MAGIC_DAMAGE_PERCENT = AttributeController.registerAttribute(AttributeKeys.MAGIC_BOOST, "Magic Damage (%)", AttributeValueType.MAGIC); + MAGIC_DEFENSE_FLAT = AttributeController.registerAttribute(AttributeKeys.MAGIC_DEFENSE, "Magic Defense", AttributeValueType.MAGIC); + MAGIC_RESISTANCE_PERCENT = AttributeController.registerAttribute(AttributeKeys.MAGIC_RESISTANCE, "Magic Resistance (%)", AttributeValueType.MAGIC); } } diff --git a/src/main/java/noppes/npcs/attribute/ModifiableAttributeInstance.java b/src/main/java/noppes/npcs/attribute/ModifiableAttributeInstance.java deleted file mode 100644 index 5645165c3..000000000 --- a/src/main/java/noppes/npcs/attribute/ModifiableAttributeInstance.java +++ /dev/null @@ -1,66 +0,0 @@ -package noppes.npcs.attribute; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -public class ModifiableAttributeInstance implements IAttributeInstance { - private final AttributeDefinition attribute; - private double baseValue; - private final Set modifiers = new HashSet<>(); - - public ModifiableAttributeInstance(AttributeDefinition attribute, double baseValue) { - this.attribute = attribute; - this.baseValue = baseValue; - } - - @Override - public AttributeDefinition getAttribute() { - return attribute; - } - - @Override - public double getBaseValue() { - return baseValue; - } - - @Override - public void setBaseValue(double value) { - this.baseValue = value; - } - - @Override - public void applyModifier(AttributeModifier modifier) { - modifiers.add(modifier); - } - - @Override - public void removeModifier(AttributeModifier modifier) { - modifiers.remove(modifier); - } - - @Override - public Collection getModifiers() { - return modifiers; - } - - @Override - public double getAttributeValue() { - double finalValue = baseValue; - // Apply FLAT modifiers - for (AttributeModifier mod : modifiers) { - if (mod.getValueType() == AttributeValueType.FLAT) { - finalValue += mod.getAmount(); - } - } - // Apply PERCENT modifiers additively. - double percentSum = 0.0; - for (AttributeModifier mod : modifiers) { - if (mod.getValueType() == AttributeValueType.PERCENT) { - percentSum += mod.getAmount(); - } - } - finalValue *= (1 + percentSum); - return finalValue; - } -} diff --git a/src/main/java/noppes/npcs/attribute/PlayerAttributeManager.java b/src/main/java/noppes/npcs/attribute/PlayerAttributeManager.java deleted file mode 100644 index 9b6a3dbef..000000000 --- a/src/main/java/noppes/npcs/attribute/PlayerAttributeManager.java +++ /dev/null @@ -1,31 +0,0 @@ -package noppes.npcs.attribute; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import net.minecraft.entity.player.EntityPlayer; - -/** - * PlayerAttributeManager maps player UUIDs to their attribute trackers. - */ -public class PlayerAttributeManager { - private static final Map trackers = new HashMap<>(); - - public static PlayerAttributeTracker getTracker(UUID playerId) { - return trackers.computeIfAbsent(playerId, id -> new PlayerAttributeTracker(id)); - } - - public static void removeTracker(UUID playerId) { - trackers.remove(playerId); - } - - /** - * Update all trackers (e.g. called every 10 ticks for all online players). - */ - public static void updateAllTrackers(Iterable players) { - for (EntityPlayer player : players) { - PlayerAttributeTracker tracker = getTracker(player.getUniqueID()); - tracker.updateIfChanged(player); - } - } -} diff --git a/src/main/java/noppes/npcs/attribute/PlayerAttributeTracker.java b/src/main/java/noppes/npcs/attribute/PlayerAttributeTracker.java deleted file mode 100644 index a406e0877..000000000 --- a/src/main/java/noppes/npcs/attribute/PlayerAttributeTracker.java +++ /dev/null @@ -1,202 +0,0 @@ -package noppes.npcs.attribute; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import java.util.Map.Entry; - -/** - * Aggregates a player's attributes based on the attributes found on their held item and armor. - * Non–magic attributes are stored in a CustomAttributeMap (using string keys via AttributeController). - * Magic attributes (offense and defense) are aggregated as maps, keyed by magic type. - * This tracker uses a PlayerEquipmentTracker to keep track of the player's equipment, - * and recalculates only when the player's held item or armor changes. - */ -public class PlayerAttributeTracker { - private final UUID playerId; - private CustomAttributeMap aggregatedAttributes = new CustomAttributeMap(); - - // Aggregated magic offensive values: - private final Map aggregatedMagicDamageFlat = new HashMap<>(); - private final Map aggregatedMagicDamagePercent = new HashMap<>(); - // Aggregated magic defensive values: - private final Map aggregatedMagicDefenseFlat = new HashMap<>(); - private final Map aggregatedMagicResistancePercent = new HashMap<>(); - - // Stores the last-known equipment. - private PlayerEquipmentTracker equipmentTracker = new PlayerEquipmentTracker(); - - public PlayerAttributeTracker(UUID playerId) { - this.playerId = playerId; - // Initialize equipmentTracker from a null player (will be updated on first tick). - recalcAttributes(null); - } - - public CustomAttributeMap getAggregatedAttributes() { - return aggregatedAttributes; - } - - /** - * Recalculates the aggregated attributes from the player's equipment. - * Non–magic attributes are read from each item's CNPCAttributes, and magic attributes - * are aggregated by summing values from their NBT maps. - */ - public void recalcAttributes(EntityPlayer player) { - aggregatedAttributes = new CustomAttributeMap(); - - // Reset magic aggregates. - aggregatedMagicDamageFlat.clear(); - aggregatedMagicDamagePercent.clear(); - aggregatedMagicDefenseFlat.clear(); - aggregatedMagicResistancePercent.clear(); - - if (player != null) { - // Update equipmentTracker with the player's current equipment. - PlayerEquipmentTracker currentEquip = new PlayerEquipmentTracker(); - currentEquip.updateFrom(player); - - // Process held item (offense). - ItemStack held = currentEquip.heldItem; - if (held != null) { - // Process non-magic attributes. - for (Entry entry : ItemAttributeHelper.readAttributes(held).entrySet()) { - String key = entry.getKey(); - double value = entry.getValue(); - AttributeDefinition def = AttributeController.getAttribute(key); - if (def != null) { - IAttributeInstance inst = aggregatedAttributes.getAttributeInstance(def); - if (inst == null) { - inst = aggregatedAttributes.registerAttribute(def, 0.0); - } - inst.setBaseValue(inst.getBaseValue() + value); - } - } - // Process magic offense. - Map magicFlat = ItemAttributeHelper.readMagicAttributeMap(held, AttributeKeys.MAGIC_DAMAGE_FLAT); - for (Entry entry : magicFlat.entrySet()) { - int magicId = entry.getKey(); - double value = entry.getValue(); - aggregatedMagicDamageFlat.put(magicId, aggregatedMagicDamageFlat.getOrDefault(magicId, 0.0) + value); - } - Map magicPercent = ItemAttributeHelper.readMagicAttributeMap(held, AttributeKeys.MAGIC_DAMAGE_PERCENT); - for (Entry entry : magicPercent.entrySet()) { - int magicId = entry.getKey(); - double value = entry.getValue(); - aggregatedMagicDamagePercent.put(magicId, aggregatedMagicDamagePercent.getOrDefault(magicId, 0.0) + value); - } - } - // Process armor (defense). - // Assuming armor order: 0: boots, 1: leggings, 2: chestplate, 3: helmet. - ItemStack[] armor = new ItemStack[] { currentEquip.boots, currentEquip.leggings, currentEquip.chestplate, currentEquip.helmet }; - for (ItemStack piece : armor) { - if (piece != null) { - // Process non-magic attributes. - for (Entry entry : ItemAttributeHelper.readAttributes(piece).entrySet()) { - String key = entry.getKey(); - double value = entry.getValue(); - AttributeDefinition def = AttributeController.getAttribute(key); - if (def != null) { - IAttributeInstance inst = aggregatedAttributes.getAttributeInstance(def); - if (inst == null) { - inst = aggregatedAttributes.registerAttribute(def, 0.0); - } - inst.setBaseValue(inst.getBaseValue() + value); - } - } - // Process magic defense. - Map magicDefFlat = ItemAttributeHelper.readMagicAttributeMap(piece, AttributeKeys.MAGIC_DEFENSE_FLAT); - for (Entry entry : magicDefFlat.entrySet()) { - int magicId = entry.getKey(); - double value = entry.getValue(); - aggregatedMagicDefenseFlat.put(magicId, aggregatedMagicDefenseFlat.getOrDefault(magicId, 0.0) + value); - } - Map magicResist = ItemAttributeHelper.readMagicAttributeMap(piece, AttributeKeys.MAGIC_RESISTANCE_PERCENT); - for (Entry entry : magicResist.entrySet()) { - int magicId = entry.getKey(); - double value = entry.getValue(); - aggregatedMagicResistancePercent.put(magicId, aggregatedMagicResistancePercent.getOrDefault(magicId, 0.0) + value); - } - } - } - // Update our stored equipment. - equipmentTracker = currentEquip; - } - } - - /** - * Checks if the player's equipment has changed by comparing stored equipment with the current equipment. - * This should be called periodically (e.g., every 10 ticks). - */ - public void updateIfChanged(EntityPlayer player) { - PlayerEquipmentTracker currentEquip = new PlayerEquipmentTracker(); - currentEquip.updateFrom(player); - if (!equipmentTracker.equals(player)) { - recalcAttributes(player); - } - } - - /** - * Returns the aggregated non-magic value for a given attribute definition. - */ - public double getAttributeValue(AttributeDefinition def) { - IAttributeInstance inst = aggregatedAttributes.getAttributeInstance(def); - return inst != null ? inst.getAttributeValue() : 0.0; - } - - /** - * Overall offense is calculated as: - * Non-magic offense (MAIN_ATTACK_FLAT*(1+MAIN_ATTACK_PERCENT)) - * plus the sum over all magic offense: for each magic type, flat*(1+percent). - */ - public double getOffense() { - double nonMagic = 0.0; - AttributeDefinition atkFlat = AttributeController.getAttribute(AttributeKeys.MAIN_ATTACK_FLAT); - AttributeDefinition atkPercent = AttributeController.getAttribute(AttributeKeys.MAIN_ATTACK_PERCENT); - if (atkFlat != null && atkPercent != null) { - nonMagic = getAttributeValue(atkFlat) * (1 + getAttributeValue(atkPercent)); - } - double magicOffense = 0.0; - for (Entry entry : aggregatedMagicDamageFlat.entrySet()) { - int magicId = entry.getKey(); - double flat = entry.getValue(); - double percent = aggregatedMagicDamagePercent.getOrDefault(magicId, 0.0); - magicOffense += flat * (1 + percent); - } - return nonMagic + magicOffense; - } - - /** - * Returns offense for a specific magic type. - */ - public double getOffense(int magicId) { - double flat = aggregatedMagicDamageFlat.getOrDefault(magicId, 0.0); - double percent = aggregatedMagicDamagePercent.getOrDefault(magicId, 0.0); - return flat * (1 + percent); - } - - /** - * Overall defense is calculated solely from magic defensive attributes. - * (HEALTH is managed separately.) - */ - public double getDefense() { - double magicDefense = 0.0; - for (Entry entry : aggregatedMagicDefenseFlat.entrySet()) { - int magicId = entry.getKey(); - double flat = entry.getValue(); - double percent = aggregatedMagicResistancePercent.getOrDefault(magicId, 0.0); - magicDefense += flat * (1 + percent); - } - return magicDefense; - } - - /** - * Returns defense for a specific magic type. - */ - public double getDefense(int magicId) { - double flat = aggregatedMagicDefenseFlat.getOrDefault(magicId, 0.0); - double percent = aggregatedMagicResistancePercent.getOrDefault(magicId, 0.0); - return flat * (1 + percent); - } -} diff --git a/src/main/java/noppes/npcs/attribute/player/PlayerAttribute.java b/src/main/java/noppes/npcs/attribute/player/PlayerAttribute.java new file mode 100644 index 000000000..97d1d7233 --- /dev/null +++ b/src/main/java/noppes/npcs/attribute/player/PlayerAttribute.java @@ -0,0 +1,30 @@ +package noppes.npcs.attribute.player; + + +import noppes.npcs.attribute.AttributeDefinition; +import noppes.npcs.attribute.IAttributeInstance; + +public class PlayerAttribute implements IAttributeInstance { + private final AttributeDefinition attribute; + private double baseValue; + + public PlayerAttribute(AttributeDefinition attribute, double baseValue) { + this.attribute = attribute; + this.baseValue = baseValue; + } + + @Override + public AttributeDefinition getAttribute() { + return attribute; + } + + @Override + public double getValue() { + return baseValue; + } + + @Override + public void setValue(double value) { + this.baseValue = value; + } +} diff --git a/src/main/java/noppes/npcs/attribute/CustomAttributeMap.java b/src/main/java/noppes/npcs/attribute/player/PlayerAttributeMap.java similarity index 76% rename from src/main/java/noppes/npcs/attribute/CustomAttributeMap.java rename to src/main/java/noppes/npcs/attribute/player/PlayerAttributeMap.java index be4edecff..91f11aa43 100644 --- a/src/main/java/noppes/npcs/attribute/CustomAttributeMap.java +++ b/src/main/java/noppes/npcs/attribute/player/PlayerAttributeMap.java @@ -1,4 +1,7 @@ -package noppes.npcs.attribute; +package noppes.npcs.attribute.player; + +import noppes.npcs.attribute.AttributeDefinition; +import noppes.npcs.attribute.IAttributeInstance; import java.util.Collection; import java.util.HashMap; @@ -7,14 +10,14 @@ import java.util.Map; /** * CustomAttributeMap holds a set of attribute instances for an entity (or item). */ -public class CustomAttributeMap { +public class PlayerAttributeMap { private final Map map = new HashMap<>(); public IAttributeInstance registerAttribute(AttributeDefinition attribute, double baseValue) { if (map.containsKey(attribute)) { throw new IllegalArgumentException("Attribute already registered: " + attribute.getKey()); } - IAttributeInstance instance = new ModifiableAttributeInstance(attribute, baseValue); + IAttributeInstance instance = new PlayerAttribute(attribute, baseValue); map.put(attribute, instance); return instance; } diff --git a/src/main/java/noppes/npcs/attribute/player/PlayerAttributeTracker.java b/src/main/java/noppes/npcs/attribute/player/PlayerAttributeTracker.java new file mode 100644 index 000000000..819a1d2d5 --- /dev/null +++ b/src/main/java/noppes/npcs/attribute/player/PlayerAttributeTracker.java @@ -0,0 +1,175 @@ +package noppes.npcs.attribute.player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.Map.Entry; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import noppes.npcs.attribute.*; + +public class PlayerAttributeTracker { + + public static final int BASE_ID = -100; + + private final UUID playerId; + private PlayerAttributeMap playerAttributes = new PlayerAttributeMap(); + + private final Map damageMap = new HashMap<>(); + private final Map defenseMap = new HashMap<>(); + + private final Map magicDamage = new HashMap<>(); + private final Map magicBoost = new HashMap<>(); + private final Map magicDefense = new HashMap<>(); + private final Map magicResistance = new HashMap<>(); + + private PlayerEquipmentTracker equipmentTracker = new PlayerEquipmentTracker(); + + public PlayerAttributeTracker(UUID playerId) { + this.playerId = playerId; + // Initial recalc with base damage 0. + recalcAttributes(null, 0); + } + + public PlayerAttributeMap getPlayerAttributes() { + return playerAttributes; + } + + public Map getOffenseMap() { + return damageMap; + } + + public Map getDefenseMap() { + return defenseMap; + } + + public void recalcAttributes(EntityPlayer player, int baseDamage) { + // Reset aggregated attributes and magic maps. + playerAttributes = new PlayerAttributeMap(); + magicDamage.clear(); + magicBoost.clear(); + magicDefense.clear(); + magicResistance.clear(); + + if (player != null) { + // Update equipment tracker. + PlayerEquipmentTracker currentEquip = new PlayerEquipmentTracker(); + currentEquip.updateFrom(player); + + // Process held item (offense). + ItemStack held = currentEquip.heldItem; + if (held != null) { + // Process non-magic attributes. + for (Entry entry : ItemAttributeHelper.readAttributes(held).entrySet()) { + String key = entry.getKey(); + double value = entry.getValue(); + AttributeDefinition def = AttributeController.getAttribute(key); + if (def != null) { + IAttributeInstance inst = playerAttributes.getAttributeInstance(def); + if (inst == null) { + inst = playerAttributes.registerAttribute(def, 0.0); + } + inst.setValue(inst.getValue() + value); + } + } + // Process magic offense. + Map magicFlat = ItemAttributeHelper.readMagicAttributeMap(held, AttributeKeys.MAGIC_DAMAGE); + for (Entry entry : magicFlat.entrySet()) { + int magicId = entry.getKey(); + double value = entry.getValue(); + magicDamage.put(magicId, magicDamage.getOrDefault(magicId, 0.0) + value); + } + Map magicPercent = ItemAttributeHelper.readMagicAttributeMap(held, AttributeKeys.MAGIC_BOOST); + for (Entry entry : magicPercent.entrySet()) { + int magicId = entry.getKey(); + double value = entry.getValue(); + magicBoost.put(magicId, magicBoost.getOrDefault(magicId, 0.0) + value); + } + } + // Process armor (defense). + ItemStack[] armor = new ItemStack[] { currentEquip.boots, currentEquip.leggings, currentEquip.chestplate, currentEquip.helmet }; + for (ItemStack piece : armor) { + if (piece != null) { + // Process non-magic attributes. + for (Entry entry : ItemAttributeHelper.readAttributes(piece).entrySet()) { + String key = entry.getKey(); + double value = entry.getValue(); + AttributeDefinition def = AttributeController.getAttribute(key); + if (def != null) { + IAttributeInstance inst = playerAttributes.getAttributeInstance(def); + if (inst == null) { + inst = playerAttributes.registerAttribute(def, 0.0); + } + inst.setValue(inst.getValue() + value); + } + } + // Process magic defense. + Map magicDefFlat = ItemAttributeHelper.readMagicAttributeMap(piece, AttributeKeys.MAGIC_DEFENSE); + for (Entry entry : magicDefFlat.entrySet()) { + int magicId = entry.getKey(); + double value = entry.getValue(); + magicDefense.put(magicId, magicDefense.getOrDefault(magicId, 0.0) + value); + } + Map magicResist = ItemAttributeHelper.readMagicAttributeMap(piece, AttributeKeys.MAGIC_RESISTANCE); + for (Entry entry : magicResist.entrySet()) { + int magicId = entry.getKey(); + double value = entry.getValue(); + magicResistance.put(magicId, magicResistance.getOrDefault(magicId, 0.0) + value); + } + } + } + equipmentTracker = currentEquip; + } + // Recalculate the maps using the provided baseDamage. + recalcOffenseMap(baseDamage); + recalcDefenseMap(); + } + + private void recalcOffenseMap(int baseDamage) { + damageMap.clear(); + // Compute non-magic offense = (baseDamage + MAIN_ATTACK_FLAT) * (1 + MAIN_ATTACK_PERCENT) + double mainFlat = getAttributeValue(ModAttributes.MAIN_ATTACK_FLAT); + double mainPercent = getAttributeValue(ModAttributes.MAIN_ATTACK_PERCENT); + double nonMagicOffense = (baseDamage + mainFlat) * (1 + mainPercent); + damageMap.put(BASE_ID, nonMagicOffense); + + // For each magic type: offense = (magicDamageFlat) * (1 + magicDamagePercent) + for (Entry entry : magicDamage.entrySet()) { + int magicId = entry.getKey(); + double flat = entry.getValue(); + double percent = magicBoost.getOrDefault(magicId, 0.0); + double magicOffense = flat * (1 + percent); + damageMap.put(magicId, magicOffense); + } + } + + private void recalcDefenseMap() { + defenseMap.clear(); + // For each magic type: defense = (magicDefenseFlat) * (1 + magicResistancePercent) + for (Entry entry : magicDefense.entrySet()) { + int magicId = entry.getKey(); + double flat = entry.getValue(); + double percent = magicResistance.getOrDefault(magicId, 0.0); + double magicDef = flat * (1 + percent); + defenseMap.put(magicId, magicDef); + } + // Set non-magic defense to 0. + defenseMap.put(BASE_ID, 0.0); + } + + /** + * 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)) { + recalcAttributes(player, 0); + } + } + + public double getAttributeValue(AttributeDefinition def) { + IAttributeInstance inst = playerAttributes.getAttributeInstance(def); + return inst != null ? inst.getValue() : 0.0; + } +} diff --git a/src/main/java/noppes/npcs/attribute/PlayerEquipmentTracker.java b/src/main/java/noppes/npcs/attribute/player/PlayerEquipmentTracker.java similarity index 96% rename from src/main/java/noppes/npcs/attribute/PlayerEquipmentTracker.java rename to src/main/java/noppes/npcs/attribute/player/PlayerEquipmentTracker.java index 88394db1a..661bd0f70 100644 --- a/src/main/java/noppes/npcs/attribute/PlayerEquipmentTracker.java +++ b/src/main/java/noppes/npcs/attribute/player/PlayerEquipmentTracker.java @@ -1,8 +1,9 @@ -package noppes.npcs.attribute; +package noppes.npcs.attribute.player; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.attribute.ItemAttributeHelper; public class PlayerEquipmentTracker { public ItemStack heldItem;