diff --git a/src/engine/ai/MobileFSM.java b/src/engine/ai/MobileFSM.java index 5d722371..4a6e80fe 100644 --- a/src/engine/ai/MobileFSM.java +++ b/src/engine/ai/MobileFSM.java @@ -6,6 +6,7 @@ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.ai; + import engine.Enum; import engine.Enum.DispatchChannel; import engine.InterestManagement.WorldGrid; @@ -22,12 +23,15 @@ import engine.objects.*; import engine.powers.ActionsBase; import engine.powers.PowersBase; import engine.server.MBServerStatics; + import java.util.ArrayList; import java.util.HashSet; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; + import static engine.math.FastMath.sqr; + public class MobileFSM { private static void AttackTarget(Mob mob, AbstractWorldObject target) { if (mob == null) @@ -42,11 +46,11 @@ public class MobileFSM { case PlayerCharacter: PlayerCharacter targetPlayer = (PlayerCharacter) target; if (canCast(mob)) { - if (!MobCast(mob)) + if (canCast(mob) && !MobCast(mob)) AttackPlayer(mob, targetPlayer); - } else { + } else AttackPlayer(mob, targetPlayer); - } + break; case Building: Building targetBuilding = (Building) target; @@ -58,79 +62,117 @@ public class MobileFSM { break; } } + public static void AttackPlayer(Mob mob, PlayerCharacter target) { + if (mob.getMobBase().getSeeInvis() < target.getHidden() || !target.isAlive()) { mob.setCombatTarget(null); return; } + if (mob.BehaviourType.callsForHelp) MobCallForHelp(mob); + if (!MovementUtilities.inRangeDropAggro(mob, target)) { mob.setCombatTarget(null); return; } - if (CombatUtilities.inRangeToAttack(mob,mob.getCombatTarget())) { + if (CombatUtilities.inRangeToAttack(mob, mob.getCombatTarget())) { + //no weapons, default mob attack speed 3 seconds. + if (System.currentTimeMillis() < mob.getLastAttackTime()) return; + // ranged mobs cant attack while running. skip until they finally stop. + if (mob.isMoving()) return; + // add timer for last attack. + ItemBase mainHand = mob.getWeaponItemBase(true); ItemBase offHand = mob.getWeaponItemBase(false); + if (mainHand == null && offHand == null) { CombatUtilities.combatCycle(mob, target, true, null); + int delay = 3000; + if (mob.isSiege()) delay = 11000; + mob.setLastAttackTime(System.currentTimeMillis() + delay); + } else if (mob.getWeaponItemBase(true) != null) { + int delay = 3000; + if (mob.isSiege()) delay = 11000; + CombatUtilities.combatCycle(mob, target, true, mob.getWeaponItemBase(true)); mob.setLastAttackTime(System.currentTimeMillis() + delay); + } else if (mob.getWeaponItemBase(false) != null) { + int attackDelay = 3000; + if (mob.isSiege()) attackDelay = 11000; + CombatUtilities.combatCycle(mob, target, false, mob.getWeaponItemBase(false)); mob.setLastAttackTime(System.currentTimeMillis() + attackDelay); } } } + public static void AttackBuilding(Mob mob, Building target) { + if (target.getRank() == -1 || !target.isVulnerable() || BuildingManager.getBuildingFromCache(target.getObjectUUID()) == null) { mob.setCombatTarget(null); return; } + City playercity = ZoneManager.getCityAtLocation(mob.getLoc()); + if (playercity != null) for (Mob guard : playercity.getParent().zoneMobSet) if (guard.BehaviourType != null && guard.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal()) if (guard.getCombatTarget() == null && !guard.getGuild().equals(mob.getGuild())) guard.setCombatTarget(mob); + if (mob.isSiege()) MovementManager.sendRWSSMsg(mob); + ItemBase mainHand = mob.getWeaponItemBase(true); ItemBase offHand = mob.getWeaponItemBase(false); + if (mainHand == null && offHand == null) { CombatUtilities.combatCycle(mob, target, true, null); + int delay = 3000; + if (mob.isSiege()) delay = 15000; + mob.setLastAttackTime(System.currentTimeMillis() + delay); } else if (mob.getWeaponItemBase(true) != null) { + int attackDelay = 3000; + if (mob.isSiege()) attackDelay = 15000; + CombatUtilities.combatCycle(mob, target, true, mob.getWeaponItemBase(true)); mob.setLastAttackTime(System.currentTimeMillis() + attackDelay); } else if (mob.getWeaponItemBase(false) != null) { + int attackDelay = 3000; + if (mob.isSiege()) attackDelay = 15000; + CombatUtilities.combatCycle(mob, target, false, mob.getWeaponItemBase(false)); mob.setLastAttackTime(System.currentTimeMillis() + attackDelay); } @@ -140,67 +182,91 @@ public class MobileFSM { DispatchMessage.dispatchMsgToInterestArea(mob, ppm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); } } + public static void AttackMob(Mob mob, Mob target) { + if (mob.getRange() >= 30 && mob.isMoving()) return; + //no weapons, default mob attack speed 3 seconds. ItemBase mainHand = mob.getWeaponItemBase(true); ItemBase offHand = mob.getWeaponItemBase(false); + if (mainHand == null && offHand == null) { CombatUtilities.combatCycle(mob, target, true, null); + int delay = 3000; + if (mob.isSiege()) delay = 11000; + mob.setLastAttackTime(System.currentTimeMillis() + delay); } else if (mob.getWeaponItemBase(true) != null) { + int attackDelay = 3000; + if (mob.isSiege()) attackDelay = 11000; + CombatUtilities.combatCycle(mob, target, true, mob.getWeaponItemBase(true)); mob.setLastAttackTime(System.currentTimeMillis() + attackDelay); } else if (mob.getWeaponItemBase(false) != null) { + int attackDelay = 3000; + if (mob.isSiege()) attackDelay = 11000; + CombatUtilities.combatCycle(mob, target, false, mob.getWeaponItemBase(false)); mob.setLastAttackTime(System.currentTimeMillis() + attackDelay); } } + private static void Patrol(Mob mob) { //make sure mob is out of combat stance - if(mob.stopPatrolTime == 0) { + if (mob.stopPatrolTime == 0) mob.stopPatrolTime = System.currentTimeMillis(); - } - if(mob.isMoving() == true){ + + if (mob.isMoving() == true) { mob.stopPatrolTime = System.currentTimeMillis(); return; } + if (mob.isCombat() && mob.getCombatTarget() == null) { mob.setCombat(false); UpdateStateMsg rwss = new UpdateStateMsg(); rwss.setPlayer(mob); DispatchMessage.sendToAllInRange(mob, rwss); } + + //early exit while waiting to patrol again + if (mob.stopPatrolTime + (MBServerStatics.AI_PATROL_DIVISOR * 1000) > System.currentTimeMillis()) - //early exit while waiting to patrol again return; + //guard captains inherit barracks patrol points dynamically + if (mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal()) { + Building barracks = mob.building; + if (barracks != null && barracks.patrolPoints != null && !barracks.getPatrolPoints().isEmpty()) { mob.patrolPoints = barracks.patrolPoints; - } else{ + } else { randomGuardPatrolPoint(mob); return; } } - if (mob.lastPatrolPointIndex > mob.patrolPoints.size() - 1) { - mob.lastPatrolPointIndex = 0; - } + + if (mob.lastPatrolPointIndex > mob.patrolPoints.size() - 1) + mob.lastPatrolPointIndex = 0; + mob.destination = mob.patrolPoints.get(mob.lastPatrolPointIndex); mob.lastPatrolPointIndex += 1; + MovementUtilities.aiMove(mob, mob.destination, true); - if(mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal()){ + + if (mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal()) { for (Entry minion : mob.siegeMinionMap.entrySet()) { //make sure mob is out of combat stance if (minion.getKey().despawned == false) { @@ -215,117 +281,162 @@ public class MobileFSM { minion.getKey().updateLocation(); Vector3fImmutable formationPatrolPoint = new Vector3fImmutable(mob.destination.x + minionOffset.x, mob.destination.y, mob.destination.z + minionOffset.z); MovementUtilities.aiMove(minion.getKey(), formationPatrolPoint, true); - MovementUtilities.moveToLocation(minion.getKey(),formationPatrolPoint,0); + MovementUtilities.moveToLocation(minion.getKey(), formationPatrolPoint, 0); } } } } } + public static boolean canCast(Mob mob) { + // Performs validation to determine if a // mobile in the proper state to cast. + if (mob == null) return false; if (mob.mobPowers.isEmpty()) return false; - if(ThreadLocalRandom.current().nextInt(100) > 50) + + if (ThreadLocalRandom.current().nextInt(100) > 50) return false; + if (mob.nextCastTime == 0) mob.nextCastTime = System.currentTimeMillis(); + return mob.nextCastTime <= System.currentTimeMillis(); } + public static boolean MobCast(Mob mob) { + // Method picks a random spell from a mobile's list of powers // and casts it on the current target (or itself). Validation // (including empty lists) is done previously within canCast(); + ArrayList powerTokens; ArrayList purgeTokens; PlayerCharacter target = (PlayerCharacter) mob.getCombatTarget(); + if (mob.BehaviourType.callsForHelp) MobCallForHelp(mob); + // Generate a list of tokens from the mob powers for this mobile. + powerTokens = new ArrayList<>(mob.mobPowers.keySet()); purgeTokens = new ArrayList<>(); + // If player has this effect on them currently then remove // this token from our list. + for (int powerToken : powerTokens) { + PowersBase powerBase = PowersManager.getPowerByToken(powerToken); + for (ActionsBase actionBase : powerBase.getActions()) { String stackType = actionBase.stackType; + if (target.getEffects() != null && target.getEffects().containsKey(stackType)) purgeTokens.add(powerToken); } } + powerTokens.removeAll(purgeTokens); + // Sanity check + if (powerTokens.isEmpty()) return false; + // Pick random spell from our list of powers int powerToken = powerTokens.get(ThreadLocalRandom.current().nextInt(powerTokens.size())); int powerRank = mob.mobPowers.get(powerToken); + PowersBase mobPower = PowersManager.getPowerByToken(powerToken); + // Cast the spell + if (CombatUtilities.inRange2D(mob, mob.getCombatTarget(), mobPower.getRange())) { PowersManager.useMobPower(mob, (AbstractCharacter) mob.getCombatTarget(), mobPower, powerRank); PerformActionMsg msg; + if (!mobPower.isHarmful() || mobPower.targetSelf) msg = PowersManager.createPowerMsg(mobPower, powerRank, mob, mob); else msg = PowersManager.createPowerMsg(mobPower, powerRank, mob, target); + msg.setUnknown04(2); PowersManager.finishUseMobPower(msg, mob, 0, 0); - mob.nextCastTime = System.currentTimeMillis() + (MBServerStatics.AI_POWER_DIVISOR * 1000); + mob.nextCastTime = System.currentTimeMillis() + (MBServerStatics.AI_POWER_DIVISOR * 1000); return true; } return false; } + public static void MobCallForHelp(Mob mob) { + boolean callGotResponse = false; - if (mob.nextCallForHelp == 0) { + + if (mob.nextCallForHelp == 0) mob.nextCallForHelp = System.currentTimeMillis(); - } + if (mob.nextCallForHelp < System.currentTimeMillis()) return; + //mob sends call for help message ChatManager.chatSayInfo(null, mob.getName() + " calls for help!"); + Zone mobCamp = mob.getParentZone(); + for (Mob helper : mobCamp.zoneMobSet) { if (helper.BehaviourType.respondsToCallForHelp && helper.BehaviourType.BehaviourHelperType.equals(mob.BehaviourType)) { helper.setCombatTarget(mob.getCombatTarget()); callGotResponse = true; } } + + //wait 60 seconds to call for help again + if (callGotResponse) - //wait 60 seconds to call for help again mob.nextCallForHelp = System.currentTimeMillis() + 60000; } + public static void DetermineAction(Mob mob) { + if (mob == null) return; - if(mob.despawned == true || mob.isAlive() == false) { + + if (mob.despawned == true || mob.isAlive() == false) { + if (mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardMinion.ordinal()) { + + //minions don't respawn while guard captain is dead + if (mob.npcOwner.isAlive() == false || ((Mob) mob.npcOwner).despawned == true) { - //minions don't respawn while guard captain is dead - mob.deathTime = System.currentTimeMillis(); - return; + mob.deathTime = System.currentTimeMillis(); + return; } } CheckForRespawn(mob); return; } + + //no players loaded, no need to proceed + if (mob.playerAgroMap.isEmpty() && mob.isPlayerGuard == false) - //no players loaded, no need to proceed return; + if (mob.isCombat() && mob.getCombatTarget() == null) { mob.setCombat(false); UpdateStateMsg rwss = new UpdateStateMsg(); rwss.setPlayer(mob); DispatchMessage.sendToAllInRange(mob, rwss); } + mob.updateLocation(); CheckToSendMobHome(mob); + switch (mob.BehaviourType) { case GuardCaptain: GuardCaptainLogic(mob); @@ -347,81 +458,110 @@ public class MobileFSM { break; } } + private static void CheckForAggro(Mob aiAgent) { + //looks for and sets mobs combatTarget + if (!aiAgent.isAlive()) return; + ConcurrentHashMap loadedPlayers = aiAgent.playerAgroMap; + for (Entry playerEntry : loadedPlayers.entrySet()) { + int playerID = (int) playerEntry.getKey(); PlayerCharacter loadedPlayer = PlayerCharacter.getFromCache(playerID); + //Player is null, let's remove them from the list. + if (loadedPlayer == null) { loadedPlayers.remove(playerID); continue; } + //Player is Dead, Mob no longer needs to attempt to aggro. Remove them from aggro map. + if (!loadedPlayer.isAlive()) { loadedPlayers.remove(playerID); continue; } + //Can't see target, skip aggro. + if (!aiAgent.canSee(loadedPlayer)) continue; + // No aggro for this race type + if (aiAgent.notEnemy.contains(loadedPlayer.getRace().getRaceType().getMonsterType())) continue; + if (MovementUtilities.inRangeToAggro(aiAgent, loadedPlayer)) { aiAgent.setCombatTarget(loadedPlayer); return; } } } + private static void CheckMobMovement(Mob mob) { + if (!MovementUtilities.canMove(mob)) return; - switch(mob.BehaviourType){ + + switch (mob.BehaviourType) { case Pet1: + //mob no longer has its owner loaded, translocate pet to owner + if (!mob.playerAgroMap.containsKey(mob.getOwner().getObjectUUID())) { - //mob no longer has its owner loaded, translocate pet to owner MovementManager.translocate(mob, mob.getOwner().getLoc(), null); return; } + + //move back to owner + if (mob.getCombatTarget() == null) { + //move back to owner + if (CombatUtilities.inRange2D(mob, mob.getOwner(), 6)) return; + mob.destination = mob.getOwner().getLoc(); MovementUtilities.moveToLocation(mob, mob.destination, 5); } else chaseTarget(mob); break; case GuardMinion: - if (!mob.npcOwner.isAlive() || ((Mob)mob.npcOwner).despawned) + if (!mob.npcOwner.isAlive() || ((Mob) mob.npcOwner).despawned) randomGuardPatrolPoint(mob); - else{ - if(mob.getCombatTarget() != null){ + else { + if (mob.getCombatTarget() != null) chaseTarget(mob); - } } break; default: - if (mob.getCombatTarget() == null) { - Patrol(mob); - }else { + if (mob.getCombatTarget() == null) + Patrol(mob); + else chaseTarget(mob); - } + break; } } + private static void CheckForRespawn(Mob aiAgent) { + if (aiAgent.deathTime == 0) { aiAgent.setDeathTime(System.currentTimeMillis()); return; } + //handles checking for respawn of dead mobs even when no players have mob loaded //Despawn Timer with Loot currently in inventory. + if (!aiAgent.despawned) { + if (aiAgent.getCharItemManager().getInventoryCount() > 0) { if (System.currentTimeMillis() > aiAgent.deathTime + MBServerStatics.DESPAWN_TIMER_WITH_LOOT) { aiAgent.despawn(); @@ -429,12 +569,15 @@ public class MobileFSM { } //No items in inventory. } else { + //Mob's Loot has been looted. + if (aiAgent.isHasLoot()) { if (System.currentTimeMillis() > aiAgent.deathTime + MBServerStatics.DESPAWN_TIMER_ONCE_LOOTED) { aiAgent.despawn(); aiAgent.deathTime = System.currentTimeMillis(); } + //Mob never had Loot. } else { if (System.currentTimeMillis() > aiAgent.deathTime + MBServerStatics.DESPAWN_TIMER) { @@ -447,10 +590,14 @@ public class MobileFSM { aiAgent.respawn(); } } + public static void CheckForAttack(Mob mob) { + //checks if mob can attack based on attack timer and range + if (mob.isAlive() == false) return; + if (MovementUtilities.inRangeDropAggro(mob, (PlayerCharacter) mob.getCombatTarget()) == false) { mob.setCombatTarget(null); if (mob.isCombat()) { @@ -470,34 +617,42 @@ public class MobileFSM { if (System.currentTimeMillis() > mob.getLastAttackTime()) AttackTarget(mob, mob.getCombatTarget()); } + private static void CheckToSendMobHome(Mob mob) { + if (mob.isPlayerGuard() && !mob.despawned) { City current = ZoneManager.getCityAtLocation(mob.getLoc()); + if (current == null || current.equals(mob.getGuild().getOwnedCity()) == false || mob.playerAgroMap.isEmpty()) { PowersBase recall = PowersManager.getPowerByToken(-1994153779); PowersManager.useMobPower(mob, mob, recall, 40); mob.setCombatTarget(null); - if(mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal() && mob.isAlive()){ - //guard captain pulls his minions home with him + + //guard captain pulls his minions home with him + + if (mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal() && mob.isAlive()) { for (Entry minion : mob.siegeMinionMap.entrySet()) { PowersManager.useMobPower(minion.getKey(), minion.getKey(), recall, 40); minion.getKey().setCombatTarget(null); } } } - } - else if(MovementUtilities.inRangeOfBindLocation(mob) == false) { + } else if (MovementUtilities.inRangeOfBindLocation(mob) == false) { PowersBase recall = PowersManager.getPowerByToken(-1994153779); PowersManager.useMobPower(mob, mob, recall, 40); mob.setCombatTarget(null); } } + private static void chaseTarget(Mob mob) { + mob.updateMovementState(); - if(mob.playerAgroMap.containsKey(mob.getCombatTarget().getObjectUUID()) == false){ + + if (mob.playerAgroMap.containsKey(mob.getCombatTarget().getObjectUUID()) == false) { mob.setCombatTarget(null); return; } + if (CombatUtilities.inRange2D(mob, mob.getCombatTarget(), mob.getRange()) == false) { if (mob.getRange() > 15) { mob.destination = mob.getCombatTarget().getLoc(); @@ -508,179 +663,263 @@ public class MobileFSM { } } } + private static void SafeGuardAggro(Mob mob) { + HashSet awoList = WorldGrid.getObjectsInRangePartial(mob, 100, MBServerStatics.MASK_MOB); + for (AbstractWorldObject awoMob : awoList) { + //dont scan self. + if (mob.equals(awoMob)) continue; + Mob aggroMob = (Mob) awoMob; + //dont attack other guards + if (aggroMob.isGuard()) continue; + if (mob.getLoc().distanceSquared2D(aggroMob.getLoc()) > sqr(50)) continue; + mob.setCombatTarget(aggroMob); } } + public static void GuardCaptainLogic(Mob mob) { + if (mob.getCombatTarget() == null) CheckForPlayerGuardAggro(mob); + CheckMobMovement(mob); + if (mob.getCombatTarget() != null) CheckForAttack(mob); } + public static void GuardMinionLogic(Mob mob) { - if (!mob.npcOwner.isAlive() && mob.getCombatTarget() == null) { + + if (!mob.npcOwner.isAlive() && mob.getCombatTarget() == null) CheckForPlayerGuardAggro(mob); - } - if(mob.npcOwner.getCombatTarget() != null) + + if (mob.npcOwner.getCombatTarget() != null) mob.setCombatTarget(mob.npcOwner.getCombatTarget()); - else + else mob.setCombatTarget(null); + CheckMobMovement(mob); + if (mob.getCombatTarget() != null) CheckForAttack(mob); } + public static void GuardWallArcherLogic(Mob mob) { + if (mob.getCombatTarget() == null) CheckForPlayerGuardAggro(mob); else CheckForAttack(mob); } + private static void PetLogic(Mob mob) { + if (mob.getCombatTarget() != null && !mob.getCombatTarget().isAlive()) mob.setCombatTarget(null); + if (MovementUtilities.canMove(mob) && mob.BehaviourType.canRoam) CheckMobMovement(mob); + if (mob.getCombatTarget() != null) CheckForAttack(mob); } + private static void HamletGuardLogic(Mob mob) { + + //safehold guard + if (mob.getCombatTarget() == null) { - //safehold guard + SafeGuardAggro(mob); + if (mob.getCombatTarget() != null) CheckForAttack(mob); } } + private static void DefaultLogic(Mob mob) { - //check for players that can be aggroed if mob is agressive and has no target + + // check for players that can be aggroed if mob is agressive and has no target + if (mob.BehaviourType.isAgressive) { AbstractWorldObject newTarget = ChangeTargetFromHateValue(mob); if (newTarget != null) { mob.setCombatTarget(newTarget); } else { if (mob.getCombatTarget() == null) { + if (mob.BehaviourType == Enum.MobBehaviourType.HamletGuard) - //safehold guard - SafeGuardAggro(mob); + SafeGuardAggro(mob); // safehold guard else - //normal aggro - CheckForAggro(mob); + CheckForAggro(mob); // normal aggro } } } + //check if mob can move for patrol or moving to target + if (mob.BehaviourType.canRoam) CheckMobMovement(mob); + //check if mob can attack if it isn't wimpy + if (!mob.BehaviourType.isWimpy && !mob.isMoving() && mob.combatTarget != null) CheckForAttack(mob); - if(mob.combatTarget != null){ + + if (mob.combatTarget != null) { CheckForAttack(mob); } } + public static void CheckForPlayerGuardAggro(Mob mob) { + //looks for and sets mobs combatTarget + if (!mob.isAlive()) return; + ConcurrentHashMap loadedPlayers = mob.playerAgroMap; + for (Entry playerEntry : loadedPlayers.entrySet()) { + int playerID = (int) playerEntry.getKey(); PlayerCharacter loadedPlayer = PlayerCharacter.getFromCache(playerID); + //Player is null, let's remove them from the list. + if (loadedPlayer == null) { loadedPlayers.remove(playerID); continue; + } //Player is Dead, Mob no longer needs to attempt to aggro. Remove them from aggro map. + if (!loadedPlayer.isAlive()) { loadedPlayers.remove(playerID); continue; } + //Can't see target, skip aggro. + if (!mob.canSee(loadedPlayer)) continue; + // No aggro for this player + if (GuardCanAggro(mob, loadedPlayer) == false) continue; + if (MovementUtilities.inRangeToAggro(mob, loadedPlayer)) { mob.setCombatTarget(loadedPlayer); return; } } } + public static Boolean GuardCanAggro(Mob mob, PlayerCharacter target) { + if (mob.getGuild().getNation().equals(target.getGuild().getNation())) return false; + if (mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardMinion.ordinal()) { - if(((Mob)mob.npcOwner).building.getCity().cityOutlaws.contains(target.getObjectUUID()) == true){ + + if (((Mob) mob.npcOwner).building.getCity().cityOutlaws.contains(target.getObjectUUID()) == true) return true; - } - } else if(mob.building.getCity().cityOutlaws.contains(target.getObjectUUID()) == true){ + + } else if (mob.building.getCity().cityOutlaws.contains(target.getObjectUUID()) == true) { return true; } + //first check condemn list for aggro allowed (allies button is checked) + if (ZoneManager.getCityAtLocation(mob.getLoc()).getTOL().reverseKOS) { + for (Entry entry : ZoneManager.getCityAtLocation(mob.getLoc()).getTOL().getCondemned().entrySet()) { + + //target is listed individually if (entry.getValue().getPlayerUID() == target.getObjectUUID() && entry.getValue().isActive()) - //target is listed individually return false; + + //target's guild is listed + if (Guild.getGuild(entry.getValue().getGuildUID()) == target.getGuild()) - //target's guild is listed return false; + + //target's nation is listed + if (Guild.getGuild(entry.getValue().getGuildUID()) == target.getGuild().getNation()) - //target's nation is listed return false; } return true; - } else{ + } else { //allies button is not checked + for (Entry entry : ZoneManager.getCityAtLocation(mob.getLoc()).getTOL().getCondemned().entrySet()) { + + //target is listed individually + if (entry.getValue().getPlayerUID() == target.getObjectUUID() && entry.getValue().isActive()) - //target is listed individually return true; + + //target's guild is listed + if (Guild.getGuild(entry.getValue().getGuildUID()) == target.getGuild()) - //target's guild is listed return true; + + //target's nation is listed + if (Guild.getGuild(entry.getValue().getGuildUID()) == target.getGuild().getNation()) - //target's nation is listed return true; } } return false; } - public static void randomGuardPatrolPoint(Mob mob){ + + public static void randomGuardPatrolPoint(Mob mob) { + + //early exit for a mob who is already moving to a patrol point + //while mob moving, update lastPatrolTime so that when they stop moving the 10 second timer can begin + if (mob.isMoving() == true) { - //early exit for a mob who is already moving to a patrol point - //while mob moving, update lastPatrolTime so that when they stop moving the 10 second timer can begin mob.stopPatrolTime = System.currentTimeMillis(); return; } + //wait between 10 and 15 seconds after reaching patrol point before moving + int patrolDelay = ThreadLocalRandom.current().nextInt(10000) + 5000; + + //early exit while waiting to patrol again + if (mob.stopPatrolTime + patrolDelay > System.currentTimeMillis()) - //early exit while waiting to patrol again return; + float xPoint = ThreadLocalRandom.current().nextInt(400) - 200; float zPoint = ThreadLocalRandom.current().nextInt(400) - 200; + Vector3fImmutable TreePos = mob.getGuild().getOwnedCity().getLoc(); - mob.destination = new Vector3fImmutable(TreePos.x + xPoint,TreePos.y,TreePos.z + zPoint); + + mob.destination = new Vector3fImmutable(TreePos.x + xPoint, TreePos.y, TreePos.z + zPoint); MovementUtilities.aiMove(mob, mob.destination, true); - if(mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal()){ + + if (mob.BehaviourType.ordinal() == Enum.MobBehaviourType.GuardCaptain.ordinal()) { + for (Entry minion : mob.siegeMinionMap.entrySet()) { + //make sure mob is out of combat stance + if (minion.getKey().despawned == false) { if (minion.getKey().isCombat() && minion.getKey().getCombatTarget() == null) { minion.getKey().setCombat(false); @@ -698,18 +937,24 @@ public class MobileFSM { } } } - public static AbstractWorldObject ChangeTargetFromHateValue(Mob mob){ + + public static AbstractWorldObject ChangeTargetFromHateValue(Mob mob) { + float CurrentHateValue = 0; - if(mob.getCombatTarget() != null && mob.getCombatTarget().getObjectType().equals(Enum.GameObjectType.PlayerCharacter)){ - CurrentHateValue = ((PlayerCharacter)mob.getCombatTarget()).getHateValue(); - } + + if (mob.getCombatTarget() != null && mob.getCombatTarget().getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) + CurrentHateValue = ((PlayerCharacter) mob.getCombatTarget()).getHateValue(); + AbstractWorldObject mostHatedTarget = null; + for (Entry playerEntry : mob.playerAgroMap.entrySet()) { - PlayerCharacter potentialTarget = PlayerCharacter.getFromCache((int)playerEntry.getKey()); - if(potentialTarget.equals(mob.getCombatTarget())){ + + PlayerCharacter potentialTarget = PlayerCharacter.getFromCache((int) playerEntry.getKey()); + + if (potentialTarget.equals(mob.getCombatTarget())) continue; - } - if(potentialTarget != null && potentialTarget.getHateValue() > CurrentHateValue){ + + if (potentialTarget != null && potentialTarget.getHateValue() > CurrentHateValue) { CurrentHateValue = potentialTarget.getHateValue(); mostHatedTarget = potentialTarget; } diff --git a/src/engine/ai/MobileFSMManager.java b/src/engine/ai/MobileFSMManager.java index 3073e877..60a16d4c 100644 --- a/src/engine/ai/MobileFSMManager.java +++ b/src/engine/ai/MobileFSMManager.java @@ -8,6 +8,7 @@ package engine.ai; + import engine.gameManager.SessionManager; import engine.gameManager.ZoneManager; import engine.objects.Mob; @@ -22,90 +23,84 @@ import java.time.Instant; public class MobileFSMManager { - private static final MobileFSMManager INSTANCE = new MobileFSMManager(); - - private volatile boolean alive; - private long timeOfKill = -1; - - public static Duration executionTime = Duration.ofNanos(1); - public static Duration executionMax = Duration.ofNanos(1); - - private MobileFSMManager() { + private static final MobileFSMManager INSTANCE = new MobileFSMManager(); + public static Duration executionTime = Duration.ofNanos(1); + public static Duration executionMax = Duration.ofNanos(1); + private volatile boolean alive; + private long timeOfKill = -1; - Runnable worker = new Runnable() { - @Override - public void run() { - execution(); - } - }; + private MobileFSMManager() { - alive = true; + Runnable worker = new Runnable() { + @Override + public void run() { + execution(); + } + }; - Thread t = new Thread(worker, "MobileFSMManager"); - t.start(); - } + alive = true; - public static MobileFSMManager getInstance() { - return INSTANCE; - } + Thread t = new Thread(worker, "MobileFSMManager"); + t.start(); + } - /** - * Stops the MobileFSMManager - */ - public void shutdown() { - if (alive) { - alive = false; - timeOfKill = System.currentTimeMillis(); - } - } + public static MobileFSMManager getInstance() { + return INSTANCE; + } + /** + * Stops the MobileFSMManager + */ + public void shutdown() { + if (alive) { + alive = false; + timeOfKill = System.currentTimeMillis(); + } + } - public long getTimeOfKill() { - return this.timeOfKill; - } - public boolean isAlive() { - return this.alive; - } + public boolean isAlive() { + return this.alive; + } - private void execution() { + private void execution() { - //Load zone threshold once. + //Load zone threshold once. - long mobPulse = System.currentTimeMillis() + MBServerStatics.AI_PULSE_MOB_THRESHOLD; - Instant startTime; + long mobPulse = System.currentTimeMillis() + MBServerStatics.AI_PULSE_MOB_THRESHOLD; + Instant startTime; - while (alive) { + while (alive) { - ThreadUtils.sleep(1); + ThreadUtils.sleep(1); - if (System.currentTimeMillis() > mobPulse) { + if (System.currentTimeMillis() > mobPulse) { - startTime = Instant.now(); + startTime = Instant.now(); - for (Zone zone : ZoneManager.getAllZones()) { + for (Zone zone : ZoneManager.getAllZones()) { - for (Mob mob : zone.zoneMobSet) { + for (Mob mob : zone.zoneMobSet) { - try { - if (mob != null && SessionManager.getActivePlayerCharacterCount() > 0) - MobileFSM.DetermineAction(mob); - } catch (Exception e) { - Logger.error("Mob: " + mob.getName() + " UUID: " + mob.getObjectUUID() + " ERROR: " + e); - e.printStackTrace(); - } - } - } + try { + if (mob != null && SessionManager.getActivePlayerCharacterCount() > 0) + MobileFSM.DetermineAction(mob); + } catch (Exception e) { + Logger.error("Mob: " + mob.getName() + " UUID: " + mob.getObjectUUID() + " ERROR: " + e); + e.printStackTrace(); + } + } + } - this.executionTime = Duration.between(startTime, Instant.now()); + executionTime = Duration.between(startTime, Instant.now()); - if (executionTime.compareTo(executionMax) > 0) - executionMax = executionTime; + if (executionTime.compareTo(executionMax) > 0) + executionMax = executionTime; - mobPulse = System.currentTimeMillis() + MBServerStatics.AI_PULSE_MOB_THRESHOLD; - } - } - } + mobPulse = System.currentTimeMillis() + MBServerStatics.AI_PULSE_MOB_THRESHOLD; + } + } + } } diff --git a/src/engine/ai/utilities/CombatUtilities.java b/src/engine/ai/utilities/CombatUtilities.java index 55cbb098..91f2ca32 100644 --- a/src/engine/ai/utilities/CombatUtilities.java +++ b/src/engine/ai/utilities/CombatUtilities.java @@ -7,7 +7,6 @@ // www.magicbane.com - package engine.ai.utilities; import engine.Enum.*; @@ -27,479 +26,482 @@ import static java.lang.Math.pow; public class CombatUtilities { - public static boolean inRangeToAttack(Mob agent,AbstractWorldObject target){ - - if (Float.isNaN(agent.getLoc().x)) - return false; - - try{ - Vector3fImmutable sl = agent.getLoc(); - Vector3fImmutable tl = target.getLoc(); - - //add Hitbox's to range. - float range = agent.getRange(); - range += CombatManager.calcHitBox(target) + CombatManager.calcHitBox(agent); - //if (target instanceof AbstractCharacter) - // if (((AbstractCharacter)target).isMoving()) - // range+= 5; - - return !(sl.distanceSquared(tl) > sqr(range)); - }catch(Exception e){ - Logger.error( e.toString()); - return false; - } - - } - public static boolean inRangeToAttack2D(Mob agent,AbstractWorldObject target){ - - if (Float.isNaN(agent.getLoc().x)) - return false; - - try{ - Vector3fImmutable sl = agent.getLoc(); - Vector3fImmutable tl = target.getLoc(); - - //add Hitbox's to range. - float range = agent.getRange(); - range += CombatManager.calcHitBox(target) + CombatManager.calcHitBox(agent); - //if (target instanceof AbstractCharacter) - // if (((AbstractCharacter)target).isMoving()) - // range+= 5; - - return !(sl.distanceSquared2D(tl) > sqr(range)); - }catch(Exception e){ - Logger.error( e.toString()); - return false; - } - - } - public static boolean inRange2D(AbstractWorldObject entity1, AbstractWorldObject entity2, double range){ - return entity1.getLoc().distance2D(entity2.getLoc()) < range; - } - public static void swingIsBlock(Mob agent,AbstractWorldObject target, int animation) { - - if (!target.isAlive()) - return; - - TargetedActionMsg msg = new TargetedActionMsg(agent,animation, target, MBServerStatics.COMBAT_SEND_BLOCK); - - if (target.getObjectType() == GameObjectType.PlayerCharacter) - DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); - else - DispatchMessage.sendToAllInRange(agent,msg); - - } - public static void swingIsParry(Mob agent,AbstractWorldObject target, int animation) { - - if (!target.isAlive()) - return; - - TargetedActionMsg msg = new TargetedActionMsg(agent,animation, target, MBServerStatics.COMBAT_SEND_PARRY); - - if (target.getObjectType() == GameObjectType.PlayerCharacter) - DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); - else - DispatchMessage.sendToAllInRange(agent,msg); - - } - public static void swingIsDodge(Mob agent,AbstractWorldObject target, int animation) { - - if (!target.isAlive()) - return; - - TargetedActionMsg msg = new TargetedActionMsg(agent,animation, target, MBServerStatics.COMBAT_SEND_DODGE); - - if (target.getObjectType() == GameObjectType.PlayerCharacter) - DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); - else - DispatchMessage.sendToAllInRange(agent,msg); - } - public static void swingIsDamage(Mob agent, AbstractWorldObject target, float damage, int animation){ - if(agent.isSiege() == true){ - damage = ThreadLocalRandom.current().nextInt(1000) + 1500; - } - float trueDamage = damage; - //target = agent.getCombatTarget(); - if (!target.isAlive()) - return; - - if (AbstractWorldObject.IsAbstractCharacter(target)) - trueDamage = ((AbstractCharacter) target).modifyHealth(-damage, agent, false); - else if (target.getObjectType() == GameObjectType.Building) - trueDamage = ((Building) target).modifyHealth(-damage, agent); - - //Don't send 0 damage kay thanx. - - if (trueDamage == 0) - return; - - TargetedActionMsg msg = new TargetedActionMsg(agent,target, damage, animation); - - if (target.getObjectType() == GameObjectType.PlayerCharacter) - DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); - else - DispatchMessage.sendToAllInRange(agent,msg); - - //check damage shields - if(AbstractWorldObject.IsAbstractCharacter(target) && target.isAlive() && target.getObjectType() != GameObjectType.Mob) - CombatManager.handleDamageShields(agent,(AbstractCharacter)target, damage); - } - public static boolean canSwing(Mob agent) { - return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None)); - } - public static void swingIsMiss(Mob agent,AbstractWorldObject target, int animation) { - - TargetedActionMsg msg = new TargetedActionMsg(agent,target, 0f, animation); - - if (target.getObjectType() == GameObjectType.PlayerCharacter) - DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true,false); - else - DispatchMessage.sendToAllInRange(agent,msg); - - } - public static boolean triggerDefense(Mob agent, AbstractWorldObject target) { - int defenseScore = 0; - int attackScore = agent.getAtrHandOne(); - switch (target.getObjectType()) { - case PlayerCharacter: - defenseScore = ((AbstractCharacter) target).getDefenseRating(); - break; - case Mob: - - Mob mob = (Mob)target; - if (mob.isSiege()) - defenseScore = attackScore; - break; - case Building: - return false; - } - - - - int hitChance; - if (attackScore > defenseScore || defenseScore == 0) - hitChance = 94; - else if (attackScore == defenseScore && target.getObjectType() == GameObjectType.Mob) - hitChance = 10; - else { - float dif = attackScore / defenseScore; - if (dif <= 0.8f) - hitChance = 4; - else - hitChance = ((int)(450 * (dif - 0.8f)) + 4); - if (target.getObjectType() == GameObjectType.Building) - hitChance = 100; - } - return ThreadLocalRandom.current().nextInt(100) > hitChance; - } - public static boolean triggerBlock(Mob agent,AbstractWorldObject ac) { - return triggerPassive(agent,ac, "Block"); - } - public static boolean triggerParry(Mob agent,AbstractWorldObject ac) { - return triggerPassive(agent,ac, "Parry"); - } - public static boolean triggerDodge(Mob agent,AbstractWorldObject ac) { - return triggerPassive(agent,ac, "Dodge"); - } - public static boolean triggerPassive(Mob agent,AbstractWorldObject ac, String type) { - float chance = 0; - if (AbstractWorldObject.IsAbstractCharacter(ac)) - chance = ((AbstractCharacter)ac).getPassiveChance(type, agent.getLevel(), true); - - if (chance > 75f) - chance = 75f; - if (agent.isSiege() && AbstractWorldObject.IsAbstractCharacter(ac)) - chance = 100; - - return ThreadLocalRandom.current().nextInt(100) < chance; - } - public static void combatCycle(Mob agent,AbstractWorldObject target, boolean mainHand, ItemBase wb) { - - if (!agent.isAlive() || !target.isAlive()) return; - - if (target.getObjectType() == GameObjectType.PlayerCharacter) - if (!((PlayerCharacter)target).isActive()) - return; - - int anim = 75; - float speed = 30f; - if (mainHand) - speed = agent.getSpeedHandOne(); - else - speed = agent.getSpeedHandTwo(); - DamageType dt = DamageType.Crush; - if (agent.isSiege()) - dt = DamageType.Siege; - if (wb != null) { - anim = CombatManager.getSwingAnimation(wb, null,mainHand); - dt = wb.getDamageType(); - } else if (!mainHand) - return; - Resists res = null; - PlayerBonuses bonus = null; - switch(target.getObjectType()){ - case Building: - res = ((Building)target).getResists(); - break; - case PlayerCharacter: - res = ((PlayerCharacter)target).getResists(); - bonus = ((PlayerCharacter)target).getBonuses(); - break; - case Mob: - Mob mob = (Mob)target; - res = mob.getResists(); - bonus = ((Mob)target).getBonuses(); - break; - } - - //must not be immune to all or immune to attack - - if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.ImmuneToAttack)) - if (res != null &&(res.immuneToAll() || res.immuneToAttacks() || res.immuneTo(dt))) - return; - - int passiveAnim = CombatManager.getSwingAnimation(wb, null,mainHand); - if(canSwing(agent)) { - if(triggerDefense(agent,target)) { - swingIsMiss(agent, target, passiveAnim); - return; - } - else if(triggerDodge(agent,target)) { - swingIsDodge(agent, target, passiveAnim); - return; - } - else if (triggerParry(agent, target)){ - swingIsParry(agent, target, passiveAnim); - - return; - } - else if(triggerBlock(agent,target)) { - swingIsBlock(agent, target, passiveAnim); - return; - } - if(agent.getEquip().get(1) != null && agent.getEquip().get(2) != null && agent.getEquip().get(2).getItemBase().isShield() == false){ - //mob is duel wielding and should conduct an attack for each hand - ItemBase weapon1 = agent.getEquip().get(1).getItemBase(); - double range1 = getMaxDmg(weapon1.getMinDamage(),agent,weapon1) - getMinDmg(weapon1.getMinDamage(),agent,weapon1); - double damage1 = getMinDmg(weapon1.getMinDamage(),agent,weapon1) + ((ThreadLocalRandom.current().nextFloat() * range1) + (ThreadLocalRandom.current().nextFloat() * range1)) / 2; - swingIsDamage(agent,target, (float) damage1, CombatManager.getSwingAnimation(weapon1, null,true)); - ItemBase weapon2 = agent.getEquip().get(2).getItemBase(); - double range2 = getMaxDmg(weapon2.getMinDamage(),agent,weapon2) - getMinDmg(weapon2.getMinDamage(),agent,weapon2); - double damage2 = getMinDmg(weapon2.getMinDamage(),agent,weapon2) + ((ThreadLocalRandom.current().nextFloat() * range2) + (ThreadLocalRandom.current().nextFloat() * range2)) / 2; - swingIsDamage(agent,target, (float) damage2, CombatManager.getSwingAnimation(weapon1, null,false)); - } else{ - swingIsDamage(agent,target, determineDamage(agent), anim); - } - - if (agent.getWeaponPower() != null) - agent.getWeaponPower().attack(target, MBServerStatics.ONE_MINUTE); - } - - if (target.getObjectType().equals(GameObjectType.PlayerCharacter)){ - PlayerCharacter player = (PlayerCharacter)target; - if (player.getDebug(64)){ - ChatManager.chatSayInfo(player, "Debug Combat: Mob UUID " + agent.getObjectUUID() + " || Building ID = " + agent.getBuildingID() + " || Floor = " + agent.getInFloorID() + " || Level = " + agent.getInBuilding() );//combat debug - } - } - - //SIEGE MONSTERS DO NOT ATTACK GUARDSs - if (target.getObjectType() == GameObjectType.Mob) - if (((Mob)target).isSiege()) - return; - - //handle the retaliate - - if (AbstractWorldObject.IsAbstractCharacter(target)) - CombatManager.handleRetaliate((AbstractCharacter)target, agent); - - if (target.getObjectType() == GameObjectType.Mob){ - Mob targetMob = (Mob)target; - if (targetMob.isSiege()) - return; - } - - - } - public static float determineDamage(Mob agent) { - if(agent == null){ - //early exit for null - return 0; - } - AbstractWorldObject target = agent.getCombatTarget(); - if(target == null){ - //early exit for null target - return 0; - } - float damage = 0; - DamageType dt = getDamageType(agent); - if(agent.isSummonedPet() == true || agent.isPet() == true || agent.isNecroPet() == true) { - damage = calculatePetDamage(agent); - }else if(agent.isPlayerGuard() == true){ - //damage = calculateGuardDamage(agent); - damage = calculateMobDamage(agent); - }else if (agent.getLevel() > 80) { - damage = calculateEpicDamage(agent); - } else{ - damage = calculateMobDamage(agent); - } - if (AbstractWorldObject.IsAbstractCharacter(target)) { - if (((AbstractCharacter) target).isSit()) { - damage *= 2.5f; //increase damage if sitting - } - return (int) (((AbstractCharacter) target).getResists().getResistedDamage(agent, (AbstractCharacter) target, dt, damage, 0)); - } - if (target.getObjectType() == GameObjectType.Building) { - Building building = (Building) target; - Resists resists = building.getResists(); - return (int) ((damage * (1 - (resists.getResist(dt, 0) / 100)))); - } - return damage; - } - public static DamageType getDamageType(Mob agent){ - DamageType dt = DamageType.Crush; - if(agent.getEquip().get(1) != null ){ - return agent.getEquip().get(1).getItemBase().getDamageType(); - } - if(agent.getEquip().get(2) != null && agent.getEquip().get(2).getItemBase().isShield() == false){ - return agent.getEquip().get(2).getItemBase().getDamageType(); - } - return dt; - } - public static int calculatePetDamage(Mob agent) { - //damage calc for pet - float range; - float damage; - float min = 40; - float max = 60; - AbstractWorldObject target = agent.getCombatTarget(); - float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); - float str = agent.getStatStrCurrent(); - float dex = agent.getStatDexCurrent(); - double minDmg = getMinDmg(min,agent, null); - double maxDmg = getMaxDmg(max,agent, null); - dmgMultiplier += agent.getLevel() / 10; - range = (float) (maxDmg - minDmg); - damage = min + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; - return (int) (damage * dmgMultiplier); - } - public static int calculateGuardDamage(Mob agent){ - //damage calc for guard - ItemBase weapon = agent.getEquip().get(1).getItemBase(); - AbstractWorldObject target = agent.getCombatTarget(); - float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); - double minDmg = weapon.getMinDamage(); - double maxDmg = weapon.getMaxDamage(); - float str = agent.getStatStrCurrent(); - float dex = agent.getStatDexCurrent(); - double min = getMinDmg(minDmg,agent, weapon); - double max = getMaxDmg(maxDmg,agent, weapon); - DamageType dt = weapon.getDamageType(); - double range = max - min; - double damage = min + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; - if (AbstractWorldObject.IsAbstractCharacter(target)) - if (((AbstractCharacter) target).isSit()) - damage *= 2.5f; //increase damage if sitting - if (AbstractWorldObject.IsAbstractCharacter(target)) - return (int) (((AbstractCharacter) target).getResists().getResistedDamage((AbstractCharacter) agent, (AbstractCharacter) target, dt, (float) damage, 0) * dmgMultiplier); - return 0; - } - public static int calculateEpicDamage(Mob agent){ - //handle r8 mob damage - DamageType dt = DamageType.Crush; - AbstractWorldObject target = agent.getCombatTarget(); - float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); - if (agent.getEquip().get(1).getItemBase() != null) { - dt = agent.getEquip().get(1).getItemBase().getDamageType(); - } else if(agent.getEquip().get(2).getItemBase() != null && agent.getEquip().get(2).getItemBase().isShield() == false){ - dt = agent.getEquip().get(2).getItemBase().getDamageType(); - } - double min = agent.getMobBase().getMinDmg(); - double max = agent.getMobBase().getMaxDmg(); - double range = max - min; - double damage = min + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; - return (int) (((AbstractCharacter) target).getResists().getResistedDamage((AbstractCharacter) agent, (AbstractCharacter) target, dt, (float) damage, 0) * dmgMultiplier); - } - public static int calculateMobDamage(Mob agent){ - ItemBase weapon = null; - double minDmg; - double maxDmg; - DamageType dt; - if(agent.getEquip().get(1) != null){ - //mainhand damage - weapon = agent.getEquip().get(1).getItemBase(); - } else if(agent.getEquip().get(2) != null){ - //offhand damage - weapon = agent.getEquip().get(2).getItemBase(); - } - if(weapon != null){ - minDmg = getMinDmg(weapon.getMinDamage(),agent,weapon); - maxDmg = getMaxDmg(weapon.getMaxDamage(),agent, weapon); - dt = weapon.getDamageType(); - }else{ - minDmg = agent.getMobBase().getDamageMin(); - maxDmg = agent.getMobBase().getDamageMax(); - dt = DamageType.Crush; - } - AbstractWorldObject target = agent.getCombatTarget(); - float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); - double range = maxDmg - minDmg; - double damage = minDmg + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; - if (AbstractWorldObject.IsAbstractCharacter(target)) - if (((AbstractCharacter) target).isSit()) - damage *= 2.5f; //increase damage if sitting - if (AbstractWorldObject.IsAbstractCharacter(target)) - return (int) (((AbstractCharacter) target).getResists().getResistedDamage((AbstractCharacter) agent, (AbstractCharacter) target, dt, (float) damage, 0) * dmgMultiplier); - return 0; - } - public static double getMinDmg(double min, Mob agent, ItemBase weapon){ - int primary = agent.getStatStrCurrent(); - int secondary = agent.getStatDexCurrent(); - int focusLevel = 0; - int masteryLevel = 0; - if(weapon != null) { - if (weapon.isStrBased() == true) { - primary = agent.getStatStrCurrent(); - secondary = agent.getStatDexCurrent(); - } else { - primary = agent.getStatDexCurrent(); - secondary = agent.getStatStrCurrent(); - if (agent.getSkills().containsKey(weapon.getSkillRequired())) { - focusLevel = (int) agent.getSkills().get(weapon.getSkillRequired()).getModifiedAmount(); - } - if (agent.getSkills().containsKey(weapon.getMastery())) { - masteryLevel = (int) agent.getSkills().get(weapon.getMastery()).getModifiedAmount(); - } - } - } - return min * (pow(0.0048*primary +.049*(primary-0.75),0.5) + pow(0.0066*secondary + 0.064*(secondary-0.75),0.5) + + 0.01*(focusLevel + masteryLevel)); - } - public static double getMaxDmg(double max, Mob agent, ItemBase weapon){ - int primary = agent.getStatStrCurrent(); - int secondary = agent.getStatDexCurrent(); - int focusLevel = 0; - int masteryLevel = 0; - if (weapon != null) { - if(weapon.isStrBased() == true){ - primary = agent.getStatStrCurrent(); - secondary = agent.getStatDexCurrent(); - } - else{ - primary = agent.getStatDexCurrent(); - secondary = agent.getStatStrCurrent(); - } - - if(agent.getSkills().containsKey(weapon.getSkillRequired())) { - focusLevel = (int) agent.getSkills().get(weapon.getSkillRequired()).getModifiedAmount(); - } - if(agent.getSkills().containsKey(weapon.getSkillRequired())) { - masteryLevel = (int) agent.getSkills().get(weapon.getMastery()).getModifiedAmount(); - } - } - return max * (pow(0.0124*primary + 0.118*(primary -0.75),0.5) + pow(0.0022*secondary + 0.028*(secondary-0.75),0.5) + 0.0075*(focusLevel + masteryLevel)); - } - public static boolean RunAIRandom(){ - int random = ThreadLocalRandom.current().nextInt(4); - - return random == 0; - } + public static boolean inRangeToAttack(Mob agent, AbstractWorldObject target) { + + if (Float.isNaN(agent.getLoc().x)) + return false; + + try { + Vector3fImmutable sl = agent.getLoc(); + Vector3fImmutable tl = target.getLoc(); + + //add Hitbox's to range. + float range = agent.getRange(); + range += CombatManager.calcHitBox(target) + CombatManager.calcHitBox(agent); + + return !(sl.distanceSquared(tl) > sqr(range)); + } catch (Exception e) { + Logger.error(e.toString()); + return false; + } + + } + + public static boolean inRange2D(AbstractWorldObject entity1, AbstractWorldObject entity2, double range) { + return entity1.getLoc().distance2D(entity2.getLoc()) < range; + } + + public static void swingIsBlock(Mob agent, AbstractWorldObject target, int animation) { + + if (!target.isAlive()) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent, animation, target, MBServerStatics.COMBAT_SEND_BLOCK); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + DispatchMessage.sendToAllInRange(agent, msg); + + } + + public static void swingIsParry(Mob agent, AbstractWorldObject target, int animation) { + + if (!target.isAlive()) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent, animation, target, MBServerStatics.COMBAT_SEND_PARRY); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + DispatchMessage.sendToAllInRange(agent, msg); + + } + + public static void swingIsDodge(Mob agent, AbstractWorldObject target, int animation) { + + if (!target.isAlive()) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent, animation, target, MBServerStatics.COMBAT_SEND_DODGE); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + DispatchMessage.sendToAllInRange(agent, msg); + } + + public static void swingIsDamage(Mob agent, AbstractWorldObject target, float damage, int animation) { + if (agent.isSiege() == true) { + damage = ThreadLocalRandom.current().nextInt(1000) + 1500; + } + float trueDamage = damage; + + if (!target.isAlive()) + return; + + if (AbstractWorldObject.IsAbstractCharacter(target)) + trueDamage = ((AbstractCharacter) target).modifyHealth(-damage, agent, false); + else if (target.getObjectType() == GameObjectType.Building) + trueDamage = ((Building) target).modifyHealth(-damage, agent); + + //Don't send 0 damage kay thanx. + + if (trueDamage == 0) + return; + + TargetedActionMsg msg = new TargetedActionMsg(agent, target, damage, animation); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + DispatchMessage.sendToAllInRange(agent, msg); + + //check damage shields + if (AbstractWorldObject.IsAbstractCharacter(target) && target.isAlive() && target.getObjectType() != GameObjectType.Mob) + CombatManager.handleDamageShields(agent, (AbstractCharacter) target, damage); + } + + public static boolean canSwing(Mob agent) { + return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None)); + } + + public static void swingIsMiss(Mob agent, AbstractWorldObject target, int animation) { + + TargetedActionMsg msg = new TargetedActionMsg(agent, target, 0f, animation); + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + DispatchMessage.dispatchMsgToInterestArea(target, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + else + DispatchMessage.sendToAllInRange(agent, msg); + + } + + public static boolean triggerDefense(Mob agent, AbstractWorldObject target) { + int defenseScore = 0; + int attackScore = agent.getAtrHandOne(); + switch (target.getObjectType()) { + case PlayerCharacter: + defenseScore = ((AbstractCharacter) target).getDefenseRating(); + break; + case Mob: + + Mob mob = (Mob) target; + if (mob.isSiege()) + defenseScore = attackScore; + break; + case Building: + return false; + } + + int hitChance; + if (attackScore > defenseScore || defenseScore == 0) + hitChance = 94; + else if (attackScore == defenseScore && target.getObjectType() == GameObjectType.Mob) + hitChance = 10; + else { + float dif = attackScore / defenseScore; + if (dif <= 0.8f) + hitChance = 4; + else + hitChance = ((int) (450 * (dif - 0.8f)) + 4); + if (target.getObjectType() == GameObjectType.Building) + hitChance = 100; + } + return ThreadLocalRandom.current().nextInt(100) > hitChance; + } + + public static boolean triggerBlock(Mob agent, AbstractWorldObject ac) { + return triggerPassive(agent, ac, "Block"); + } + + public static boolean triggerParry(Mob agent, AbstractWorldObject ac) { + return triggerPassive(agent, ac, "Parry"); + } + + public static boolean triggerDodge(Mob agent, AbstractWorldObject ac) { + return triggerPassive(agent, ac, "Dodge"); + } + + public static boolean triggerPassive(Mob agent, AbstractWorldObject ac, String type) { + float chance = 0; + if (AbstractWorldObject.IsAbstractCharacter(ac)) + chance = ((AbstractCharacter) ac).getPassiveChance(type, agent.getLevel(), true); + + if (chance > 75f) + chance = 75f; + if (agent.isSiege() && AbstractWorldObject.IsAbstractCharacter(ac)) + chance = 100; + + return ThreadLocalRandom.current().nextInt(100) < chance; + } + + public static void combatCycle(Mob agent, AbstractWorldObject target, boolean mainHand, ItemBase wb) { + + if (!agent.isAlive() || !target.isAlive()) + return; + + if (target.getObjectType() == GameObjectType.PlayerCharacter) + if (!((PlayerCharacter) target).isActive()) + return; + + int anim = 75; + float speed; + + if (mainHand) + speed = agent.getSpeedHandOne(); + else + speed = agent.getSpeedHandTwo(); + + DamageType dt = DamageType.Crush; + + if (agent.isSiege()) + dt = DamageType.Siege; + if (wb != null) { + anim = CombatManager.getSwingAnimation(wb, null, mainHand); + dt = wb.getDamageType(); + } else if (!mainHand) + return; + + Resists res = null; + PlayerBonuses bonus = null; + + switch (target.getObjectType()) { + case Building: + res = ((Building) target).getResists(); + break; + case PlayerCharacter: + res = ((PlayerCharacter) target).getResists(); + bonus = ((PlayerCharacter) target).getBonuses(); + break; + case Mob: + Mob mob = (Mob) target; + res = mob.getResists(); + bonus = ((Mob) target).getBonuses(); + break; + } + + //must not be immune to all or immune to attack + + if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.ImmuneToAttack)) + if (res != null && (res.immuneToAll() || res.immuneToAttacks() || res.immuneTo(dt))) + return; + + int passiveAnim = CombatManager.getSwingAnimation(wb, null, mainHand); + if (canSwing(agent)) { + if (triggerDefense(agent, target)) { + swingIsMiss(agent, target, passiveAnim); + return; + } else if (triggerDodge(agent, target)) { + swingIsDodge(agent, target, passiveAnim); + return; + } else if (triggerParry(agent, target)) { + swingIsParry(agent, target, passiveAnim); + + return; + } else if (triggerBlock(agent, target)) { + swingIsBlock(agent, target, passiveAnim); + return; + } + if (agent.getEquip().get(1) != null && agent.getEquip().get(2) != null && agent.getEquip().get(2).getItemBase().isShield() == false) { + //mob is duel wielding and should conduct an attack for each hand + ItemBase weapon1 = agent.getEquip().get(1).getItemBase(); + double range1 = getMaxDmg(weapon1.getMinDamage(), agent, weapon1) - getMinDmg(weapon1.getMinDamage(), agent, weapon1); + double damage1 = getMinDmg(weapon1.getMinDamage(), agent, weapon1) + ((ThreadLocalRandom.current().nextFloat() * range1) + (ThreadLocalRandom.current().nextFloat() * range1)) / 2; + swingIsDamage(agent, target, (float) damage1, CombatManager.getSwingAnimation(weapon1, null, true)); + ItemBase weapon2 = agent.getEquip().get(2).getItemBase(); + double range2 = getMaxDmg(weapon2.getMinDamage(), agent, weapon2) - getMinDmg(weapon2.getMinDamage(), agent, weapon2); + double damage2 = getMinDmg(weapon2.getMinDamage(), agent, weapon2) + ((ThreadLocalRandom.current().nextFloat() * range2) + (ThreadLocalRandom.current().nextFloat() * range2)) / 2; + swingIsDamage(agent, target, (float) damage2, CombatManager.getSwingAnimation(weapon1, null, false)); + } else { + swingIsDamage(agent, target, determineDamage(agent), anim); + } + + if (agent.getWeaponPower() != null) + agent.getWeaponPower().attack(target, MBServerStatics.ONE_MINUTE); + } + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + PlayerCharacter player = (PlayerCharacter) target; + + if (player.getDebug(64)) + ChatManager.chatSayInfo(player, "Debug Combat: Mob UUID " + agent.getObjectUUID() + " || Building ID = " + agent.getBuildingID() + " || Floor = " + agent.getInFloorID() + " || Level = " + agent.getInBuilding());//combat debug + } + + //SIEGE MONSTERS DO NOT ATTACK GUARDSs + if (target.getObjectType() == GameObjectType.Mob) + if (((Mob) target).isSiege()) + return; + + //handle the retaliate + + if (AbstractWorldObject.IsAbstractCharacter(target)) + CombatManager.handleRetaliate((AbstractCharacter) target, agent); + + if (target.getObjectType() == GameObjectType.Mob) { + Mob targetMob = (Mob) target; + if (targetMob.isSiege()) + return; + } + + } + + public static float determineDamage(Mob agent) { + + //early exit for null + + if (agent == null) + return 0; + + AbstractWorldObject target = agent.getCombatTarget(); + + if (target == null) + return 0; + + float damage = 0; + + DamageType dt = getDamageType(agent); + if (agent.isSummonedPet() == true || agent.isPet() == true || agent.isNecroPet() == true) { + damage = calculatePetDamage(agent); + } else if (agent.isPlayerGuard() == true) { + //damage = calculateGuardDamage(agent); + damage = calculateMobDamage(agent); + } else if (agent.getLevel() > 80) { + damage = calculateEpicDamage(agent); + } else { + damage = calculateMobDamage(agent); + } + if (AbstractWorldObject.IsAbstractCharacter(target)) { + if (((AbstractCharacter) target).isSit()) { + damage *= 2.5f; //increase damage if sitting + } + return (int) (((AbstractCharacter) target).getResists().getResistedDamage(agent, (AbstractCharacter) target, dt, damage, 0)); + } + if (target.getObjectType() == GameObjectType.Building) { + Building building = (Building) target; + Resists resists = building.getResists(); + return (int) ((damage * (1 - (resists.getResist(dt, 0) / 100)))); + } + return damage; + } + + public static DamageType getDamageType(Mob agent) { + DamageType dt = DamageType.Crush; + if (agent.getEquip().get(1) != null) { + return agent.getEquip().get(1).getItemBase().getDamageType(); + } + if (agent.getEquip().get(2) != null && agent.getEquip().get(2).getItemBase().isShield() == false) { + return agent.getEquip().get(2).getItemBase().getDamageType(); + } + return dt; + } + + public static int calculatePetDamage(Mob agent) { + //damage calc for pet + float range; + float damage; + float min = 40; + float max = 60; + AbstractWorldObject target = agent.getCombatTarget(); + float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); + double minDmg = getMinDmg(min, agent, null); + double maxDmg = getMaxDmg(max, agent, null); + dmgMultiplier += agent.getLevel() / 10; + range = (float) (maxDmg - minDmg); + damage = min + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + return (int) (damage * dmgMultiplier); + } + + public static int calculateGuardDamage(Mob agent) { + //damage calc for guard + ItemBase weapon = agent.getEquip().get(1).getItemBase(); + AbstractWorldObject target = agent.getCombatTarget(); + + float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); + + double minDmg = weapon.getMinDamage(); + double maxDmg = weapon.getMaxDamage(); + double min = getMinDmg(minDmg, agent, weapon); + double max = getMaxDmg(maxDmg, agent, weapon); + + DamageType dt = weapon.getDamageType(); + + double range = max - min; + double damage = min + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + + if (AbstractWorldObject.IsAbstractCharacter(target)) + if (((AbstractCharacter) target).isSit()) + damage *= 2.5f; //increase damage if sitting + if (AbstractWorldObject.IsAbstractCharacter(target)) + return (int) (((AbstractCharacter) target).getResists().getResistedDamage(agent, (AbstractCharacter) target, dt, (float) damage, 0) * dmgMultiplier); + return 0; + } + + public static int calculateEpicDamage(Mob agent) { + //handle r8 mob damage + DamageType dt = DamageType.Crush; + AbstractWorldObject target = agent.getCombatTarget(); + float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); + if (agent.getEquip().get(1).getItemBase() != null) { + dt = agent.getEquip().get(1).getItemBase().getDamageType(); + } else if (agent.getEquip().get(2).getItemBase() != null && agent.getEquip().get(2).getItemBase().isShield() == false) { + dt = agent.getEquip().get(2).getItemBase().getDamageType(); + } + double min = agent.getMobBase().getMinDmg(); + double max = agent.getMobBase().getMaxDmg(); + double range = max - min; + double damage = min + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + return (int) (((AbstractCharacter) target).getResists().getResistedDamage(agent, (AbstractCharacter) target, dt, (float) damage, 0) * dmgMultiplier); + } + + public static int calculateMobDamage(Mob agent) { + ItemBase weapon = null; + double minDmg; + double maxDmg; + DamageType dt; + + //main hand or offhand damage + + if (agent.getEquip().get(1) != null) + weapon = agent.getEquip().get(1).getItemBase(); + else if (agent.getEquip().get(2) != null) + weapon = agent.getEquip().get(2).getItemBase(); + + if (weapon != null) { + minDmg = getMinDmg(weapon.getMinDamage(), agent, weapon); + maxDmg = getMaxDmg(weapon.getMaxDamage(), agent, weapon); + dt = weapon.getDamageType(); + } else { + minDmg = agent.getMobBase().getDamageMin(); + maxDmg = agent.getMobBase().getDamageMax(); + dt = DamageType.Crush; + } + + AbstractWorldObject target = agent.getCombatTarget(); + + float dmgMultiplier = 1 + agent.getBonuses().getFloatPercentAll(ModType.MeleeDamageModifier, SourceType.None); + double range = maxDmg - minDmg; + double damage = minDmg + ((ThreadLocalRandom.current().nextFloat() * range) + (ThreadLocalRandom.current().nextFloat() * range)) / 2; + + if (AbstractWorldObject.IsAbstractCharacter(target)) + if (((AbstractCharacter) target).isSit()) + damage *= 2.5f; //increase damage if sitting + if (AbstractWorldObject.IsAbstractCharacter(target)) + return (int) (((AbstractCharacter) target).getResists().getResistedDamage(agent, (AbstractCharacter) target, dt, (float) damage, 0) * dmgMultiplier); + return 0; + } + + public static double getMinDmg(double min, Mob agent, ItemBase weapon) { + + int primary = agent.getStatStrCurrent(); + int secondary = agent.getStatDexCurrent(); + int focusLevel = 0; + int masteryLevel = 0; + + if (weapon != null) { + if (weapon.isStrBased() == true) { + primary = agent.getStatStrCurrent(); + secondary = agent.getStatDexCurrent(); + } else { + primary = agent.getStatDexCurrent(); + secondary = agent.getStatStrCurrent(); + if (agent.getSkills().containsKey(weapon.getSkillRequired())) { + focusLevel = (int) agent.getSkills().get(weapon.getSkillRequired()).getModifiedAmount(); + } + if (agent.getSkills().containsKey(weapon.getMastery())) { + masteryLevel = (int) agent.getSkills().get(weapon.getMastery()).getModifiedAmount(); + } + } + } + return min * (pow(0.0048 * primary + .049 * (primary - 0.75), 0.5) + pow(0.0066 * secondary + 0.064 * (secondary - 0.75), 0.5) + +0.01 * (focusLevel + masteryLevel)); + } + + public static double getMaxDmg(double max, Mob agent, ItemBase weapon) { + + int primary = agent.getStatStrCurrent(); + int secondary = agent.getStatDexCurrent(); + int focusLevel = 0; + int masteryLevel = 0; + + if (weapon != null) { + + if (weapon.isStrBased() == true) { + primary = agent.getStatStrCurrent(); + secondary = agent.getStatDexCurrent(); + } else { + primary = agent.getStatDexCurrent(); + secondary = agent.getStatStrCurrent(); + } + + if (agent.getSkills().containsKey(weapon.getSkillRequired())) + focusLevel = (int) agent.getSkills().get(weapon.getSkillRequired()).getModifiedAmount(); + + if (agent.getSkills().containsKey(weapon.getSkillRequired())) + masteryLevel = (int) agent.getSkills().get(weapon.getMastery()).getModifiedAmount(); + + } + return max * (pow(0.0124 * primary + 0.118 * (primary - 0.75), 0.5) + pow(0.0022 * secondary + 0.028 * (secondary - 0.75), 0.5) + 0.0075 * (focusLevel + masteryLevel)); + } + } diff --git a/src/engine/ai/utilities/MovementUtilities.java b/src/engine/ai/utilities/MovementUtilities.java index 9b905ad1..77a6858e 100644 --- a/src/engine/ai/utilities/MovementUtilities.java +++ b/src/engine/ai/utilities/MovementUtilities.java @@ -7,7 +7,6 @@ // www.magicbane.com - package engine.ai.utilities; import engine.Enum; @@ -30,179 +29,176 @@ import static engine.math.FastMath.sqrt; public class MovementUtilities { - public static boolean inRangeOfBindLocation(Mob agent){ - - - - if (agent.isPlayerGuard()){ - - Mob guardCaptain = null; - if (agent.getContract() != null) - guardCaptain = agent; - else - guardCaptain = (Mob) agent.npcOwner; - - if (guardCaptain != null){ + public static boolean inRangeOfBindLocation(Mob agent) { + + + if (agent.isPlayerGuard()) { + + Mob guardCaptain = null; + if (agent.getContract() != null) + guardCaptain = agent; + else + guardCaptain = (Mob) agent.npcOwner; + + if (guardCaptain != null) { Building barracks = guardCaptain.building; - - if (barracks != null){ - City city = barracks.getCity(); - - if (city != null){ - Building tol = city.getTOL(); - - //Guards recall distance = 814. - if (tol != null){ - if (agent.getLoc().distanceSquared2D(tol.getLoc()) > sqr(Enum.CityBoundsType.ZONE.extents)) { - return false; - } - } - - } - } - } - - return true; - } - Vector3fImmutable sl = new Vector3fImmutable(agent.getLoc().getX(), 0, agent.getLoc().getZ()); - Vector3fImmutable tl = new Vector3fImmutable(agent.getTrueBindLoc().x,0,agent.getTrueBindLoc().z); + if (barracks != null) { + City city = barracks.getCity(); + + if (city != null) { + Building tol = city.getTOL(); + + //Guards recall distance = 814. + if (tol != null) { + return !(agent.getLoc().distanceSquared2D(tol.getLoc()) > sqr(Enum.CityBoundsType.ZONE.extents)); + } + + } + } + } - float distanceSquaredToTarget = sl.distanceSquared2D(tl); //distance to center of target - float zoneRange = 250; + return true; + } - if (agent.getParentZone() != null){ - if (agent.getParentZone().getBounds() != null) - zoneRange = agent.getParentZone().getBounds().getHalfExtents().x * 2; - } + Vector3fImmutable sl = new Vector3fImmutable(agent.getLoc().getX(), 0, agent.getLoc().getZ()); + Vector3fImmutable tl = new Vector3fImmutable(agent.getTrueBindLoc().x, 0, agent.getTrueBindLoc().z); - if (zoneRange > 300) - zoneRange = 300; - - if (agent.getSpawnRadius() > zoneRange) - zoneRange = agent.getSpawnRadius(); - + float distanceSquaredToTarget = sl.distanceSquared2D(tl); //distance to center of target + float zoneRange = 250; - return distanceSquaredToTarget < sqr(MBServerStatics.AI_DROP_AGGRO_RANGE + zoneRange); + if (agent.getParentZone() != null) { + if (agent.getParentZone().getBounds() != null) + zoneRange = agent.getParentZone().getBounds().getHalfExtents().x * 2; + } - } + if (zoneRange > 300) + zoneRange = 300; - public static boolean inRangeToAggro(Mob agent,PlayerCharacter target){ + if (agent.getSpawnRadius() > zoneRange) + zoneRange = agent.getSpawnRadius(); - Vector3fImmutable sl = agent.getLoc(); - Vector3fImmutable tl =target.getLoc(); - float distanceSquaredToTarget = sl.distanceSquared2D(tl) - sqr(agent.calcHitBox() + target.calcHitBox()); //distance to center of target - float range = MBServerStatics.AI_BASE_AGGRO_RANGE; + return distanceSquaredToTarget < sqr(MBServerStatics.AI_DROP_AGGRO_RANGE + zoneRange); - if (agent.isPlayerGuard()) - range = 150; + } - return distanceSquaredToTarget < sqr(range); + public static boolean inRangeToAggro(Mob agent, PlayerCharacter target) { - } + Vector3fImmutable sl = agent.getLoc(); + Vector3fImmutable tl = target.getLoc(); - public static boolean inRangeDropAggro(Mob agent,PlayerCharacter target){ + float distanceSquaredToTarget = sl.distanceSquared2D(tl) - sqr(agent.calcHitBox() + target.calcHitBox()); //distance to center of target + float range = MBServerStatics.AI_BASE_AGGRO_RANGE; - Vector3fImmutable sl = agent.getLoc(); - Vector3fImmutable tl = target.getLoc(); + if (agent.isPlayerGuard()) + range = 150; - float distanceSquaredToTarget = sl.distanceSquared2D(tl) - sqr(agent.calcHitBox() + target.calcHitBox()); //distance to center of target + return distanceSquaredToTarget < sqr(range); - float range = agent.getRange() + 150; + } - if (range > 200) - range = 200; + public static boolean inRangeDropAggro(Mob agent, PlayerCharacter target) { + Vector3fImmutable sl = agent.getLoc(); + Vector3fImmutable tl = target.getLoc(); - return distanceSquaredToTarget < sqr(range); + float distanceSquaredToTarget = sl.distanceSquared2D(tl) - sqr(agent.calcHitBox() + target.calcHitBox()); //distance to center of target - } + float range = agent.getRange() + 150; - public static Vector3fImmutable GetMoveLocation(Mob aiAgent, AbstractCharacter aggroTarget){ + if (range > 200) + range = 200; - // Player isnt moving and neither is mob. Just return - // the mobile's current location. Ain't goin nowhere! - // *** Refactor: Check to ensure methods calling us - // all don't sent move messages when not moving. - if ((aggroTarget.isMoving() == false)) - return aggroTarget.getLoc(); + return distanceSquaredToTarget < sqr(range); - if (aggroTarget.getEndLoc().x != 0){ + } - float aggroTargetDistanceSquared = aggroTarget.getLoc().distanceSquared2D(aggroTarget.getEndLoc()); - float aiAgentDistanceSquared = aiAgent.getLoc().distanceSquared2D(aggroTarget.getEndLoc()); + public static Vector3fImmutable GetMoveLocation(Mob aiAgent, AbstractCharacter aggroTarget) { - if (aiAgentDistanceSquared >= aggroTargetDistanceSquared) - return aggroTarget.getEndLoc(); - else{ - float distanceToMove = sqrt(aggroTargetDistanceSquared + aiAgentDistanceSquared) *.5f; + // Player isnt moving and neither is mob. Just return + // the mobile's current location. Ain't goin nowhere! + // *** Refactor: Check to ensure methods calling us + // all don't sent move messages when not moving. - return aggroTarget.getFaceDir().scaleAdd(distanceToMove, aggroTarget.getLoc()); + if ((aggroTarget.isMoving() == false)) + return aggroTarget.getLoc(); - } - } + if (aggroTarget.getEndLoc().x != 0) { - // One of us is moving so let's calculate our destination loc for this - // simulation frame. We will simply project our position onto the - // character's movement vector and return the closest point. + float aggroTargetDistanceSquared = aggroTarget.getLoc().distanceSquared2D(aggroTarget.getEndLoc()); + float aiAgentDistanceSquared = aiAgent.getLoc().distanceSquared2D(aggroTarget.getEndLoc()); - return aiAgent.getLoc().ClosestPointOnLine(aggroTarget.getLoc(), aggroTarget.getEndLoc()); - } + if (aiAgentDistanceSquared >= aggroTargetDistanceSquared) + return aggroTarget.getEndLoc(); + else { + float distanceToMove = sqrt(aggroTargetDistanceSquared + aiAgentDistanceSquared) * .5f; - public static void moveToLocation(Mob agent,Vector3fImmutable newLocation, float offset){ - try { - - //don't move farther than 30 units from player. - if (offset > 30) - offset = 30; - Vector3fImmutable newLoc = Vector3fImmutable.getRandomPointInCircle(newLocation, offset); + return aggroTarget.getFaceDir().scaleAdd(distanceToMove, aggroTarget.getLoc()); + } + } - agent.setFaceDir(newLoc.subtract2D(agent.getLoc()).normalize()); - - aiMove(agent,newLoc,false); - } catch (Exception e) { - Logger.error( e.toString()); - } - } + // One of us is moving so let's calculate our destination loc for this + // simulation frame. We will simply project our position onto the + // character's movement vector and return the closest point. + return aiAgent.getLoc().ClosestPointOnLine(aggroTarget.getLoc(), aggroTarget.getEndLoc()); + } + public static void moveToLocation(Mob agent, Vector3fImmutable newLocation, float offset) { + try { - public static boolean canMove(Mob agent) { - if (agent.getMobBase() != null && Enum.MobFlagType.SENTINEL.elementOf(agent.getMobBase().getFlags())) - return false; + //don't move farther than 30 units from player. + if (offset > 30) + offset = 30; + Vector3fImmutable newLoc = Vector3fImmutable.getRandomPointInCircle(newLocation, offset); - return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned,SourceType.None) && !agent.getBonuses().getBool(ModType.CannotMove, SourceType.None)); - } - public static Vector3fImmutable randomPatrolLocation(Mob agent,Vector3fImmutable center, float radius){ + agent.setFaceDir(newLoc.subtract2D(agent.getLoc()).normalize()); - //Determing where I want to move. - return new Vector3fImmutable((center.x - radius) + ((ThreadLocalRandom.current().nextFloat()+.1f*2)*radius), - center.y, - (center.z - radius) + ((ThreadLocalRandom.current().nextFloat()+.1f *2)*radius)); - } - public static Long estimateMovementTime(Mob agent) { - if(agent.getEndLoc().x == 0 && agent.getEndLoc().y == 0) - return 0L; + aiMove(agent, newLoc, false); + } catch (Exception e) { + Logger.error(e.toString()); + } + } - return (long) ((agent.getLoc().distance2D(agent.getEndLoc())*1000)/agent.getSpeed()); - } - public static void aiMove(Mob agent,Vector3fImmutable vect, boolean isWalking) { + public static boolean canMove(Mob agent) { + if (agent.getMobBase() != null && Enum.MobFlagType.SENTINEL.elementOf(agent.getMobBase().getFlags())) + return false; - //update our walk/run state. - if (isWalking && !agent.isWalk()){ - agent.setWalkMode(true); - MovementManager.sendRWSSMsg(agent); - }else if(!isWalking && agent.isWalk()){ - agent.setWalkMode(false); - MovementManager.sendRWSSMsg(agent); - } + return (agent.isAlive() && !agent.getBonuses().getBool(ModType.Stunned, SourceType.None) && !agent.getBonuses().getBool(ModType.CannotMove, SourceType.None)); + } - MoveToPointMsg msg = new MoveToPointMsg(); + public static Vector3fImmutable randomPatrolLocation(Mob agent, Vector3fImmutable center, float radius) { + + //Determing where I want to move. + return new Vector3fImmutable((center.x - radius) + ((ThreadLocalRandom.current().nextFloat() + .1f * 2) * radius), + center.y, + (center.z - radius) + ((ThreadLocalRandom.current().nextFloat() + .1f * 2) * radius)); + } + + public static Long estimateMovementTime(Mob agent) { + if (agent.getEndLoc().x == 0 && agent.getEndLoc().y == 0) + return 0L; + + return (long) ((agent.getLoc().distance2D(agent.getEndLoc()) * 1000) / agent.getSpeed()); + } + + public static void aiMove(Mob agent, Vector3fImmutable vect, boolean isWalking) { + + //update our walk/run state. + if (isWalking && !agent.isWalk()) { + agent.setWalkMode(true); + MovementManager.sendRWSSMsg(agent); + } else if (!isWalking && agent.isWalk()) { + agent.setWalkMode(false); + MovementManager.sendRWSSMsg(agent); + } + + MoveToPointMsg msg = new MoveToPointMsg(); // Regions currentRegion = Mob.InsideBuildingRegion(agent); @@ -225,9 +221,8 @@ public class MovementUtilities { // agent.setLastRegion(currentRegion); - - Vector3fImmutable startLoc = null; - Vector3fImmutable endLoc = null; + Vector3fImmutable startLoc = null; + Vector3fImmutable endLoc = null; // if (agent.getLastRegion() != null){ // Building inBuilding = Building.getBuildingFromCache(agent.getInBuildingID()); @@ -241,63 +236,60 @@ public class MovementUtilities { // startLoc = agent.getLoc(); // endLoc = vect; // } - - startLoc = agent.getLoc(); - endLoc = vect; - - msg.setSourceType(GameObjectType.Mob.ordinal()); - msg.setSourceID(agent.getObjectUUID()); - msg.setStartCoord(startLoc); - msg.setEndCoord(endLoc); - msg.setUnknown01(-1); - msg.setInBuilding(-1); - msg.setTargetType(0); - msg.setTargetID(0); - - - try { - MovementManager.movement(msg, agent); - } catch (MsgSendException e) { - // TODO Figure out how we want to handle the msg send exception - e.printStackTrace(); - } - } - - public static Vector3fImmutable GetDestinationToCharacter(Mob aiAgent, AbstractCharacter character){ - - if (!character.isMoving()) - return character.getLoc(); - - - float agentDistanceEndLoc = aiAgent.getLoc().distanceSquared2D(character.getEndLoc()); - float characterDistanceEndLoc = character.getLoc().distanceSquared2D(character.getEndLoc()); - - if (agentDistanceEndLoc > characterDistanceEndLoc) - return character.getEndLoc(); - - return character.getLoc(); - } - - public static boolean updateMovementToCharacter(Mob aiAgent, AbstractCharacter aggroTarget){ - - if (aiAgent.destination.equals(Vector3fImmutable.ZERO)) - return true; - - if (!aiAgent.isMoving()) - return true; - - - - - if (aggroTarget.isMoving()){ - if (!aiAgent.destination.equals(aggroTarget.getEndLoc()) && !aiAgent.destination.equals(aggroTarget.getLoc())) - return true; - }else{ - if (aiAgent.destination.equals(aggroTarget.getLoc())) - return false; - } - - return false; - } + + startLoc = agent.getLoc(); + endLoc = vect; + + msg.setSourceType(GameObjectType.Mob.ordinal()); + msg.setSourceID(agent.getObjectUUID()); + msg.setStartCoord(startLoc); + msg.setEndCoord(endLoc); + msg.setUnknown01(-1); + msg.setInBuilding(-1); + msg.setTargetType(0); + msg.setTargetID(0); + + + try { + MovementManager.movement(msg, agent); + } catch (MsgSendException e) { + // TODO Figure out how we want to handle the msg send exception + e.printStackTrace(); + } + } + + public static Vector3fImmutable GetDestinationToCharacter(Mob aiAgent, AbstractCharacter character) { + + if (!character.isMoving()) + return character.getLoc(); + + + float agentDistanceEndLoc = aiAgent.getLoc().distanceSquared2D(character.getEndLoc()); + float characterDistanceEndLoc = character.getLoc().distanceSquared2D(character.getEndLoc()); + + if (agentDistanceEndLoc > characterDistanceEndLoc) + return character.getEndLoc(); + + return character.getLoc(); + } + + public static boolean updateMovementToCharacter(Mob aiAgent, AbstractCharacter aggroTarget) { + + if (aiAgent.destination.equals(Vector3fImmutable.ZERO)) + return true; + + if (!aiAgent.isMoving()) + return true; + + + if (aggroTarget.isMoving()) { + return !aiAgent.destination.equals(aggroTarget.getEndLoc()) && !aiAgent.destination.equals(aggroTarget.getLoc()); + } else { + if (aiAgent.destination.equals(aggroTarget.getLoc())) + return false; + } + + return false; + } } diff --git a/src/engine/ai/utilities/PowerUtilities.java b/src/engine/ai/utilities/PowerUtilities.java index 12c80cb7..66039f96 100644 --- a/src/engine/ai/utilities/PowerUtilities.java +++ b/src/engine/ai/utilities/PowerUtilities.java @@ -7,7 +7,6 @@ // www.magicbane.com - package engine.ai.utilities; public class PowerUtilities {