From 2bb414fc33ff125908e3f5ce01dee38f90668bf3 Mon Sep 17 00:00:00 2001
From: MagicBot <MagicBot@magicbane.com>
Date: Sat, 23 Mar 2024 18:33:32 -0400
Subject: [PATCH] Update to passive logic

---
 src/engine/Enum.java                          |  13 ++
 .../gameManager/FinalCombatManager.java       | 210 +++++++++++-------
 2 files changed, 147 insertions(+), 76 deletions(-)

diff --git a/src/engine/Enum.java b/src/engine/Enum.java
index 393343f5..ef9d5177 100644
--- a/src/engine/Enum.java
+++ b/src/engine/Enum.java
@@ -2839,4 +2839,17 @@ public class Enum {
         Intelligence,
         Spirit,
     }
+
+    public enum PassiveType {
+        None(0),
+        Dodge(20),
+        Block(21),
+        Parry(22);
+
+        public int value;
+
+        PassiveType(int value) {
+            this.value = value;
+        }
+    }
 }
diff --git a/src/engine/gameManager/FinalCombatManager.java b/src/engine/gameManager/FinalCombatManager.java
index 39cc81d6..9c0303bf 100644
--- a/src/engine/gameManager/FinalCombatManager.java
+++ b/src/engine/gameManager/FinalCombatManager.java
@@ -15,24 +15,27 @@ import engine.powers.effectmodifiers.AbstractEffectModifier;
 import engine.server.MBServerStatics;
 import org.pmw.tinylog.Logger;
 
