2 changed files with 35 additions and 450 deletions
			
			
		@ -1,430 +0,0 @@
				@@ -1,430 +0,0 @@
					 | 
				
			||||
package engine.gameManager; | 
				
			||||
 | 
				
			||||
import engine.Enum; | 
				
			||||
import engine.exception.MsgSendException; | 
				
			||||
import engine.job.JobContainer; | 
				
			||||
import engine.job.JobScheduler; | 
				
			||||
import engine.jobs.AttackJob; | 
				
			||||
import engine.jobs.DeferredPowerJob; | 
				
			||||
import engine.net.DispatchMessage; | 
				
			||||
import engine.net.client.ClientConnection; | 
				
			||||
import engine.net.client.msg.AttackCmdMsg; | 
				
			||||
import engine.net.client.msg.TargetedActionMsg; | 
				
			||||
import engine.objects.*; | 
				
			||||
import engine.powers.DamageShield; | 
				
			||||
import engine.powers.effectmodifiers.AbstractEffectModifier; | 
				
			||||
import engine.powers.effectmodifiers.WeaponProcEffectModifier; | 
				
			||||
import engine.server.MBServerStatics; | 
				
			||||
import org.pmw.tinylog.Logger; | 
				
			||||
 | 
				
			||||
import java.util.concurrent.ConcurrentHashMap; | 
				
			||||
import java.util.concurrent.ThreadLocalRandom; | 
				
			||||
 | 
				
			||||
public class CombatSystem { | 
				
			||||
 | 
				
