diff --git a/src/engine/objects/PlayerCharacter.java b/src/engine/objects/PlayerCharacter.java index 56979b05..9f9c0532 100644 --- a/src/engine/objects/PlayerCharacter.java +++ b/src/engine/objects/PlayerCharacter.java @@ -180,6 +180,8 @@ public class PlayerCharacter extends AbstractCharacter { public boolean isBoxed = false; + public PlayerCombatStats combatStats; + /** * No Id Constructor */ @@ -5106,6 +5108,13 @@ public class PlayerCharacter extends AbstractCharacter { } + + if(this.combatStats == null){ + this.combatStats = new PlayerCombatStats(this); + } + PlayerCombatStats cStats = this.combatStats; + cStats.update(); + } catch (Exception e) { Logger.error(e); } finally { diff --git a/src/engine/objects/PlayerCombatStats.java b/src/engine/objects/PlayerCombatStats.java new file mode 100644 index 00000000..35b44189 --- /dev/null +++ b/src/engine/objects/PlayerCombatStats.java @@ -0,0 +1,405 @@ +package engine.objects; + +import engine.Enum; +import engine.powers.effectmodifiers.AbstractEffectModifier; + +import java.util.ArrayList; +import java.util.HashMap; + +public class PlayerCombatStats { + public static HashMap combatstats = new HashMap<>(); + + public PlayerCharacter owner; + //main hand data + public int minDamageHandOne; + public int maxDamageHandOne; + public float attackSpeedHandOne; + public float rangeHandOne; + public float atrHandOne; + //off hand data + public int minDamageHandTwo; + public int maxDamageHandTwo; + public float attackSpeedHandTwo; + public float rangeHandTwo; + public float atrHandTwo; + //defense + public int defense; + //regen rates + public float healthRegen; + public float manaRegen; + public float staminaRegen; + + public PlayerCombatStats(PlayerCharacter pc) { + this.owner = pc; + this.update(); + } + + public void update() { + this.calculateATR(true); + this.calculateATR(false); + this.calculateMin(true); + this.calculateMin(false); + this.calculateMax(true); + this.calculateMax(false); + this.calculateAttackSpeed(true); + this.calculateAttackSpeed(false); + this.calculateAttackRange(true); + this.calculateAttackRange(false); + this.calculateRegen(); + this.calculateDefense(); + combatstats.put(this.owner, this); + } + + public void calculateATR(boolean mainHand) { + Item weapon; + float atr; + if(mainHand) { + weapon = this.owner.charItemManager.getEquipped(1); + }else { + weapon = this.owner.charItemManager.getEquipped(2); + } + + String skill = "Unarmed Combat"; + String mastery = "Unarmed Combat Mastery"; + int primary = this.owner.statDexCurrent; + if(weapon != null) { + skill= weapon.getItemBase().getSkillRequired(); + mastery = weapon.getItemBase().getMastery(); + if(weapon.getItemBase().isStrBased()) + primary = this.owner.statStrCurrent; + } + + float skillLevel = 0; + float masteryLevel = 0; + + if(this.owner.skills.containsKey(skill)) + skillLevel = this.owner.skills.get(skill).getModifiedAmount(); + + if(this.owner.skills.containsKey(mastery)) + masteryLevel = this.owner.skills.get(mastery).getModifiedAmount(); + + float primaryCalc = primary * 0.5f; + float skillCalc = skillLevel * 4; + float masteryCalc = masteryLevel * 3; + float atrEnchants = 0; + float stanceValue = 0.0f; + + for(String effID : this.owner.effects.keySet()){ + if(effID.contains("STC")){ + for(AbstractEffectModifier mod : this.owner.effects.get(effID).getEffectModifiers()){ + if(mod.modType.equals(Enum.ModType.AttackDelay)){ + stanceValue = mod.getPercentMod() * 0.01f; // account for weapon prefix and suffix mods + } + } + } + } + + if(this.owner.bonuses != null){ + atrEnchants = this.owner.bonuses.getFloat(Enum.ModType.OCV, Enum.SourceType.None); + } + + atrEnchants -= stanceValue; + + atr = primaryCalc + skillCalc + masteryCalc + atrEnchants; + atr *= 1 + stanceValue; + + if(mainHand){ + this.atrHandOne = atr; + }else{ + this.atrHandTwo = atr; + } + } + + public void calculateMin(boolean mainHand) { + Item weapon; + double baseDMG = 6; + int primaryStat = this.owner.statDexCurrent; + int secondaryStat = this.owner.statStrCurrent; + double weaponSkill = 0; + double weaponMastery = 0; + + if (mainHand) { + weapon = this.owner.charItemManager.getEquipped(1); + } else { + weapon = this.owner.charItemManager.getEquipped(2); + } + + String skill = "Unarmed Combat"; + String mastery = "Unarmed Combat Mastery"; + + if (weapon != null) { + baseDMG = weapon.getItemBase().getMaxDamage(); + skill = weapon.getItemBase().getSkillRequired(); + mastery = weapon.getItemBase().getMastery(); + if (weapon.getItemBase().isStrBased()) { + primaryStat = this.owner.statStrCurrent; + secondaryStat = this.owner.statDexCurrent; + } + } + + if (this.owner.skills.containsKey(skill)) { + weaponSkill = this.owner.skills.get(skill).getModifiedAmount(); + } + + if (this.owner.skills.containsKey(mastery)) { + weaponMastery = this.owner.skills.get(mastery).getModifiedAmount(); + } + + double minDMG = baseDMG * ( + 0.0048 * primaryStat + + 0.049 * Math.sqrt(primaryStat - 0.75) + + 0.0066 * secondaryStat + + 0.064 * Math.sqrt(secondaryStat - 0.75) + + 0.01 * (weaponSkill + weaponMastery) + ); + + if (mainHand) { + this.minDamageHandOne = (int) minDMG; + } else { + this.minDamageHandTwo = (int) minDMG; + } + } + + public void calculateMax(boolean mainHand) { + //Weapon Max DMG = BaseDMG * (0.0124*Primary Stat + 0.118*(Primary Stat -0.75)^0.5 + // + 0.0022*Secondary Stat + 0.028*(Secondary Stat-0.75)^0.5 + 0.0075*(Weapon Skill + Weapon Mastery)) + Item weapon; + double baseDMG = 6; + int primaryStat = this.owner.statDexCurrent; + int secondaryStat = this.owner.statStrCurrent; + double weaponSkill = 0; + double weaponMastery = 0; + + if (mainHand) { + weapon = this.owner.charItemManager.getEquipped(1); + } else { + weapon = this.owner.charItemManager.getEquipped(2); + } + + String skill = "Unarmed Combat"; + String mastery = "Unarmed Combat Mastery"; + + if (weapon != null) { + baseDMG = weapon.getItemBase().getMaxDamage(); + skill = weapon.getItemBase().getSkillRequired(); + mastery = weapon.getItemBase().getMastery(); + if (weapon.getItemBase().isStrBased()) { + primaryStat = this.owner.statStrCurrent; + secondaryStat = this.owner.statDexCurrent; + } + } + + if (this.owner.skills.containsKey(skill)) { + weaponSkill = this.owner.skills.get(skill).getModifiedAmount(); + }else{ + + } + + if (this.owner.skills.containsKey(mastery)) { + weaponMastery = this.owner.skills.get(mastery).getModifiedAmount(); + } + + double maxDMG = baseDMG * ( + 0.0124 * primaryStat + + 0.118 * Math.sqrt(primaryStat - 0.75) + + 0.0022 * secondaryStat + + 0.028 * Math.sqrt(secondaryStat - 0.75) + + 0.0075 * (weaponSkill + weaponMastery) + ); + + if(mainHand){ + this.maxDamageHandOne = (int) maxDMG; + }else{ + this.maxDamageHandTwo = (int) maxDMG; + } + } + + public void calculateAttackSpeed(boolean mainHand){ + Item weapon; + float speed; + if(mainHand) { + weapon = this.owner.charItemManager.getEquipped(1); + }else { + weapon = this.owner.charItemManager.getEquipped(2); + } + + if(weapon == null) { + speed = 20.0f; + }else{ + speed = weapon.getItemBase().getSpeed(); + for(Effect eff : weapon.effects.values()){ + for(AbstractEffectModifier mod : eff.getEffectModifiers()){ + if(mod.modType.equals(Enum.ModType.WeaponProc)){ + speed *= 1 + (mod.getPercentMod() * 0.01f); + } + } + } + } + + float stanceValue = 1.0f; + for(String effID : this.owner.effects.keySet()){ + if(effID.contains("STC")){ + for(AbstractEffectModifier mod : this.owner.effects.get(effID).getEffectModifiers()){ + if(mod.modType.equals(Enum.ModType.AttackDelay)){ + stanceValue = mod.getPercentMod() * 0.01f; // account for weapon prefix and suffix mods + } + } + } + } + + float bonusValues = 1.0f; + if(this.owner.bonuses != null){ + for(AbstractEffectModifier mod : this.owner.bonuses.bonusFloats.keySet()){ + if(mod.modType.equals(Enum.ModType.AttackDelay)){ + bonusValues += mod.getPercentMod() * 0.01f; // calculate all alac bonuses + } + } + } + + bonusValues -= stanceValue; // take away stance modifier from alac bonus values + speed *= 1 + stanceValue; // apply stance bonus + speed *= bonusValues; // apply alac bonuses without stance mod + + if(speed < 10.0f) + speed = 10.0f; + + if(mainHand){ + this.attackSpeedHandOne = speed; + }else{ + this.attackSpeedHandTwo = speed; + } + } + + public void calculateAttackRange(boolean mainHand){ + Item weapon; + float range; + if(mainHand) { + weapon = this.owner.charItemManager.getEquipped(1); + }else { + weapon = this.owner.charItemManager.getEquipped(2); + } + + if(weapon == null) { + range = 6.0f; + }else{ + range = weapon.getItemBase().getRange(); + } + if(owner.bonuses != null){ + range *= 1 + this.owner.bonuses.getFloatPercentAll(Enum.ModType.WeaponRange, Enum.SourceType.None); + } + if(mainHand){ + this.rangeHandOne = range; + }else{ + this.rangeHandTwo = range; + } + } + + public void calculateRegen(){ + if(owner.bonuses != null){ + this.healthRegen = 1.0f + this.owner.bonuses.getFloatPercentAll(Enum.ModType.HealthRecoverRate, Enum.SourceType.None); + this.manaRegen = 1.0f + this.owner.bonuses.getFloatPercentAll(Enum.ModType.ManaRecoverRate, Enum.SourceType.None); + this.staminaRegen = 1.0f + this.owner.bonuses.getFloatPercentAll(Enum.ModType.StaminaRecoverRate, Enum.SourceType.None); + + }else{ + this.healthRegen = 1.0f; + this.manaRegen = 1.0f; + this.staminaRegen = 1.0f; + } + } + + public void calculateDefense(){ + int armorDefense = 0; + int shieldDefense = 0; + int dexterity = this.owner.statDexCurrent; + double armorSkill = 0; + double blockSkill = 0; + double weaponSkill = 0; + double weaponMastery = 0; + int flatBonuses = 0; + + //armor defense value need to loop all equipped items and log armor defense values + ArrayList armorTypes = new ArrayList<>(); + if(this.owner.charItemManager != null){ + for(Item equipped : this.owner.charItemManager.getEquippedList()){ + if(equipped.getItemBase().isHeavyArmor()){ + armorDefense += equipped.getItemBase().getDefense(); + if(!armorTypes.contains(equipped.getItemBase().getSkillRequired())) + armorTypes.add(equipped.getItemBase().getSkillRequired()); + } else if(equipped.getItemBase().isLightArmor()){ + armorDefense += equipped.getItemBase().getDefense(); + if(!armorTypes.contains(equipped.getItemBase().getSkillRequired())) + armorTypes.add(equipped.getItemBase().getSkillRequired()); + } else if(equipped.getItemBase().isMediumArmor()){ + armorDefense += equipped.getItemBase().getDefense(); + if(!armorTypes.contains(equipped.getItemBase().getSkillRequired())) + armorTypes.add(equipped.getItemBase().getSkillRequired()); + } else if(equipped.getItemBase().isClothArmor()){ + armorDefense += equipped.getItemBase().getDefense(); + }else if(equipped.getItemBase().isShield()){ + shieldDefense += equipped.getItemBase().getDefense(); + } + } + } + + //armor skill needs to calculate all trains in armor types + for(String armorType : armorTypes){ + if(this.owner.skills!= null){ + if(this.owner.skills.containsKey(armorType)){ + armorSkill += this.owner.skills.get(armorType).getModifiedAmount(); + } + } + } + + + if (this.owner.skills.containsKey("Block")) { + blockSkill = this.owner.skills.get("Block").getModifiedAmount(); + } + + String primarySkillName = "Unarmed Combat"; + String primaryMasteryName = "Unarmed Combat Mastery"; + Item weapon = this.owner.charItemManager.getEquipped(1); + if(weapon == null){ + weapon = this.owner.charItemManager.getEquipped(2); + } + if(weapon != null){ + primarySkillName = weapon.getItemBase().getSkillRequired(); + primaryMasteryName = weapon.getItemBase().getMastery(); + } + + if(this.owner.skills != null){ + if(this.owner.skills.containsKey(primarySkillName)){ + weaponSkill = this.owner.skills.get(primarySkillName).getModifiedAmount(); + } + if(this.owner.skills.containsKey(primaryMasteryName)){ + weaponMastery = this.owner.skills.get(primaryMasteryName).getModifiedAmount(); + } + } + + float stanceValue = 0.0f; + if(this.owner.bonuses != null){ + for(String effID : this.owner.effects.keySet()){ + if(effID.contains("STC")){ + for(AbstractEffectModifier mod : this.owner.effects.get(effID).getEffectModifiers()){ + if(mod.modType.equals(Enum.ModType.AttackDelay)){ + stanceValue = mod.getPercentMod() * 0.01f; // account for weapon prefix and suffix mods + } + } + } + } + + float bonusValues = 1.0f + this.owner.bonuses.getFloat(Enum.ModType.DCV, Enum.SourceType.None); + + bonusValues -= stanceValue; // take away stance modifier from alac bonus values + flatBonuses += bonusValues; // apply alac bonuses without stance mod + } + + double defense = (1 + armorSkill / 50.0) * armorDefense + + (1 + blockSkill / 100.0) * shieldDefense + + (weaponSkill / 2.0) + + (weaponMastery / 2.0) + + dexterity * 2.0 + + flatBonuses; + + this.defense = (int) (defense * 1.0f + stanceValue); + } +}