+import java.util.EnumSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ThreadLocalRandom;
 
 public class FinalCombatManager {
     public static void combatCycle(AbstractCharacter attacker, AbstractWorldObject target) {
+
         //early exit checks
-        if(attacker == null || target == null || !attacker.isAlive() || !target.isAlive())
+
+        if (attacker == null || target == null || !attacker.isAlive() || !target.isAlive())
             return;
 
-        switch(target.getObjectType()){
+        switch (target.getObjectType()) {
             case Building:
-                if(((Building)target).isVulnerable() == false)
+                if (((Building) target).isVulnerable() == false)
                     return;
                 break;
             case PlayerCharacter:
             case Mob:
-                PlayerBonuses bonuses = ((AbstractCharacter)target).getBonuses();
-                if(bonuses != null && bonuses.getBool(Enum.ModType.ImmuneToAttack, Enum.SourceType.NONE))
+                PlayerBonuses bonuses = ((AbstractCharacter) target).getBonuses();
+                if (bonuses != null && bonuses.getBool(Enum.ModType.ImmuneToAttack, Enum.SourceType.NONE))
                     return;
                 break;
             case NPC:
@@ -41,91 +44,108 @@ public class FinalCombatManager {
 
         Item mainWeapon = attacker.charItemManager.getEquipped().get(Enum.EquipSlotType.RHELD);
         Item offWeapon = attacker.charItemManager.getEquipped().get(Enum.EquipSlotType.LHELD);
-        if(mainWeapon == null && offWeapon == null){
+
+        if (mainWeapon == null && offWeapon == null) {
             //no weapons equipped, punch with both fists
-            processAttack(attacker,target,Enum.EquipSlotType.RHELD);
-            processAttack(attacker,target,Enum.EquipSlotType.LHELD);
-        }else if(mainWeapon == null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block")){
+            processAttack(attacker, target, Enum.EquipSlotType.RHELD);
+            processAttack(attacker, target, Enum.EquipSlotType.LHELD);
+        } else if (mainWeapon == null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block")) {
             //no weapon equipped with a shield, punch with one hand
-            processAttack(attacker,target,Enum.EquipSlotType.RHELD);
-        }else if(mainWeapon != null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block")){
+            processAttack(attacker, target, Enum.EquipSlotType.RHELD);
+        } else if (mainWeapon != null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block")) {
             //one weapon equipped with a shield, swing with one hand
-            processAttack(attacker,target,Enum.EquipSlotType.RHELD);
-        }else if(mainWeapon != null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block") == false){
+            processAttack(attacker, target, Enum.EquipSlotType.RHELD);
+        } else if (mainWeapon != null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block") == false) {
             //two weapons equipped, swing both hands
-            processAttack(attacker,target,Enum.EquipSlotType.RHELD);
-            processAttack(attacker,target,Enum.EquipSlotType.LHELD);
-        } else if(mainWeapon == null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block") == false){
+            processAttack(attacker, target, Enum.EquipSlotType.RHELD);
+            processAttack(attacker, target, Enum.EquipSlotType.LHELD);
+        } else if (mainWeapon == null && offWeapon != null && offWeapon.template.item_skill_required.containsKey("Block") == false) {
             //swing left hand only
-            processAttack(attacker,target,Enum.EquipSlotType.LHELD);
+            processAttack(attacker, target, Enum.EquipSlotType.LHELD);
         }
     }
 
-    public static void processAttack(AbstractCharacter attacker, AbstractWorldObject target, Enum.EquipSlotType slot){
-        //check if character can even attack yet
-        if(attacker.getTimestamps().containsKey("Attack" + slot.name()))
-            if(System.currentTimeMillis() < attacker.getTimestamps().get("Attack" + slot.name()))
+    public static void processAttack(AbstractCharacter attacker, AbstractWorldObject target, Enum.EquipSlotType slot) {
+
+        // heck if character can even attack yet
+
+        if (attacker.getTimestamps().containsKey("Attack" + slot.name()))
+            if (System.currentTimeMillis() < attacker.getTimestamps().get("Attack" + slot.name()))
                 return;
 
-        //check if character is in range to attack target
+        // check if character is in range to attack target
+
         PlayerBonuses bonus = attacker.getBonuses();
+
         float rangeMod = 1.0f;
         float attackRange = MBServerStatics.NO_WEAPON_RANGE;
+
         Item weapon = attacker.charItemManager.getEquipped(slot);
+
         if (weapon != null) {
             if (bonus != null)
                 rangeMod += bonus.getFloatPercentAll(Enum.ModType.WeaponRange, Enum.SourceType.NONE);
 
             attackRange = weapon.template.item_weapon_max_range * rangeMod;
         }
-        if(attacker.getObjectType().equals(Enum.GameObjectType.Mob))
-            if(((Mob)attacker).isSiege())
+
+        if (attacker.getObjectType().equals(Enum.GameObjectType.Mob))
+            if (((Mob) attacker).isSiege())
                 attackRange = 300;
 
         float distanceSquared = attacker.loc.distanceSquared(target.loc);
-        if(distanceSquared > attackRange * attackRange)
+
+        if (distanceSquared > attackRange * attackRange)
             return;
 
-        //take stamina away from attacker
-        if (weapon == null)
-            attacker.modifyStamina(-0.5f, attacker, true);
-        else {
+        // take stamina away from attacker
+
+        if (weapon != null) {
             float stam = weapon.template.item_wt / 3f;
             stam = (stam < 1) ? 1 : stam;
             attacker.modifyStamina(-(stam), attacker, true);
-        }
+        } else
+            attacker.modifyStamina(-0.5f, attacker, true);
 
         //cancel things that are cancelled by an attack
+
         attacker.cancelOnAttackSwing();
 
         //declare relevant variables
+
         int min = attacker.minDamageHandOne;
         int max = attacker.maxDamageHandOne;
         int atr = attacker.atrHandOne;
 
         //get the proper stats based on which slot is attacking
-        if(slot == Enum.EquipSlotType.LHELD){
+
+        if (slot == Enum.EquipSlotType.LHELD) {
             min = attacker.minDamageHandTwo;
             max = attacker.maxDamageHandTwo;
             atr = attacker.atrHandTwo;
         }
+
         int def = 0;
-        if(AbstractCharacter.IsAbstractCharacter(target))
-            def = ((AbstractCharacter)target).defenseRating;
+
+        if (AbstractCharacter.IsAbstractCharacter(target))
+            def = ((AbstractCharacter) target).defenseRating;
 
         //calculate hit chance based off ATR and DEF
+
         int hitChance;
         float dif = atr / def;
+
         if (dif <= 0.8f)
             hitChance = 4;
         else
             hitChance = ((int) (450 * (dif - 0.8f)) + 4);
+
         if (target.getObjectType() == Enum.GameObjectType.Building)
             hitChance = 100;
 
         int passiveAnim = getSwingAnimation(attacker.charItemManager.getEquipped().get(slot).template, null, true);
 
-        if(ThreadLocalRandom.current().nextInt(100) > hitChance) {
+        if (ThreadLocalRandom.current().nextInt(100) > hitChance) {
             TargetedActionMsg msg = new TargetedActionMsg(attacker, target, 0f, passiveAnim);
 
             if (target.getObjectType() == Enum.GameObjectType.PlayerCharacter)
@@ -137,13 +157,25 @@ public class FinalCombatManager {
         }
 
         //calculate passive chances only if target is AbstractCharacter
-        if(AbstractCharacter.IsAbstractCharacter(target)){
+
+        if (EnumSet.of(Enum.GameObjectType.PlayerCharacter, Enum.GameObjectType.NPC, Enum.GameObjectType.Mob).contains(target.getObjectType())) {
+            Enum.PassiveType passiveType = Enum.PassiveType.None;
             int hitRoll = ThreadLocalRandom.current().nextInt(100);
+
             float dodgeChance = ((AbstractCharacter) target).getPassiveChance("Dodge", attacker.getLevel(), true);
             float blockChance = ((AbstractCharacter) target).getPassiveChance("Block", attacker.getLevel(), true);
             float parryChance = ((AbstractCharacter) target).getPassiveChance("Parry", attacker.getLevel(), true);
-            if(hitRoll > dodgeChance || hitRoll > parryChance || hitRoll > blockChance){
-                TargetedActionMsg msg = new TargetedActionMsg(attacker, passiveAnim, target, MBServerStatics.COMBAT_SEND_BLOCK);
+
+            if (hitRoll < dodgeChance)
+                passiveType = Enum.PassiveType.Dodge;
+            else if (hitRoll < blockChance)
+                passiveType = Enum.PassiveType.Block;
+            else if (hitRoll < parryChance)
+                passiveType = Enum.PassiveType.Parry;
+
+
+            if (passiveType.equals(Enum.PassiveType.None) == false) {
+                TargetedActionMsg msg = new TargetedActionMsg(attacker, passiveAnim, target, passiveType.value);
 
                 if (target.getObjectType() == Enum.GameObjectType.PlayerCharacter)
                     DispatchMessage.dispatchMsgToInterestArea(target, msg, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
@@ -155,34 +187,37 @@ public class FinalCombatManager {
         }
 
         //calculate the base damage
-        int damage = ThreadLocalRandom.current().nextInt(min,max + 1);
-        if(damage == 0)
+        int damage = ThreadLocalRandom.current().nextInt(min, max + 1);
+        if (damage == 0)
             return;
 
         //get the damage type
+
         Enum.SourceType damageType;
-        if(attacker.charItemManager.getEquipped().get(slot) == null) {
+
+        if (attacker.charItemManager.getEquipped().get(slot) == null) {
             damageType = Enum.SourceType.CRUSHING;
-            if(attacker.getObjectType().equals(Enum.GameObjectType.Mob))
-                if(((Mob)attacker).isSiege())
+            if (attacker.getObjectType().equals(Enum.GameObjectType.Mob))
+                if (((Mob) attacker).isSiege())
                     damageType = Enum.SourceType.SIEGE;
-        }else {
+        } else {
             damageType = (Enum.SourceType) attacker.charItemManager.getEquipped().get(slot).template.item_weapon_damage.keySet().toArray()[0];
         }
 
         //get resists
+
         Resists resists;
-        if(AbstractCharacter.IsAbstractCharacter(target) == false){
-            //this is a building
-            resists = ((Building) target).getResists();
-        }else{
-            //this is a character
-            resists = ((AbstractCharacter) target).getResists();
-        }
 
-        if(AbstractCharacter.IsAbstractCharacter(target)) {
+        if (AbstractCharacter.IsAbstractCharacter(target) == false)
+            resists = ((Building) target).getResists();            //this is a building
+        else
+            resists = ((AbstractCharacter) target).getResists();   //this is a character
+
+        if (AbstractCharacter.IsAbstractCharacter(target)) {
             AbstractCharacter absTarget = (AbstractCharacter) target;
+
             //check damage shields
+
             PlayerBonuses bonuses = absTarget.getBonuses();
 
             if (bonuses != null) {
@@ -193,13 +228,16 @@ public class FinalCombatManager {
                 for (DamageShield ds : damageShields.values()) {
 
                     //get amount to damage back
+
                     float amount;
+
                     if (ds.usePercent())
                         amount = damage * ds.getAmount() / 100;
                     else
                         amount = ds.getAmount();
 
                     //get resisted damage for damagetype
+
                     if (resists != null)
                         amount = resists.getResistedDamage(absTarget, attacker, ds.getDamageType(), amount, 0);
                     total += amount;
@@ -212,29 +250,39 @@ public class FinalCombatManager {
                     DispatchMessage.sendToAllInRange(target, cmm);
                 }
             }
+
             if (resists != null) {
+
                 //check for damage type immunities
+
                 if (resists.immuneTo(damageType))
                     return;
+
                 //calculate resisted damage including fortitude
-                damage = (int) resists.getResistedDamage(attacker,(AbstractCharacter)target,damageType,damage,0);
+
+                damage = (int) resists.getResistedDamage(attacker, (AbstractCharacter) target, damageType, damage, 0);
             }
         }
 
         //remove damage from target health
-        if(damage > 0){
-            if(AbstractCharacter.IsAbstractCharacter(target)){
-                ((AbstractCharacter)target).modifyHealth(-damage, attacker, true);
-            }else{
-                ((Building)target).setCurrentHitPoints(target.getCurrentHitpoints() - damage);
-            }
-            TargetedActionMsg cmm = new TargetedActionMsg(attacker,target, (float) damage,0);
+
+        if (damage > 0) {
+
+            if (AbstractCharacter.IsAbstractCharacter(target))
+                ((AbstractCharacter) target).modifyHealth(-damage, attacker, true);
+            else
+                ((Building) target).setCurrentHitPoints(target.getCurrentHitpoints() - damage);
+
+            TargetedActionMsg cmm = new TargetedActionMsg(attacker, target, (float) damage, 0);
             DispatchMessage.sendToAllInRange(target, cmm);
         }
 
         //calculate next allowed attack and update the timestamp
+
         long delay = 20 * 100;
-        if (weapon != null){
+
+        if (weapon != null) {
+
             int wepSpeed = (int) (weapon.template.item_weapon_wepspeed);
 
             if (weapon.getBonusPercent(Enum.ModType.WeaponSpeed, Enum.SourceType.NONE) != 0f) //add weapon speed bonus
@@ -248,9 +296,11 @@ public class FinalCombatManager {
 
             delay = wepSpeed * 100;
         }
-        attacker.getTimestamps().put("Attack" + slot.name(),System.currentTimeMillis() + delay);
+
+        attacker.getTimestamps().put("Attack" + slot.name(), System.currentTimeMillis() + delay);
 
         //handle auto attack job creation
+
         ConcurrentHashMap<String, JobContainer> timers = attacker.getTimers();
 
         if (timers != null) {
@@ -258,41 +308,46 @@ public class FinalCombatManager {
             JobContainer job;
             job = JobScheduler.getInstance().scheduleJob(aj, (delay + 1)); // offset 1 millisecond so no overlap issue
             timers.put("Attack" + slot, job);
-        } else {
+        } else
             Logger.error("Unable to find Timers for Character " + attacker.getObjectUUID());
-        }
+
     }
 
     public static void toggleCombat(boolean toggle, ClientConnection origin) {
 
-        PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
-        if (pc == null)
+        PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin);
+
+        if (playerCharacter == null)
             return;
 
-        pc.setCombat(toggle);
+        playerCharacter.setCombat(toggle);
+
         if (!toggle) // toggle is move it to false so clear combat target
-            pc.setCombatTarget(null); //clear last combat target
+            playerCharacter.setCombatTarget(null); //clear last combat target
 
         UpdateStateMsg rwss = new UpdateStateMsg();
-        rwss.setPlayer(pc);
-        DispatchMessage.dispatchMsgToInterestArea(pc, rwss, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
+        rwss.setPlayer(playerCharacter);
+        DispatchMessage.dispatchMsgToInterestArea(playerCharacter, rwss, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
     }
 
     public static void toggleSit(boolean toggle, ClientConnection origin) {
 
-        PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
-        if (pc == null)
+        PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin);
+
+        if (playerCharacter == null)
             return;
 
-        pc.setSit(toggle);
+        playerCharacter.setSit(toggle);
         UpdateStateMsg rwss = new UpdateStateMsg();
-        rwss.setPlayer(pc);
-        DispatchMessage.dispatchMsgToInterestArea(pc, rwss, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
+        rwss.setPlayer(playerCharacter);
+        DispatchMessage.dispatchMsgToInterestArea(playerCharacter, rwss, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
     }
 
-    //Called when character takes damage.
+
     public static void handleRetaliate(AbstractCharacter target, AbstractCharacter attacker) {
 
+        //Called when character takes damage.
+
         if (attacker == null || target == null)
             return;
 
@@ -308,11 +363,13 @@ public class FinalCombatManager {
         boolean isCombat = target.isCombat();
 
         //If target in combat and has no target, then attack back
-        if(isCombat && target.combatTarget == null)
+
+        if (isCombat && target.combatTarget == null)
             target.setCombatTarget(attacker);
     }
 
     public static int getSwingAnimation(ItemTemplate wb, DeferredPowerJob dpj, boolean mainHand) {
+
         int token = 0;
 
         if (dpj != null)
@@ -356,6 +413,7 @@ public class FinalCombatManager {
             }
         } else {
             if (template.weapon_attack_anim_left.size() > 0) {
+
                 int animation;
                 int random = ThreadLocalRandom.current().nextInt(template.weapon_attack_anim_left.size());