diff --git a/src/engine/mobileAI/MobAI.java b/src/engine/mobileAI/MobAI.java index cabe0d9d..e0c2621d 100644 --- a/src/engine/mobileAI/MobAI.java +++ b/src/engine/mobileAI/MobAI.java @@ -560,6 +560,21 @@ public class MobAI { public static void MobCallForHelp(Mob mob) { + boolean continueExecution = false; + switch(mob.BehaviourType){ + case GuardCaptain: + case GuardMinion: + case Pet1: + case GuardWallArcher: + case HamletGuard: + case SimpleStandingGuard: + continueExecution = true; + break; + } + if(!continueExecution) { + MobAi2.runAI(mob); + return; + } try { boolean callGotResponse = false; diff --git a/src/engine/mobileAI/MobAi2.java b/src/engine/mobileAI/MobAi2.java new file mode 100644 index 00000000..1949a7b9 --- /dev/null +++ b/src/engine/mobileAI/MobAi2.java @@ -0,0 +1,347 @@ +package engine.mobileAI; + +import engine.Enum; +import engine.InterestManagement.WorldGrid; +import engine.gameManager.*; +import engine.mobileAI.Threads.MobAIThread; +import engine.mobileAI.utilities.CombatUtilities; +import engine.mobileAI.utilities.MovementUtilities; +import engine.objects.*; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.util.HashSet; +import java.util.concurrent.ThreadLocalRandom; + +public class MobAi2 { + + public enum State + { + Idle, + Patrolling, + Attacking, + Dead + } + public static State getState(Mob mob){ + + if(!mob.isAlive()) + return State.Dead; + + if(mob.playerAgroMap.isEmpty()) + return State.Idle; + + if(mob.combatTarget != null) + return State.Attacking; + + return State.Patrolling; + } + + public static void runAI(Mob mob){ + switch(mob.BehaviourType){ + case GuardCaptain: + case GuardMinion: + case Pet1: + case GuardWallArcher: + case HamletGuard: + case SimpleStandingGuard: + return; + } + switch(getState(mob)){ + case Idle: + if(mob.isMoving()) + mob.stopMovement(mob.getMovementLoc()); + if(mob.combatTarget != null) { + mob.setCombatTarget(null); + mob.setCombat(false); + } + return; + case Dead: + respawn(mob); + break; + case Patrolling: + patrol(mob); + break; + case Attacking: + attack(mob); + break; + } + } + + //handles respawning and de-spawning for mobs and their corpses + public static void respawn(Mob mob){ + + //if mob doesn't have a death time somehow, set it to now + if (mob.deathTime == 0) + mob.setDeathTime(System.currentTimeMillis()); + + //only execute this logic is the mob hasn't de-spawned yet + if (!mob.despawned) { + + //if the inventory is empty, the mob can disappear + if(mob.getInventory(true).isEmpty()) { + mob.despawn(); + mob.deathTime = System.currentTimeMillis(); + return; + } + + //if the mob has been dead for 10 seconds it can disappear + if (System.currentTimeMillis() > mob.deathTime + 10000L) { + mob.despawn(); + mob.deathTime = System.currentTimeMillis(); + return; + } + } + + //disc dropper respawns are handled elsewhere + if(Mob.discDroppers.contains(mob)) + return; + + //if mob isn't queued for respawn, do so now + if (!Zone.respawnQue.contains(mob)) { + if (System.currentTimeMillis() > (mob.deathTime + (mob.spawnTime * 1000L))) { + Zone.respawnQue.add(mob); + } + } + } + + //handles patrolling and looking for potential combat targets + public static void patrol(Mob mob){ + HashSet potentialTargets = WorldGrid.getObjectsInRangePartial(mob.loc, Float.parseFloat(ConfigManager.MB_AI_AGGRO_RANGE.getValue()), MBServerStatics.MASK_PLAYER); + for(AbstractWorldObject awo : potentialTargets){ + PlayerCharacter target = (PlayerCharacter) awo; + if(mob.canSee(target)) + mob.setCombatTarget(target); + if(mob.combatTarget != null) { + mob.stopMovement(mob.getMovementLoc()); + return; + } + } + if(mob.isMoving() || !mob.BehaviourType.canRoam) + return; + + int patrolDelay = ThreadLocalRandom.current().nextInt((int) (MobAIThread.AI_PATROL_DIVISOR * 0.5f), MobAIThread.AI_PATROL_DIVISOR) + MobAIThread.AI_PATROL_DIVISOR; + + 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); + } + + public static void attack(Mob mob){ + AbstractWorldObject target = mob.combatTarget; + + if (target == null || !target.isAlive()) { + mob.setCombatTarget(null); + return; + } + + if(!mob.isCombat()) + mob.setCombat(true); + + if (!CombatUtilities.inRangeToAttack(mob, target)) { + MovementUtilities.aiMove(mob, target.loc, false); + return; + } + + switch (target.getObjectType()) { + case PlayerCharacter: + PlayerCharacter targetPlayer = (PlayerCharacter) target; + AttackPlayer(mob, targetPlayer); + break; + case Building: + Building targetBuilding = (Building) target; + AttackBuilding(mob, targetBuilding); + break; + case Mob: + Mob targetMob = (Mob) target; + AttackMob(mob, targetMob); + break; + } + } + + public static void AttackPlayer(Mob mob, PlayerCharacter target) { + + try { + + if (!mob.canSee(target)) { + mob.setCombatTarget(null); + return; + } + + if (mob.BehaviourType.callsForHelp) + MobCallForHelp(mob); + + if (!MovementUtilities.inRangeDropAggro(mob, target)) { + mob.setCombatTarget(null); + return; + } + + if (CombatUtilities.inRange2D(mob, target, mob.getRange())) { + + //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() && mob.getRange() > 20) + 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); + } + } + + if (target.getPet() != null) + if (target.getPet().getCombatTarget() == null && target.getPet().assist == true) + target.getPet().setCombatTarget(mob); + + } catch (Exception e) { + Logger.info(mob.getObjectUUID() + " " + mob.getName() + " Failed At: AttackPlayer" + " " + e.getMessage()); + } + + } + + public static void AttackBuilding(Mob mob, Building target) { + + try { + + if(mob == null || target == null) + return; + + 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() != null && mob.getGuild() != 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); + } + + //if (mob.isSiege()) { + // PowerProjectileMsg ppm = new PowerProjectileMsg(mob, target); + // ppm.setRange(50); + // DispatchMessage.dispatchMsgToInterestArea(mob, ppm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + //} + + } catch (Exception e) { + Logger.info(mob.getObjectUUID() + " " + mob.getName() + " Failed At: AttackBuilding" + " " + e.getMessage()); + } + } + + public static void AttackMob(Mob mob, Mob target) { + + try { + + 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); + if (target.getCombatTarget() == null) { + target.setCombatTarget(mob); + } + } + } catch (Exception e) { + Logger.info(mob.getObjectUUID() + " " + mob.getName() + " Failed At: AttackMob" + " " + e.getMessage()); + } + } + + public static void MobCallForHelp(Mob mob) { + + if (mob.nextCallForHelp == 0) + mob.nextCallForHelp = System.currentTimeMillis(); + + if (mob.nextCallForHelp > System.currentTimeMillis()) + return; + + Zone mobCamp = mob.getParentZone(); + + for (Mob helper : mobCamp.zoneMobSet) { + if (helper.BehaviourType.respondsToCallForHelp && helper.BehaviourType.BehaviourHelperType.equals(mob.BehaviourType)) { + helper.setCombatTarget(mob.getCombatTarget()); + break; + } + } + + mob.nextCallForHelp = System.currentTimeMillis() + 30000L; + } +}