Public Repository for the Magicbane Shadowbane Emulator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1775 lines
61 KiB

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.ai;
import engine.Enum;
import engine.Enum.DispatchChannel;
import engine.Enum.GameObjectType;
import engine.InterestManagement.WorldGrid;
import engine.ai.utilities.CombatUtilities;
import engine.ai.utilities.MovementUtilities;
import engine.gameManager.BuildingManager;
import engine.gameManager.CombatManager;
import engine.gameManager.MovementManager;
import engine.gameManager.PowersManager;
import engine.math.Vector3fImmutable;
import engine.net.DispatchMessage;
import engine.net.client.msg.PerformActionMsg;
import engine.net.client.msg.PowerProjectileMsg;
import engine.net.client.msg.UpdateStateMsg;
import engine.objects.*;
import engine.powers.ActionsBase;
import engine.powers.PowersBase;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
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 {
public enum STATE {
Disabled,
Respawn,
Idle,
Awake,
Aggro,
Patrol,
Help,
Attack,
Home,
Dead,
Recalling,
Retaliate,
Chase
}
public static void run(Mob mob) {
if (mob == null) {
return;
}
STATE state = mob.state;
switch (state) {
case Idle:
if (mob.isAlive())
mob.updateLocation();
if (mob.isPlayerGuard()) {
guardAwake(mob);
break;
}
idle(mob);
break;
case Awake:
if (mob.isAlive())
mob.updateLocation();
if (mob.isPlayerGuard())
guardAwake(mob);
else if (mob.isSiege() == false) {
if (mob.isPet())
petAwake(mob);
else if (mob.isGuard())
awakeNPCguard(mob);
else
awake(mob);
}
break;
case Aggro:
if (mob.isAlive())
mob.updateLocation();
if (mob.isPlayerGuard())
guardAggro(mob, mob.getAggroTargetID());
else if (mob.isGuard()) {
awakeNPCguard(mob);
} else {
aggro(mob, mob.getAggroTargetID());
}
break;
case Patrol:
if (mob.isAlive())
mob.updateLocation();
if (mob.isPlayerGuard())
guardPatrol(mob);
else
patrol(mob);
break;
case Attack:
if (mob.isAlive())
mob.updateLocation();
if (!mob.isCombat()) {
mob.setCombat(true);
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(mob);
DispatchMessage.sendToAllInRange(mob, rwss);
}
if (mob.isPlayerGuard())
guardAttack(mob);
else if (mob.isPet() || mob.isSiege())
petAttack(mob);
else if (mob.isGuard())
guardAttackMob(mob);
else
mobAttack(mob);
break;
case Home:
if (mob.isPlayerGuard())
guardHome(mob, mob.isWalkingHome());
else
home(mob, mob.isWalkingHome());
break;
case Dead:
dead(mob);
break;
case Respawn:
respawn(mob);
break;
case Recalling:
recalling(mob);
break;
case Retaliate:
retaliate(mob);
break;
case Chase:
handleMobChase(mob);
break;
}
}
public static boolean setAwake(Mob aiAgent, boolean force) {
if (force) {
aiAgent.state = STATE.Awake;
return true;
}
if (aiAgent.state == STATE.Idle) {
aiAgent.state = STATE.Awake;
return true;
}
return false;
}
public static boolean setAggro(Mob aiAgent, int targetID) {
if (aiAgent.state != STATE.Dead) {
aiAgent.setNoAggro(false);
aiAgent.setAggroTargetID(targetID);
aiAgent.state = STATE.Aggro;
return true;
}
return false;
}
private static void idle(Mob mob) {
if (mob.getLoc().distanceSquared2D(mob.getBindLoc()) > sqr(2000)) {
mob.setWalkingHome(false);
mob.state = STATE.Home;
}
}
private static void awake(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) {
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
//Don't attempt to aggro if No aggro is on and aiAgent is not home yet.
if (aiAgent.isNoAggro() && aiAgent.isMoving()) {
return;
}
//Mob stopped Moving let's turn aggro back on.
if (aiAgent.isNoAggro()) {
aiAgent.setNoAggro(false);
}
//no players currently have this mob loaded. return to IDLE.
if (aiAgent.playerAgroMap.isEmpty()) {
aiAgent.state = STATE.Idle;
return;
}
//currently npc guards wont patrol or aggro
if (aiAgent.isGuard()) {
return;
}
//Get the Map for Players that loaded this mob.
ConcurrentHashMap<Integer, Boolean> loadedPlayers = aiAgent.playerAgroMap;
if (!Enum.MobFlagType.AGGRESSIVE.elementOf(aiAgent.getMobBase().getFlags()) && aiAgent.getCombatTarget() == null) {
//attempt to patrol even if aiAgent isn't aggresive;
int patrolRandom = ThreadLocalRandom.current().nextInt(1000);
if (patrolRandom <= MBServerStatics.AI_PATROL_DIVISOR) {
aiAgent.state = STATE.Patrol;
}
return;
}
//aiAgent finished moving home, set aggro on.
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) {
// Logger.error("MobileFSM", "Player with UID " + playerID + " returned null in mob.getPlayerAgroMap()");
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 (loadedPlayer.getRace().getRaceType().getMonsterType().elementOf(aiAgent.notEnemy))
continue;
if (MovementUtilities.inRangeToAggro(aiAgent, loadedPlayer)) {
aiAgent.setAggroTargetID(playerID);
aiAgent.state = STATE.Aggro;
return;
}
}
int patrolRandom = ThreadLocalRandom.current().nextInt(1000);
if (patrolRandom <= MBServerStatics.AI_PATROL_DIVISOR) {
aiAgent.state = STATE.Patrol;
}
}
private static void guardAttackMob(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
AbstractGameObject target = aiAgent.getCombatTarget();
if (target == null) {
aiAgent.state = STATE.Awake;
return;
}
if (target.getObjectType().equals(GameObjectType.Mob) == false) {
aiAgent.state = STATE.Awake;
return;
}
if (target.equals(aiAgent)) {
aiAgent.state = STATE.Awake;
return;
}
Mob mob = (Mob) target;
if (!mob.isAlive() || mob.state == STATE.Dead) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (CombatUtilities.inRangeToAttack(aiAgent, mob)) {
//not time to attack yet.
if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) {
return;
}
if (!CombatUtilities.RunAIRandom())
return;
if (aiAgent.getRange() >= 30 && aiAgent.isMoving())
return;
//no weapons, defualt mob attack speed 3 seconds.
ItemBase mainHand = aiAgent.getWeaponItemBase(true);
ItemBase offHand = aiAgent.getWeaponItemBase(false);
if (mainHand == null && offHand == null) {
CombatUtilities.combatCycle(aiAgent, mob, true, null);
int delay = 3000;
if (aiAgent.isSiege())
delay = 11000;
aiAgent.setLastAttackTime(System.currentTimeMillis() + delay);
} else
//TODO set offhand attack time.
if (aiAgent.getWeaponItemBase(true) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 11000;
CombatUtilities.combatCycle(aiAgent, mob, true, aiAgent.getWeaponItemBase(true));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
} else if (aiAgent.getWeaponItemBase(false) != null) {
int attackDelay = (int) (aiAgent.getSpeedHandTwo() * 100);
if (aiAgent.isSiege())
attackDelay = 3000;
CombatUtilities.combatCycle(aiAgent, mob, false, aiAgent.getWeaponItemBase(false));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
}
return;
}
if (!MovementUtilities.updateMovementToCharacter(aiAgent, mob))
return;
if (!MovementUtilities.canMove(aiAgent))
return;
double WeaponRange = aiAgent.getEquip().get(0).getItemBase().getRange();
if (CombatUtilities.inRange2D(aiAgent, mob, WeaponRange))
return;
aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, mob);
MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange());
}
private static void awakeNPCguard(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
// Player guards are bound to their city zone
// and recall when leaving it.
if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) {
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
//Don't attempt to aggro if No aggro is on and aiAgent is not home yet.
//no players currently have this mob loaded. return to IDLE.
//currently npc guards wont patrol or aggro
//Get the Map for Players that loaded this mob.
HashSet<AbstractWorldObject> awoList = WorldGrid.getObjectsInRangePartial(aiAgent, 100, MBServerStatics.MASK_MOB);
for (AbstractWorldObject awoMob : awoList) {
//dont scan self.
if (aiAgent.equals(awoMob))
continue;
Mob mob = (Mob) awoMob;
//dont attack other guards
if (mob.isGuard())
continue;
if (aiAgent.getLoc().distanceSquared2D(mob.getLoc()) > sqr(50))
continue;
aiAgent.setCombatTarget(mob);
aiAgent.state = STATE.Attack;
}
}
private static void petAwake(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
PlayerCharacter petOwner = aiAgent.getOwner();
if (petOwner == null)
return;
//lets make mobs ai less twitchy, Don't call another movement until mob reaches it's destination.
if (aiAgent.isMoving())
return;
if (!MovementUtilities.canMove(aiAgent))
return;
if (petOwner.getLoc().distanceSquared2D(aiAgent.getLoc()) > MBServerStatics.AI_RECALL_RANGE * MBServerStatics.AI_RECALL_RANGE) {
aiAgent.teleport(petOwner.getLoc());
return;
}
if (petOwner.getLoc().distanceSquared2D(aiAgent.getLoc()) > 30 * 30) {
if (aiAgent.isMoving())
return;
if (!MovementUtilities.canMove(aiAgent))
return;
if (aiAgent.getLoc().distanceSquared2D(petOwner.getLoc()) < aiAgent.getRange() * aiAgent.getRange())
return;
MovementUtilities.moveToLocation(aiAgent, petOwner.getLoc(), aiAgent.getRange());
}
}
private static void aggro(Mob aiAgent, int targetID) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) {
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
if (!aiAgent.isCombat()) {
aiAgent.setCombat(true);
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(aiAgent);
DispatchMessage.sendToAllInRange(aiAgent, rwss);
}
//a player got in aggro range. Move to player until in range of attack.
PlayerCharacter aggroTarget = PlayerCharacter.getFromCache(targetID);
if (aggroTarget == null) {
// Logger.error("MobileFSM.aggro", "aggro target with UUID " + targetID + " returned null");
aiAgent.playerAgroMap.remove(targetID);
aiAgent.setAggroTargetID(0);
aiAgent.state = STATE.Patrol;
return;
}
if (!aiAgent.canSee(aggroTarget)) {
aiAgent.setCombatTarget(null);
targetID = 0;
aiAgent.state = STATE.Patrol;
return;
}
if (!aggroTarget.isActive()) {
aiAgent.setCombatTarget(null);
targetID = 0;
aiAgent.state = STATE.Patrol;
return;
}
aiAgent.setCombatTarget(aggroTarget);
if (canCast(aiAgent) == true) {
if (MobCast(aiAgent) == false) {
attack(aiAgent, targetID);
}
} else if (CombatUtilities.inRange2D(aiAgent, aggroTarget, aiAgent.getRange())) {
aiAgent.state = STATE.Attack;
attack(aiAgent, targetID);
return;
}
if (!MovementUtilities.inRangeDropAggro(aiAgent, aggroTarget)) {
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0);
aiAgent.state = STATE.Awake;
return;
}
if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) {
aiAgent.setCombatTarget(null);
aiAgent.setAggroTargetID(0);
aiAgent.state = STATE.Home;
return;
}
//use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check.
if (CombatUtilities.inRangeToAttack2D(aiAgent, aggroTarget))
return;
if (!MovementUtilities.updateMovementToCharacter(aiAgent, aggroTarget))
return;
if (!MovementUtilities.canMove(aiAgent))
return;
if (aiAgent.getLoc().distanceSquared2D(aggroTarget.getLoc()) < aiAgent.getRange() * aiAgent.getRange())
return;
aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, aggroTarget);
MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange());
}
private static void petAttack(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
AbstractGameObject target = aiAgent.getCombatTarget();
if (target == null) {
aiAgent.state = STATE.Awake;
return;
}
switch (target.getObjectType()) {
case PlayerCharacter:
PlayerCharacter player = (PlayerCharacter) target;
if (!player.isActive()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (player.inSafeZone()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
handlePlayerAttackForPet(aiAgent, player);
break;
case Building:
Building building = (Building) target;
petHandleBuildingAttack(aiAgent, building);
break;
case Mob:
Mob mob = (Mob) target;
handleMobAttackForPet(aiAgent, mob);
break;
}
}
private static void mobAttack(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) {
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
AbstractGameObject target = aiAgent.getCombatTarget();
if (target == null) {
aiAgent.state = STATE.Patrol;
return;
}
switch (target.getObjectType()) {
case PlayerCharacter:
PlayerCharacter player = (PlayerCharacter) target;
if (!player.isActive()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Patrol;
return;
}
if (aiAgent.isNecroPet() && player.inSafeZone()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Idle;
return;
}
if (canCast(aiAgent) == true) {
if (MobCast(aiAgent) == false) {
handlePlayerAttackForMob(aiAgent, player);
}
} else {
handlePlayerAttackForMob(aiAgent, player);
}
break;
case Building:
Building building = (Building) target;
petHandleBuildingAttack(aiAgent, building);
break;
case Mob:
Mob mob = (Mob) target;
handleMobAttackForMob(aiAgent, mob);
}
}
private static void petHandleBuildingAttack(Mob aiAgent, Building building) {
int buildingHitBox = (int) CombatManager.calcHitBox(building);
if (building.getRank() == -1) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (!building.isVulnerable()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (BuildingManager.getBuildingFromCache(building.getObjectUUID()) == null) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (building.getParentZone() != null && building.getParentZone().isPlayerCity()) {
for (Mob mob : building.getParentZone().zoneMobSet) {
if (!mob.isPlayerGuard())
continue;
if (mob.getCombatTarget() != null)
continue;
if (mob.getGuild() != null && building.getGuild() != null)
if (!Guild.sameGuild(mob.getGuild().getNation(), building.getGuild().getNation()))
continue;
mob.setCombatTarget(aiAgent);
mob.state = STATE.Attack;
}
}
if (CombatUtilities.inRangeToAttack(aiAgent, building)) {
//not time to attack yet.
if (!CombatUtilities.RunAIRandom())
return;
if (System.currentTimeMillis() < aiAgent.getLastAttackTime())
return;
if (aiAgent.getRange() >= 30 && aiAgent.isMoving())
return;
//reset attack animation
if (aiAgent.isSiege())
MovementManager.sendRWSSMsg(aiAgent);
// Fire siege balls
// TODO: Fix animations not following stone
//no weapons, defualt mob attack speed 3 seconds.
ItemBase mainHand = aiAgent.getWeaponItemBase(true);
ItemBase offHand = aiAgent.getWeaponItemBase(false);
if (mainHand == null && offHand == null) {
CombatUtilities.combatCycle(aiAgent, building, true, null);
int delay = 3000;
if (aiAgent.isSiege())
delay = 15000;
aiAgent.setLastAttackTime(System.currentTimeMillis() + delay);
} else
//TODO set offhand attack time.
if (aiAgent.getWeaponItemBase(true) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 15000;
CombatUtilities.combatCycle(aiAgent, building, true, aiAgent.getWeaponItemBase(true));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
} else if (aiAgent.getWeaponItemBase(false) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 15000;
CombatUtilities.combatCycle(aiAgent, building, false, aiAgent.getWeaponItemBase(false));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
}
if (aiAgent.isSiege()) {
PowerProjectileMsg ppm = new PowerProjectileMsg(aiAgent, building);
ppm.setRange(50);
DispatchMessage.dispatchMsgToInterestArea(aiAgent, ppm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
}
return;
}
//Outside of attack Range, Move to players predicted loc.
if (!aiAgent.isMoving())
if (MovementUtilities.canMove(aiAgent))
MovementUtilities.moveToLocation(aiAgent, building.getLoc(), aiAgent.getRange() + buildingHitBox);
}
private static void handlePlayerAttackForPet(Mob aiAgent, PlayerCharacter player) {
if (aiAgent.getMobBase().getSeeInvis() < player.getHidden()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (!player.isAlive()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (CombatUtilities.inRangeToAttack(aiAgent, player)) {
//not time to attack yet.
if (System.currentTimeMillis() < aiAgent.getLastAttackTime())
return;
if (!CombatUtilities.RunAIRandom())
return;
if (aiAgent.getRange() >= 30 && aiAgent.isMoving())
return;
// add timer for last attack.
//player.setTimeStamp("LastCombatPlayer", System.currentTimeMillis());
//no weapons, defualt mob attack speed 3 seconds.
ItemBase mainHand = aiAgent.getWeaponItemBase(true);
ItemBase offHand = aiAgent.getWeaponItemBase(false);
if (mainHand == null && offHand == null) {
CombatUtilities.combatCycle(aiAgent, player, true, null);
int delay = 3000;
if (aiAgent.isSiege())
delay = 11000;
aiAgent.setLastAttackTime(System.currentTimeMillis() + delay);
}
//TODO set offhand attack time.
if (aiAgent.getWeaponItemBase(true) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 11000;
CombatUtilities.combatCycle(aiAgent, player, true, aiAgent.getWeaponItemBase(true));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
} else if (aiAgent.getWeaponItemBase(false) != null) {
int attackDelay = (int) (aiAgent.getSpeedHandTwo() * 100);
if (aiAgent.isSiege())
attackDelay = 3000;
CombatUtilities.combatCycle(aiAgent, player, false, aiAgent.getWeaponItemBase(false));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
}
return;
}
if (!MovementUtilities.updateMovementToCharacter(aiAgent, player))
return;
//out of range to attack move
if (!MovementUtilities.canMove(aiAgent))
return;
aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, player);
MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange());
}
private static void handlePlayerAttackForMob(Mob aiAgent, PlayerCharacter player) {
if (aiAgent.getMobBase().getSeeInvis() < player.getHidden()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (!player.isAlive()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (aiAgent.getMobBase().getFlags().contains(Enum.MobFlagType.CALLSFORHELP)) {
MobCallForHelp(aiAgent);
}
if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) {
aiAgent.setCombatTarget(null);
aiAgent.setAggroTargetID(0);
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
if (!MovementUtilities.inRangeDropAggro(aiAgent, player)) {
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0);
aiAgent.state = STATE.Awake;
return;
}
if (CombatUtilities.inRange2D(aiAgent, player, aiAgent.getRange())) {
//no weapons, defualt mob attack speed 3 seconds.
if (System.currentTimeMillis() < aiAgent.getLastAttackTime())
return;
//if (!CombatUtilities.RunAIRandom())
// return;
// ranged mobs cant attack while running. skip until they finally stop.
//if (aiAgent.getRange() >= 30 && aiAgent.isMoving())
if(aiAgent.isMoving())
return;
// add timer for last attack.
// player.setTimeStamp("LastCombatPlayer", System.currentTimeMillis());
ItemBase mainHand = aiAgent.getWeaponItemBase(true);
ItemBase offHand = aiAgent.getWeaponItemBase(false);
if (mainHand == null && offHand == null) {
CombatUtilities.combatCycle(aiAgent, player, true, null);
int delay = 3000;
if (aiAgent.isSiege())
delay = 11000;
aiAgent.setLastAttackTime(System.currentTimeMillis() + delay);
} else
//TODO set offhand attack time.
if (aiAgent.getWeaponItemBase(true) != null) {
int delay = 3000;
if (aiAgent.isSiege())
delay = 11000;
CombatUtilities.combatCycle(aiAgent, player, true, aiAgent.getWeaponItemBase(true));
aiAgent.setLastAttackTime(System.currentTimeMillis() + delay);
} else if (aiAgent.getWeaponItemBase(false) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 11000;
if (aiAgent.getMobBase().getFlags().contains(Enum.MobFlagType.CALLSFORHELP)) {
MobCallForHelp(aiAgent);
}
CombatUtilities.combatCycle(aiAgent, player, false, aiAgent.getWeaponItemBase(false));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
}
return;
}
if (!MovementUtilities.updateMovementToCharacter(aiAgent, player))
return;
if (!MovementUtilities.canMove(aiAgent))
return;
//this stops mobs from attempting to move while they are underneath a player.
if (CombatUtilities.inRangeToAttack2D(aiAgent, player))
return;
//set mob to pursue target
aiAgent.state = STATE.Chase;
}
private static void handleMobAttackForPet(Mob aiAgent, Mob mob) {
if (!mob.isAlive()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (CombatUtilities.inRangeToAttack(aiAgent, mob)) {
//not time to attack yet.
if (System.currentTimeMillis() < aiAgent.getLastAttackTime())
return;
if (!CombatUtilities.RunAIRandom())
return;
if (aiAgent.getRange() >= 30 && aiAgent.isMoving())
return;
//no weapons, defualt mob attack speed 3 seconds.
ItemBase mainHand = aiAgent.getWeaponItemBase(true);
ItemBase offHand = aiAgent.getWeaponItemBase(false);
if (mainHand == null && offHand == null) {
CombatUtilities.combatCycle(aiAgent, mob, true, null);
int delay = 3000;
if (aiAgent.isSiege())
delay = 11000;
aiAgent.setLastAttackTime(System.currentTimeMillis() + delay);
} else
//TODO set offhand attack time.
if (aiAgent.getWeaponItemBase(true) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 11000;
CombatUtilities.combatCycle(aiAgent, mob, true, aiAgent.getWeaponItemBase(true));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
} else if (aiAgent.getWeaponItemBase(false) != null) {
int attackDelay = (int) (aiAgent.getSpeedHandTwo() * 100);
if (aiAgent.isSiege())
attackDelay = 3000;
CombatUtilities.combatCycle(aiAgent, mob, false, aiAgent.getWeaponItemBase(false));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
}
return;
}
if (!MovementUtilities.updateMovementToCharacter(aiAgent, mob))
return;
if (!MovementUtilities.canMove(aiAgent))
return;
if (CombatUtilities.inRangeToAttack2D(aiAgent, mob))
return;
aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, mob);
MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange());
}
private static void handleMobAttackForMob(Mob aiAgent, Mob mob) {
if (!mob.isAlive()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
return;
}
if (CombatUtilities.inRangeToAttack(aiAgent, mob)) {
//not time to attack yet.
if (System.currentTimeMillis() < aiAgent.getLastAttackTime()) {
return;
}
if (!CombatUtilities.RunAIRandom())
return;
if (aiAgent.getRange() >= 30 && aiAgent.isMoving())
return;
//no weapons, defualt mob attack speed 3 seconds.
ItemBase mainHand = aiAgent.getWeaponItemBase(true);
ItemBase offHand = aiAgent.getWeaponItemBase(false);
if (mainHand == null && offHand == null) {
CombatUtilities.combatCycle(aiAgent, mob, true, null);
int delay = 3000;
if (aiAgent.isSiege())
delay = 11000;
aiAgent.setLastAttackTime(System.currentTimeMillis() + delay);
} else
//TODO set offhand attack time.
if (aiAgent.getWeaponItemBase(true) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 11000;
CombatUtilities.combatCycle(aiAgent, mob, true, aiAgent.getWeaponItemBase(true));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
} else if (aiAgent.getWeaponItemBase(false) != null) {
int attackDelay = 3000;
if (aiAgent.isSiege())
attackDelay = 11000;
CombatUtilities.combatCycle(aiAgent, mob, false, aiAgent.getWeaponItemBase(false));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
}
return;
}
//use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check.
if (CombatUtilities.inRangeToAttack2D(aiAgent, mob))
return;
if (!MovementUtilities.updateMovementToCharacter(aiAgent, mob))
return;
//out of range to attack move
if (!MovementUtilities.canMove(aiAgent))
return;
aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, mob);
MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange());
}
private static void attack(Mob aiAgent, int targetID) {
//in range to attack, start attacking now!
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
PlayerCharacter aggroTarget = PlayerCharacter.getFromCache(targetID);
if (aggroTarget == null) {
// Logger.error("MobileFSM.aggro", "aggro target with UUID " + targetID + " returned null");
aiAgent.playerAgroMap.remove(targetID);
aiAgent.setAggroTargetID(0);
aiAgent.state = STATE.Patrol;
return;
}
if (aiAgent.getMobBase().getSeeInvis() < aggroTarget.getHidden()) {
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Patrol;
return;
}
if (!aggroTarget.isAlive()) {
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Patrol;
return;
}
if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) {
aiAgent.setCombatTarget(null);
aiAgent.setAggroTargetID(0);
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
if (!MovementUtilities.inRangeDropAggro(aiAgent, aggroTarget)) {
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0);
aiAgent.state = STATE.Awake;
return;
}
if (CombatUtilities.inRangeToAttack(aiAgent, aggroTarget)) {
if (aiAgent.getCombatTarget() == null)
aiAgent.setCombatTarget(aggroTarget);
if (!CombatUtilities.RunAIRandom())
return;
//not time to attack yet.
if (System.currentTimeMillis() < aiAgent.getLastAttackTime())
return;
if (aiAgent.getRange() >= 30 && aiAgent.isMoving())
return;
//no weapons, defualt mob attack speed 3 seconds.
ItemBase mainHand = aiAgent.getWeaponItemBase(true);
ItemBase offHand = aiAgent.getWeaponItemBase(false);
if (mainHand == null && offHand == null) {
CombatUtilities.combatCycle(aiAgent, aggroTarget, true, null);
aiAgent.setLastAttackTime(System.currentTimeMillis() + 3000);
} else
//TODO set offhand attack time.
if (aiAgent.getWeaponItemBase(true) != null) {
int attackDelay = 3000;
CombatUtilities.combatCycle(aiAgent, aggroTarget, true, aiAgent.getWeaponItemBase(true));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
} else if (aiAgent.getWeaponItemBase(false) != null) {
int attackDelay = 3000;
CombatUtilities.combatCycle(aiAgent, aggroTarget, false, aiAgent.getWeaponItemBase(false));
aiAgent.setLastAttackTime(System.currentTimeMillis() + attackDelay);
}
return;
}
//use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check.
if (CombatUtilities.inRangeToAttack2D(aiAgent, aggroTarget))
return;
if (!MovementUtilities.canMove(aiAgent))
return;
if (!MovementUtilities.updateMovementToCharacter(aiAgent, aggroTarget))
return;
aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, aggroTarget);
MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange());
}
private static void home(Mob aiAgent, boolean walk) {
//recall home.
MovementManager.translocate(aiAgent, aiAgent.getBindLoc(), null);
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
}
private static void recalling(Mob aiAgent) {
//recall home.
if (aiAgent.getLoc() == aiAgent.getBindLoc())
aiAgent.state = STATE.Awake;
if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) {
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
}
}
private static void patrol(Mob aiAgent) {
MobBase mobbase = aiAgent.getMobBase();
if (mobbase != null && (Enum.MobFlagType.SENTINEL.elementOf(mobbase.getFlags()) || !Enum.MobFlagType.CANROAM.elementOf(mobbase.getFlags()))) {
aiAgent.state = STATE.Awake;
return;
}
if (MovementUtilities.canMove(aiAgent) && !aiAgent.isMoving()) {
float patrolRadius = aiAgent.getSpawnRadius();
if (patrolRadius > 256)
patrolRadius = 256;
if (patrolRadius < 60)
patrolRadius = 60;
MovementUtilities.aiMove(aiAgent, Vector3fImmutable.getRandomPointInCircle(aiAgent.getBindLoc(), patrolRadius), true);
}
aiAgent.state = STATE.Awake;
}
private static void dead(Mob aiAgent) {
//Despawn Timer with Loot currently in inventory.
if (aiAgent.getCharItemManager().getInventoryCount() > 0) {
if (System.currentTimeMillis() > aiAgent.deathTime + MBServerStatics.DESPAWN_TIMER_WITH_LOOT) {
aiAgent.despawn();
//update time of death after mob despawns so respawn time happens after mob despawns.
aiAgent.setDeathTime(System.currentTimeMillis());
aiAgent.state = STATE.Respawn;
}
//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();
//update time of death after mob despawns so respawn time happens after mob despawns.
aiAgent.setDeathTime(System.currentTimeMillis());
aiAgent.state = STATE.Respawn;
}
//Mob never had Loot.
} else {
if (System.currentTimeMillis() > aiAgent.deathTime + MBServerStatics.DESPAWN_TIMER) {
aiAgent.despawn();
//update time of death after mob despawns so respawn time happens after mob despawns.
aiAgent.setDeathTime(System.currentTimeMillis());
aiAgent.state = STATE.Respawn;
}
}
}
}
private static void guardAwake(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
if (aiAgent.getLoc().distanceSquared2D(aiAgent.getBindLoc()) > sqr(2000)) {
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
//Don't attempt to aggro if No aggro is on and aiAgent is not home yet.
//Mob stopped Moving let's turn aggro back on.
if (aiAgent.isNoAggro())
aiAgent.setNoAggro(false);
// do nothing if no players are around.
if (aiAgent.playerAgroMap.isEmpty())
return;
//Get the Map for Players that loaded this mob.
ConcurrentHashMap<Integer, Boolean> loadedPlayers = aiAgent.playerAgroMap;
//no players currently have this mob loaded. return to IDLE.
//aiAgent finished moving home, set aggro on.
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) {
// Logger.error("MobileFSM", "Player with UID " + playerID + " returned null in mob.getPlayerAgroMap()");
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;
}
//Guard aggro check
boolean aggro = false;
Zone cityZone = aiAgent.getParentZone();
if (cityZone != null) {
City city = City.GetCityFromCache(cityZone.getPlayerCityUUID());
if (city != null) {
Building tol = city.getTOL();
if (tol != null) {
if (tol.reverseKOS) {
aggro = true;
for (Condemned condemned : tol.getCondemned().values()) {
switch (condemned.getFriendType()) {
case Condemned.NATION:
if (loadedPlayer.getGuild() != null && loadedPlayer.getGuild().getNation() != null)
if (loadedPlayer.getGuild().getNation().getObjectUUID() == condemned.getGuildUID())
if (condemned.isActive())
aggro = false;
break;
case Condemned.GUILD:
if (loadedPlayer.getGuild() != null)
if (loadedPlayer.getGuild().getObjectUUID() == condemned.getGuildUID())
if (condemned.isActive())
aggro = false;
break;
case Condemned.INDIVIDUAL:
if (loadedPlayer.getObjectUUID() == condemned.getPlayerUID())
if (condemned.isActive())
aggro = false;
break;
}
}
} else {
aggro = false;
for (Condemned condemned : tol.getCondemned().values()) {
switch (condemned.getFriendType()) {
case Condemned.NATION:
if (loadedPlayer.getGuild() != null && loadedPlayer.getGuild().getNation() != null)
if (loadedPlayer.getGuild().getNation().getObjectUUID() == condemned.getGuildUID())
if (condemned.isActive())
aggro = true;
break;
case Condemned.GUILD:
if (loadedPlayer.getGuild() != null)
if (loadedPlayer.getGuild().getObjectUUID() == condemned.getGuildUID())
if (condemned.isActive())
aggro = true;
break;
case Condemned.INDIVIDUAL:
if (loadedPlayer.getObjectUUID() == condemned.getPlayerUID())
if (condemned.isActive())
aggro = true;
break;
}
}
}
}
}
if (loadedPlayer.getGuild() != null && loadedPlayer.getGuild().getNation() != null && city.getGuild() != null)
if (Guild.sameGuild(loadedPlayer.getGuild().getNation(), city.getGuild().getNation()))
aggro = false;
}
//lets make sure we dont aggro players in the nation.
if (aggro) {
if (CombatUtilities.inRangeToAttack(aiAgent, loadedPlayer)) {
aiAgent.setAggroTargetID(playerID);
aiAgent.state = STATE.Aggro;
return;
}
if (MovementUtilities.inRangeToAggro(aiAgent, loadedPlayer)) {
aiAgent.setAggroTargetID(playerID);
aiAgent.state = STATE.Aggro;
return;
}
}
}
//attempt to patrol even if aiAgent isn't aggresive;
if (aiAgent.isMoving() == false)
aiAgent.state = STATE.Patrol;
}
private static void guardAggro(Mob aiAgent, int targetID) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
if (!aiAgent.isCombat()) {
aiAgent.setCombat(true);
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(aiAgent);
DispatchMessage.sendToAllInRange(aiAgent, rwss);
}
//a player got in aggro range. Move to player until in range of attack.
PlayerCharacter aggroTarget = PlayerCharacter.getFromCache(targetID);
if (aggroTarget == null) {
aiAgent.state = STATE.Patrol;
return;
}
if (!aiAgent.canSee(aggroTarget)) {
aiAgent.setCombatTarget(null);
targetID = 0;
aiAgent.state = STATE.Patrol;
return;
}
if (!aggroTarget.isActive()) {
aiAgent.setCombatTarget(null);
targetID = 0;
aiAgent.state = STATE.Patrol;
return;
}
if (CombatUtilities.inRangeToAttack(aiAgent, aggroTarget)) {
aiAgent.setCombatTarget(aggroTarget);
aiAgent.state = STATE.Attack;
guardAttack(aiAgent);
return;
}
//use this so mobs dont continue to try to move if they are underneath a flying target. only use 2D range check.
if (CombatUtilities.inRangeToAttack2D(aiAgent, aggroTarget))
return;
if (!MovementUtilities.canMove(aiAgent))
return;
if (!MovementUtilities.inRangeDropAggro(aiAgent, aggroTarget)) {
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
MovementUtilities.moveToLocation(aiAgent, aiAgent.getTrueBindLoc(), 0);
aiAgent.state = STATE.Awake;
return;
}
if (!MovementUtilities.inRangeOfBindLocation(aiAgent)) {
aiAgent.setCombatTarget(null);
aiAgent.setAggroTargetID(0);
aiAgent.setWalkingHome(false);
aiAgent.state = STATE.Home;
return;
}
if (!MovementUtilities.updateMovementToCharacter(aiAgent, aggroTarget))
return;
//Outside of attack Range, Move to players predicted loc.
if (aiAgent.getLoc().distanceSquared2D(aggroTarget.getLoc()) < aiAgent.getRange() * aiAgent.getRange())
return;
aiAgent.destination = MovementUtilities.GetDestinationToCharacter(aiAgent, aggroTarget);
MovementUtilities.moveToLocation(aiAgent, aiAgent.destination, aiAgent.getRange());
}
private static void guardPatrol(Mob aiAgent) {
if (aiAgent.playerAgroMap.isEmpty()) {
aiAgent.state = STATE.Awake;
return;
}
if (aiAgent.isCombat() && aiAgent.getCombatTarget() == null) {
aiAgent.setCombat(false);
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(aiAgent);
DispatchMessage.sendToAllInRange(aiAgent, rwss);
}
if (aiAgent.npcOwner == null) {
if (!aiAgent.isWalk() || (aiAgent.isCombat() && aiAgent.getCombatTarget() == null)) {
aiAgent.setWalkMode(true);
aiAgent.setCombat(false);
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(aiAgent);
DispatchMessage.sendToAllInRange(aiAgent, rwss);
}
if (aiAgent.isMoving()) {
aiAgent.state = STATE.Awake;
return;
}
Building barrack = aiAgent.building;
if (barrack == null) {
aiAgent.state = STATE.Awake;
return;
}
int patrolRandom = ThreadLocalRandom.current().nextInt(1000);
if (patrolRandom <= 10) {
int buildingHitBox = (int) CombatManager.calcHitBox(barrack);
if (MovementUtilities.canMove(aiAgent)) {
MovementUtilities.aiMove(aiAgent, MovementUtilities.randomPatrolLocation(aiAgent, aiAgent.getBindLoc(), buildingHitBox * 2), true);
}
}
aiAgent.state = STATE.Awake;
return;
}
if (!aiAgent.isWalk() || (aiAgent.isCombat() && aiAgent.getCombatTarget() == null)) {
aiAgent.setWalkMode(true);
aiAgent.setCombat(false);
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(aiAgent);
DispatchMessage.sendToAllInRange(aiAgent, rwss);
}
Building barrack = ((Mob) aiAgent.npcOwner).building;
if (barrack == null) {
aiAgent.state = STATE.Awake;
return;
}
if (barrack.getPatrolPoints() == null) {
aiAgent.state = STATE.Awake;
return;
}
if (barrack.getPatrolPoints().isEmpty()) {
aiAgent.state = STATE.Awake;
return;
}
if (aiAgent.isMoving()) {
aiAgent.state = STATE.Awake;
return;
}
int patrolRandom = ThreadLocalRandom.current().nextInt(1000);
if (patrolRandom <= 10) {
if (aiAgent.getPatrolPointIndex() < barrack.getPatrolPoints().size()) {
Vector3fImmutable patrolLoc = barrack.getPatrolPoints().get(aiAgent.getPatrolPointIndex());
aiAgent.setPatrolPointIndex(aiAgent.getPatrolPointIndex() + 1);
if (aiAgent.getPatrolPointIndex() == barrack.getPatrolPoints().size())
aiAgent.setPatrolPointIndex(0);
if (patrolLoc != null) {
if (MovementUtilities.canMove(aiAgent)) {
MovementUtilities.aiMove(aiAgent, patrolLoc, true);
aiAgent.state = STATE.Awake;
}
}
}
}
aiAgent.state = STATE.Awake;
}
private static void guardAttack(Mob aiAgent) {
if (!aiAgent.isAlive()) {
aiAgent.state = STATE.Dead;
return;
}
AbstractGameObject target = aiAgent.getCombatTarget();
if (target == null) {
aiAgent.state = STATE.Patrol;
return;
}
switch (target.getObjectType()) {
case PlayerCharacter:
PlayerCharacter player = (PlayerCharacter) target;
if (!player.isActive()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Patrol;
return;
}
if (aiAgent.isNecroPet() && player.inSafeZone()) {
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Idle;
return;
}
if (canCast(aiAgent) == true) {
if (MobCast(aiAgent) == false) {
handlePlayerAttackForMob(aiAgent, player);
}
} else {
handlePlayerAttackForMob(aiAgent, player);
}
break;
case Building:
Logger.info("PLAYER GUARD ATTEMPTING TO ATTACK BUILDING IN " + aiAgent.getParentZone().getName());
aiAgent.state = STATE.Awake;
break;
case Mob:
Mob mob = (Mob) target;
handleMobAttackForMob(aiAgent, mob);
}
}
private static void guardHome(Mob aiAgent, boolean walk) {
//recall home.
PowersBase recall = PowersManager.getPowerByToken(-1994153779);
PowersManager.useMobPower(aiAgent, aiAgent, recall, 40);
aiAgent.setAggroTargetID(0);
aiAgent.setCombatTarget(null);
aiAgent.state = STATE.Awake;
}
private static void respawn(Mob aiAgent) {
if (!aiAgent.canRespawn())
return;
long spawnTime = aiAgent.getSpawnTime();
if (aiAgent.isPlayerGuard() && aiAgent.npcOwner != null && !aiAgent.npcOwner.isAlive())
return;
if (System.currentTimeMillis() > aiAgent.deathTime + spawnTime) {
aiAgent.respawn();
aiAgent.state = STATE.Idle;
}
}
private static void retaliate(Mob aiAgent) {
if (aiAgent.getCombatTarget() == null)
aiAgent.state = STATE.Awake;
//out of range to attack move
if (!MovementUtilities.canMove(aiAgent)) {
aiAgent.state = STATE.Attack;
return;
}
aiAgent.state = STATE.Attack;
//lets make mobs ai less twitchy, Don't call another movement until mob reaches it's destination.
if (aiAgent.isMoving())
return;
MovementUtilities.moveToLocation(aiAgent, aiAgent.getCombatTarget().getLoc(), aiAgent.getRange());
}
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(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<Integer> powerTokens;
ArrayList<Integer> purgeTokens;
PlayerCharacter target = (PlayerCharacter) mob.getCombatTarget();
if (mob.getMobBase().getFlags().contains(Enum.MobFlagType.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() == false || mobPower.targetSelf == true)
msg = PowersManager.createPowerMsg(mobPower, powerRank, mob, mob);
else
msg = PowersManager.createPowerMsg(mobPower, powerRank, mob, target);
msg.setUnknown04(2);
PowersManager.finishUseMobPower(msg, mob, 0, 0);
// Default minimum seconds between cast = 10
long coolDown = mobPower.getCooldown();
if (coolDown < 10000)
mob.nextCastTime = System.currentTimeMillis() + 10000 + coolDown;
else
mob.nextCastTime = System.currentTimeMillis() + coolDown;
return true;
}
return false;
}
public static void MobCallForHelp(Mob mob) {
if(mob.nextCallForHelp == 0){
mob.nextCallForHelp = System.currentTimeMillis();
}
2 years ago
if(mob.nextCallForHelp < System.currentTimeMillis()){
return;
}
Zone mobCamp = mob.getParentZone();
for (Mob mob1 : mobCamp.zoneMobSet) {
if (mob1.getMobBase().getFlags().contains(Enum.MobFlagType.RESPONDSTOCALLSFORHELP)) {
if (mob1.state == STATE.Awake) {
if (CombatUtilities.inRange2D(mob, mob1, mob.getAggroRange()) == true) {
MovementUtilities.moveToLocation(mob1, mob.getLoc(), 0);
}
}
}
}
//wait 60 seconds to call for help again
mob.nextCallForHelp = System.currentTimeMillis() + 60000;
}
public static void handleMobChase(Mob mob){
if (!MovementUtilities.inRangeOfBindLocation(mob)) {
mob.setCombatTarget(null);
mob.setAggroTargetID(0);
mob.setWalkingHome(false);
mob.state = STATE.Home;
return;
}
mob.updateMovementState();
mob.updateLocation();
if(CombatUtilities.inRange2D(mob,mob.getCombatTarget(), mob.getRange()) == true) {
MovementUtilities.moveToLocation(mob, mob.getLoc(), 0);
mob.state = STATE.Attack;
}
else {//if (mob.isMoving() == false){
if(mob.getRange() > 15) {
mob.destination = mob.getCombatTarget().getLoc();
MovementUtilities.moveToLocation(mob, mob.destination, 0);
} else{
mob.destination = MovementUtilities.GetDestinationToCharacter(mob, (AbstractCharacter) mob.getCombatTarget());
MovementUtilities.moveToLocation(mob, mob.destination, mob.getRange());
}
}
}
}