			||||
    public static void attemptCombat(AbstractCharacter source, AbstractWorldObject target, boolean mainhand){ | 
				
			||||
 | 
				
			||||
        //1. source or target doesn't exist, early exit
 | 
				
			||||
        if(source == null || target == null) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        //2. source or target is dead, early exit
 | 
				
			||||
        if(!source.isAlive() || !target.isAlive()) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        //3. make sure if target is a building to ensure that it is damageable
 | 
				
			||||
        if(target.getObjectType().equals(Enum.GameObjectType.Building)){ | 
				
			||||
            Building building = (Building)target; | 
				
			||||
            if(building.assetIsProtected() || building.getProtectionState().equals(Enum.ProtectionState.NPC)) | 
				
			||||
                return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //after thought: make sure target is in range of source
 | 
				
			||||
        if(!inRange(source,target,mainhand)) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        //4. apply any weapon powers and then clear the weapon power memory for the player
 | 
				
			||||
        if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { | 
				
			||||
            PlayerCharacter pc = (PlayerCharacter)source; | 
				
			||||
            if(pc.getWeaponPower() != null){ | 
				
			||||
                pc.getWeaponPower().attack(target,pc.getRange()); | 
				
			||||
                pc.setWeaponPower(null); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //5. make sure if target is AbstractCharacter to check for defense trigger and passive trigger
 | 
				
			||||
        if(AbstractCharacter.IsAbstractCharacter(target)) { | 
				
			||||
            int atr; | 
				
			||||
            int def; | 
				
			||||
            if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { | 
				
			||||
                PlayerCharacter pc = (PlayerCharacter)source; | 
				
			||||
                if(pc.combatStats == null) | 
				
			||||
                    pc.combatStats = new PlayerCombatStats(pc); | 
				
			||||
                atr = (int) pc.combatStats.atrHandOne; | 
				
			||||
                if(!mainhand) | 
				
			||||
                    atr =(int) pc.combatStats.atrHandTwo; | 
				
			||||
 | 
				
			||||
                def = pc.combatStats.defense; | 
				
			||||
            } else { | 
				
			||||
                atr = (int) ((source.getAtrHandOne() + source.getAtrHandTwo()) * 0.5f); | 
				
			||||
                def = source.defenseRating; | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            if(!LandHit(atr,def)) { | 
				
			||||
                createTimer(source,mainhand); | 
				
			||||
                return; | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            if(source.getBonuses() != null) | 
				
			||||
                if(!source.getBonuses().getBool(Enum.ModType.IgnorePassiveDefense, Enum.SourceType.None)) | 
				
			||||
                    if(triggerPassive(source,target)) { | 
				
			||||
                        createTimer(source,mainhand); | 
				
			||||
                        return; | 
				
			||||
                    } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //commence actual combat management
 | 
				
			||||
 | 
				
			||||
        //6. check for any procs
 | 
				
			||||
        if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { | 
				
			||||
            PlayerCharacter pc = (PlayerCharacter)source; | 
				
			||||
            if(pc.getCharItemManager() != null && pc.getCharItemManager().getEquipped() != null){ | 
				
			||||
                Item weapon = pc.getCharItemManager().getEquipped(1); | 
				
			||||
                if(!mainhand) | 
				
			||||
                    weapon = pc.getCharItemManager().getEquipped(2); | 
				
			||||
                if(weapon != null){ | 
				
			||||
                    if(weapon.effects != null){ | 
				
			||||
                        for (Effect eff : weapon.effects.values()){ | 
				
			||||
                            for(AbstractEffectModifier mod : eff.getEffectModifiers()){ | 
				
			||||
                                if(mod.modType.equals(Enum.ModType.WeaponProc)){ | 
				
			||||
                                    int procChance = ThreadLocalRandom.current().nextInt(0,101); | 
				
			||||
                                    if (procChance <= MBServerStatics.PROC_CHANCE) { | 
				
			||||
                                        try { | 
				
			||||
                                            ((WeaponProcEffectModifier) mod).applyProc(source, target); | 
				
			||||
                                        }catch(Exception e){ | 
				
			||||
                                            Logger.error(eff.getName() + " Failed To Cast Proc"); | 
				
			||||
                                        } | 
				
			||||
                                    } | 
				
			||||
                                } | 
				
			||||
                            } | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //7. configure damage amounts and type
 | 
				
			||||
        Enum.DamageType damageType = Enum.DamageType.Crush; | 
				
			||||
        int min = 0; | 
				
			||||
        int max = 0; | 
				
			||||
        if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { | 
				
			||||
            PlayerCharacter pc = (PlayerCharacter) source; | 
				
			||||
            if(mainhand){ | 
				
			||||
                min = pc.combatStats.minDamageHandOne; | 
				
			||||
                max = pc.combatStats.maxDamageHandOne; | 
				
			||||
            }else{ | 
				
			||||
                min = pc.combatStats.minDamageHandTwo; | 
				
			||||
                max = pc.combatStats.maxDamageHandTwo; | 
				
			||||
            } | 
				
			||||
        }else if (source.getObjectType().equals(Enum.GameObjectType.Mob)) { | 
				
			||||
            Mob mob = (Mob) source; | 
				
			||||
            min = (int) mob.mobBase.getDamageMin(); | 
				
			||||
            max = (int) mob.mobBase.getDamageMax(); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        int damage = ThreadLocalRandom.current().nextInt(min,max + 1); | 
				
			||||
 | 
				
			||||
        if(source.getBonuses() != null){ | 
				
			||||
            damage *= 1 + source.getBonuses().getFloatPercentAll(Enum.ModType.MeleeDamageModifier, Enum.SourceType.None); | 
				
			||||
        } | 
				
			||||
        if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) { | 
				
			||||
            PlayerCharacter pc = (PlayerCharacter) source; | 
				
			||||
            damage *= pc.ZergMultiplier; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //8. configure the attack message to be sent to the clients
 | 
				
			||||
        int animation = 0; | 
				
			||||
        ItemBase wb = null; | 
				
			||||
        if(source.getCharItemManager() != null && source.getCharItemManager().getEquipped() != null) { | 
				
			||||
            Item weapon = source.getCharItemManager().getEquipped(1); | 
				
			||||
            if (!mainhand) | 
				
			||||
                weapon = source.getCharItemManager().getEquipped(2); | 
				
			||||
 | 
				
			||||
            if(weapon != null && weapon.getItemBase().getAnimations() != null && !weapon.getItemBase().getAnimations().isEmpty()){ | 
				
			||||
                animation = weapon.getItemBase().getAnimations().get(0); | 
				
			||||
                wb = weapon.getItemBase(); | 
				
			||||
                damageType = wb.getDamageType(); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //9. reduce damage from resists and apply damage shields
 | 
				
			||||
        if(AbstractCharacter.IsAbstractCharacter(target)){ | 
				
			||||
            AbstractCharacter abs = (AbstractCharacter) target; | 
				
			||||
            damage = (int) abs.getResists().getResistedDamage(source, abs,damageType,damage,1); | 
				
			||||
            handleDamageShields(source,abs,damage); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        sendCombatMessage(source, target, 0f, wb, null, mainhand, animation); | 
				
			||||
 | 
				
			||||
        //if attacker is player, set last attack timestamp
 | 
				
			||||
        if (source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) | 
				
			||||
            updateAttackTimers((PlayerCharacter) source, target); | 
				
			||||
 | 
				
			||||
        //10. cancel all effects that cancel on attack
 | 
				
			||||
        source.cancelOnAttack(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static boolean LandHit(int ATR, int DEF){ | 
				
			||||
 | 
				
			||||
        int roll = ThreadLocalRandom.current().nextInt(101); | 
				
			||||
 | 
				
			||||
        float chance = PlayerCombatStats.getHitChance(ATR,DEF); | 
				
			||||
        return chance >= roll; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private static void sendCombatMessage(AbstractCharacter source, AbstractWorldObject target, float damage, ItemBase wb, DeferredPowerJob dpj, boolean mainHand, int swingAnimation) { | 
				
			||||
 | 
				
			||||
        if (dpj != null) | 
				
			||||
            if (PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) | 
				
			||||
                swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); | 
				
			||||
 | 
				
			||||
        if (source.getObjectType() == Enum.GameObjectType.PlayerCharacter) | 
				
			||||
            for (Effect eff : source.getEffects().values()) | 
				
			||||
                if (eff.getPower() != null && (eff.getPower().getToken() == 429506943 || eff.getPower().getToken() == 429408639 || eff.getPower().getToken() == 429513599 || eff.getPower().getToken() == 429415295)) | 
				
			||||
                    swingAnimation = 0; | 
				
			||||
 | 
				
			||||
        TargetedActionMsg cmm = new TargetedActionMsg(source, target, damage, swingAnimation); | 
				
			||||
        DispatchMessage.sendToAllInRange(target, cmm); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private static void updateAttackTimers(PlayerCharacter pc, AbstractWorldObject target) { | 
				
			||||
 | 
				
			||||
        //Set Attack Timers
 | 
				
			||||
 | 
				
			||||
        if (target.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) | 
				
			||||
            pc.setLastPlayerAttackTime(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static void handleDamageShields(AbstractCharacter ac, AbstractCharacter target, float damage) { | 
				
			||||
 | 
				
			||||
        if (ac == null || target == null) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        PlayerBonuses bonuses = target.getBonuses(); | 
				
			||||
 | 
				
			||||
        if (bonuses != null) { | 
				
			||||
 | 
				
			||||
            ConcurrentHashMap<AbstractEffectModifier, DamageShield> damageShields = bonuses.getDamageShields(); | 
				
			||||
            float total = 0; | 
				
			||||
 | 
				
			||||
            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
 | 
				
			||||
 | 
				
			||||
                Resists resists = ac.getResists(); | 
				
			||||
 | 
				
			||||
                if (resists != null) { | 
				
			||||
                    amount = resists.getResistedDamage(target, ac, ds.getDamageType(), amount, 0); | 
				
			||||
                } | 
				
			||||
                total += amount; | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            if (total > 0) { | 
				
			||||
 | 
				
			||||
                //apply Damage back
 | 
				
			||||
 | 
				
			||||
                ac.modifyHealth(-total, target, true); | 
				
			||||
 | 
				
			||||
                TargetedActionMsg cmm = new TargetedActionMsg(ac, ac, total, 0); | 
				
			||||
                DispatchMessage.sendToAllInRange(target, cmm); | 
				
			||||
 | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static boolean inRange(AbstractCharacter source, AbstractWorldObject target, boolean mainhand){ | 
				
			||||
 | 
				
			||||
        if(source == null || target == null) | 
				
			||||
            return false; | 
				
			||||
 | 
				
			||||
        float distanceSquared = source.loc.distanceSquared(target.loc); | 
				
			||||
 | 
				
			||||
        float rangeSquared = 16.0f; | 
				
			||||
 | 
				
			||||
        if(source.getCharItemManager() != null && source.getCharItemManager().getEquipped() != null){ | 
				
			||||
            Item weapon = source.getCharItemManager().getEquipped(1); | 
				
			||||
            if(!mainhand) | 
				
			||||
                weapon = source.getCharItemManager().getEquipped(2); | 
				
			||||
            if(weapon != null) | 
				
			||||
                rangeSquared = weapon.getItemBase().getRange() * weapon.getItemBase().getRange(); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if(source.getBonuses() != null){ | 
				
			||||
            rangeSquared *= 1 + source.getBonuses().getFloatPercentAll(Enum.ModType.WeaponRange, Enum.SourceType.None); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        return distanceSquared <= rangeSquared; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static boolean triggerPassive(AbstractCharacter source, AbstractWorldObject target) { | 
				
			||||
        boolean passiveFired = false; | 
				
			||||
 | 
				
			||||
        if (!AbstractCharacter.IsAbstractCharacter(target)) | 
				
			||||
            return false; | 
				
			||||
 | 
				
			||||
        AbstractCharacter tarAc = (AbstractCharacter) target; | 
				
			||||
        //Handle Block passive
 | 
				
			||||
        if (testPassive(source, tarAc, "Block")) { | 
				
			||||
            sendPassiveDefenseMessage(source, null, target, MBServerStatics.COMBAT_SEND_DODGE, null, true); | 
				
			||||
            return true; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //Handle Parry passive
 | 
				
			||||
        if (testPassive(source, tarAc, "Parry")) { | 
				
			||||
            sendPassiveDefenseMessage(source, null, target, MBServerStatics.COMBAT_SEND_DODGE, null, true); | 
				
			||||
            return true; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        //Handle Dodge passive
 | 
				
			||||
        if (testPassive(source, tarAc, "Dodge")) { | 
				
			||||
            sendPassiveDefenseMessage(source, null, target, MBServerStatics.COMBAT_SEND_DODGE, null, true); | 
				
			||||
            return true; | 
				
			||||
        } | 
				
			||||
        return false; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private static void sendPassiveDefenseMessage(AbstractCharacter source, ItemBase wb, AbstractWorldObject target, int passiveType, DeferredPowerJob dpj, boolean mainHand) { | 
				
			||||
 | 
				
			||||
        int swingAnimation = 75; | 
				
			||||
 | 
				
			||||
        if (dpj != null) | 
				
			||||
            if (PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) | 
				
			||||
                swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); | 
				
			||||
 | 
				
			||||
        TargetedActionMsg cmm = new TargetedActionMsg(source, swingAnimation, target, passiveType); | 
				
			||||
        DispatchMessage.sendToAllInRange(target, cmm); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private static boolean testPassive(AbstractCharacter source, AbstractCharacter target, String type) { | 
				
			||||
 | 
				
			||||
        if(target.getBonuses() != null) | 
				
			||||
            if(target.getBonuses().getBool(Enum.ModType.Stunned, Enum.SourceType.None)) | 
				
			||||
                return false; | 
				
			||||
 | 
				
			||||
        float chance = target.getPassiveChance(type, source.getLevel(), true); | 
				
			||||
 | 
				
			||||
        if (chance == 0f) | 
				
			||||
            return false; | 
				
			||||
 | 
				
			||||
        //max 75% chance of passive to fire
 | 
				
			||||
 | 
				
			||||
        if (chance > 75f) | 
				
			||||
            chance = 75f; | 
				
			||||
 | 
				
			||||
        int roll = ThreadLocalRandom.current().nextInt(1,100); | 
				
			||||
 | 
				
			||||
        return roll < chance; | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    private static void createTimer(AbstractCharacter source, boolean mainhand) { | 
				
			||||
 | 
				
			||||
        ConcurrentHashMap<String, JobContainer> timers = source.getTimers(); | 
				
			||||
        int slot = 1; | 
				
			||||
        if(!mainhand) | 
				
			||||
            slot = 2; | 
				
			||||
 | 
				
			||||
        int time = 3000; | 
				
			||||
        if(source.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)){ | 
				
			||||
            PlayerCharacter pc = (PlayerCharacter)source; | 
				
			||||
            if(mainhand){ | 
				
			||||
                time = (int) pc.combatStats.attackSpeedHandOne; | 
				
			||||
            }else{ | 
				
			||||
                time = (int) pc.combatStats.attackSpeedHandTwo; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (timers != null) { | 
				
			||||
            AttackJob aj = new AttackJob(source, slot, true); | 
				
			||||
            JobContainer job; | 
				
			||||
            job = JobScheduler.getInstance().scheduleJob(aj, (time * 100)); | 
				
			||||
            timers.put("Attack" + slot, job); | 
				
			||||
        } else { | 
				
			||||
            Logger.error("Unable to find Timers for Character " + source.getObjectUUID()); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static void setAttackTarget(AttackCmdMsg msg, ClientConnection origin) throws MsgSendException { | 
				
			||||
 | 
				
			||||
        PlayerCharacter player; | 
				
			||||
        int targetType; | 
				
			||||
        AbstractWorldObject target; | 
				
			||||
 | 
				
			||||
        if (TargetedActionMsg.un2cnt == 60 || TargetedActionMsg.un2cnt == 70) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        player = SessionManager.getPlayerCharacter(origin); | 
				
			||||
 | 
				
			||||
        if (player == null) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        //source must match player this account belongs to
 | 
				
			||||
 | 
				
			||||
        if (player.getObjectUUID() != msg.getSourceID() || player.getObjectType().ordinal() != msg.getSourceType()) { | 
				
			||||
            Logger.error("Msg Source ID " + msg.getSourceID() + " Does not Match Player ID " + player.getObjectUUID()); | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        targetType = msg.getTargetType(); | 
				
			||||
 | 
				
			||||
        if (targetType == Enum.GameObjectType.PlayerCharacter.ordinal()) { | 
				
			||||
            target = PlayerCharacter.getFromCache(msg.getTargetID()); | 
				
			||||
        } else if (targetType == Enum.GameObjectType.Building.ordinal()) { | 
				
			||||
            target = BuildingManager.getBuildingFromCache(msg.getTargetID()); | 
				
			||||
        } else if (targetType == Enum.GameObjectType.Mob.ordinal()) { | 
				
			||||
            target = Mob.getFromCache(msg.getTargetID()); | 
				
			||||
        } else { | 
				
			||||
            player.setCombatTarget(null); | 
				
			||||
            return; //not valid type to attack
 | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // quit of the combat target is already the current combat target
 | 
				
			||||
        // or there is no combat target
 | 
				
			||||
 | 
				
			||||
        if (target == null) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        //set sources target
 | 
				
			||||
 | 
				
			||||
        player.setCombatTarget(target); | 
				
			||||
 | 
				
			||||
        boolean hasMain = false; | 
				
			||||
        boolean hasOff = false; | 
				
			||||
        if(player.getCharItemManager() != null && player.getCharItemManager().getEquipped() != null){ | 
				
			||||
            if(player.getCharItemManager().getEquipped(1) != null) | 
				
			||||
                hasMain = true; | 
				
			||||
            if(player.getCharItemManager().getEquipped(2) != null && !player.getCharItemManager().getEquipped(2).getItemBase().isShield()) | 
				
			||||
                hasOff = true; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if(hasMain){ | 
				
			||||
            createTimer(player,true); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if(hasOff){ | 
				
			||||
            createTimer(player,false); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue