forked from MagicBane/Server
				
			
				 1 changed files with 329 additions and 0 deletions
			
			
		| @ -0,0 +1,329 @@ | |||||||
|  | package engine.gameManager; | ||||||
|  | 
 | ||||||
|  | import engine.Enum; | ||||||
|  | import engine.jobs.DeferredPowerJob; | ||||||
|  | import engine.net.DispatchMessage; | ||||||
|  | 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)) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             if(source.getBonuses() != null) | ||||||
|  |                 if(!source.getBonuses().getBool(Enum.ModType.IgnorePassiveDefense, Enum.SourceType.None)) | ||||||
|  |                     if(triggerPassive(source,target)) | ||||||
|  |                         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; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue