forked from MagicBane/Server
				
			
				 2 changed files with 35 additions and 450 deletions
			
			
		| @ -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