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<PlayerCharacter, PlayerCombatStats> 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<String> 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);
+    }
+}