From 25937cdd9715a19595c27c623b07bdcd5b3339bd Mon Sep 17 00:00:00 2001 From: FatBoy-DOTC Date: Wed, 26 Mar 2025 21:27:16 -0500 Subject: [PATCH] new mob AI --- src/engine/mobileAI/MobAI.java | 24 ++ .../mobileAI/MobHandlers/MobHandler.java | 271 +++++++++++++++++- 2 files changed, 294 insertions(+), 1 deletion(-) diff --git a/src/engine/mobileAI/MobAI.java b/src/engine/mobileAI/MobAI.java index 890781d4..091766dd 100644 --- a/src/engine/mobileAI/MobAI.java +++ b/src/engine/mobileAI/MobAI.java @@ -619,6 +619,30 @@ public class MobAI { public static void DetermineAction(Mob mob) { try { + boolean override = false; + switch (mob.BehaviourType) { + case GuardCaptain: + case GuardMinion: + case GuardWallArcher: + case Pet1: + case HamletGuard: + override = false; + break; + default: + override = true; + break; + } + + if(mob.isSiege()) + override = false; + + if(mob.isPet()) + override = false; + + if(override){ + SuperSimpleMobAI.run(mob); + return; + } //always check the respawn que, respawn 1 mob max per second to not flood the client diff --git a/src/engine/mobileAI/MobHandlers/MobHandler.java b/src/engine/mobileAI/MobHandlers/MobHandler.java index a47894a0..c84c598c 100644 --- a/src/engine/mobileAI/MobHandlers/MobHandler.java +++ b/src/engine/mobileAI/MobHandlers/MobHandler.java @@ -1,9 +1,278 @@ package engine.mobileAI.MobHandlers; -import engine.objects.Mob; +import engine.Enum; +import engine.InterestManagement.InterestManager; +import engine.gameManager.ChatManager; +import engine.gameManager.PowersManager; +import engine.gameManager.ZoneManager; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; +import engine.mobileAI.Threads.MobAIThread; +import engine.mobileAI.utilities.CombatUtilities; +import engine.mobileAI.utilities.MovementUtilities; +import engine.objects.*; +import engine.powers.PowersBase; +import engine.server.MBServerStatics; + +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; public class MobHandler { public static void run(Mob mob){ + if (!mob.isAlive()) { + CheckForRespawn(mob); + return; + } + + if(mob.playerAgroMap.isEmpty()) + return; + + CheckToSendMobHome(mob); + + if(mob.combatTarget == null || !mob.combatTarget.isAlive()){ + CheckForAggro(mob); + return; + } + if(mob.combatTarget != null) + CheckToDropAggro(mob); + + if(MovementUtilities.canMove(mob)) + CheckMobMovement(mob); + + if(mob.combatTarget != null && CombatUtilities.inRangeToAttack(mob,mob.combatTarget)) + CheckToAttack(mob); + } + + public static void CheckToDropAggro(Mob mob){ + if(mob.loc.distanceSquared(mob.combatTarget.loc) > (64f * 64f)) + mob.setCombatTarget(null); + } + + public static void CheckForRespawn(Mob mob){ + try { + + if (mob.deathTime == 0) { + mob.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 (!mob.despawned) { + + if (mob.getCharItemManager().getInventoryCount() > 0) { + if (System.currentTimeMillis() > mob.deathTime + MBServerStatics.DESPAWN_TIMER_WITH_LOOT) { + mob.despawn(); + mob.deathTime = System.currentTimeMillis(); + return; + } + //No items in inventory. + } else if (mob.isHasLoot()) { + if (System.currentTimeMillis() > mob.deathTime + MBServerStatics.DESPAWN_TIMER_ONCE_LOOTED) { + mob.despawn(); + mob.deathTime = System.currentTimeMillis(); + return; + } + //Mob never had Loot. + } else { + if (System.currentTimeMillis() > mob.deathTime + MBServerStatics.DESPAWN_TIMER) { + mob.despawn(); + mob.deathTime = System.currentTimeMillis(); + return; + } + } + return; + } + + if(Mob.discDroppers.contains(mob)) + return; + + if (mob.despawned && System.currentTimeMillis() > (mob.deathTime + (mob.spawnTime * 1000L))) { + if (!Zone.respawnQue.contains(mob)) { + Zone.respawnQue.add(mob); + } + } + } catch (Exception e) { + //(aiAgent.getObjectUUID() + " " + aiAgent.getName() + " Failed At: CheckForRespawn" + " " + e.getMessage()); + } + } + + public static void CheckForAggro(Mob mob){ + PlayerCharacter tar = null; + for(int id : mob.playerAgroMap.keySet()){ + PlayerCharacter target = PlayerCharacter.getFromCache(id); + if(tar == null || mob.loc.distanceSquared(tar.loc) < mob.loc.distanceSquared(target.loc)) + if(MobCanAggro(mob,target)) + tar = target; + } + } + + public static Boolean MobCanAggro(Mob mob, PlayerCharacter loadedPlayer){ + if (loadedPlayer == null) + return false; + + //Player is Dead, Mob no longer needs to attempt to aggro. Remove them from aggro map. + if (!loadedPlayer.isAlive()) + return false; + + //Can't see target, skip aggro. + if (!mob.canSee(loadedPlayer)) + return false; + + // No aggro for this race type + if (mob.notEnemy != null && mob.notEnemy.size() > 0 && mob.notEnemy.contains(loadedPlayer.getRace().getRaceType().getMonsterType())) + return false; + + //mob has enemies and this player race is not it + if (mob.enemy != null && mob.enemy.size() > 0 && !mob.enemy.contains(loadedPlayer.getRace().getRaceType().getMonsterType())) + return false; + + return true; + } + + public static void CheckMobMovement(Mob mob){ + if(!mob.isAlive()) + return; + + if(mob.combatTarget == null){ + //patrol + Patrol(mob); + }else{ + //combat movement + if(CombatUtilities.inRangeToAttack(mob,mob.combatTarget)) + return; + else + MovementUtilities.aiMove(mob,mob.combatTarget.loc,false); + } + } + + public static void CheckToAttack(Mob mob){ + try { + + PlayerCharacter target = (PlayerCharacter) mob.combatTarget; + + if (!mob.canSee(target)) { + mob.setCombatTarget(null); + return; + } + + if (mob.BehaviourType.callsForHelp) + MobCallForHelp(mob); + + if (mob.isMoving() && mob.getRange() > 20) + return; + + if(target.combatStats == null) + target.combatStats = new PlayerCombatStats(target); + + ItemBase mainHand = mob.getWeaponItemBase(true); + ItemBase offHand = mob.getWeaponItemBase(false); + + if (mainHand == null && offHand == null) { + CombatUtilities.combatCycle(mob, target, true, null); + int delay = 3000; + mob.setLastAttackTime(System.currentTimeMillis() + delay); + } else if (mob.getWeaponItemBase(true) != null) { + int delay = 3000; + CombatUtilities.combatCycle(mob, target, true, mob.getWeaponItemBase(true)); + mob.setLastAttackTime(System.currentTimeMillis() + delay); + } else if (mob.getWeaponItemBase(false) != null) { + int attackDelay = 3000; + CombatUtilities.combatCycle(mob, target, false, mob.getWeaponItemBase(false)); + mob.setLastAttackTime(System.currentTimeMillis() + attackDelay); + } + + if (target.getPet() != null) + if (target.getPet().getCombatTarget() == null && target.getPet().assist) + target.getPet().setCombatTarget(mob); + + } catch (Exception e) { + ////(mob.getObjectUUID() + " " + mob.getName() + " Failed At: AttackPlayer" + " " + e.getMessage()); + } + } + + public static void MobCallForHelp(Mob mob) { + + try { + + boolean callGotResponse = false; + + 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) + mob.nextCallForHelp = System.currentTimeMillis() + 60000; + + } catch (Exception e) { + //(mob.getObjectUUID() + " " + mob.getName() + " Failed At: MobCallForHelp" + " " + e.getMessage()); + } + } + + private static void Patrol(Mob mob) { + + try { + + //make sure mob is out of combat stance + + int patrolDelay = ThreadLocalRandom.current().nextInt((int) (MobAIThread.AI_PATROL_DIVISOR * 0.5f), MobAIThread.AI_PATROL_DIVISOR) + MobAIThread.AI_PATROL_DIVISOR; + + //early exit while waiting to patrol again + + if (mob.stopPatrolTime + (patrolDelay * 1000) > System.currentTimeMillis()) + return; + + 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); + + } catch (Exception e) { + ////(mob.getObjectUUID() + " " + mob.getName() + " Failed At: AttackTarget" + " " + e.getMessage()); + } + } + + private static void CheckToSendMobHome(Mob mob) { + + if(mob.isNecroPet()) + return; + + try { + + if (!MovementUtilities.inRangeOfBindLocation(mob)) { + + PowersBase recall = PowersManager.getPowerByToken(-1994153779); + PowersManager.useMobPower(mob, mob, recall, 40); + + for (Map.Entry playerEntry : mob.playerAgroMap.entrySet()) + PlayerCharacter.getFromCache((int) playerEntry.getKey()).setHateValue(0); + + mob.setCombatTarget(null); + } + } catch (Exception e) { + //(mob.getObjectUUID() + " " + mob.getName() + " Failed At: CheckToSendMobHome" + " " + e.getMessage()); + } } }