AI movement rework for return to spawn and long range paths.

This commit is contained in:
MobiusDevelopment
2022-07-22 23:34:26 +00:00
parent c9a3a26302
commit 563794ef55
140 changed files with 2367 additions and 951 deletions

View File

@ -660,7 +660,6 @@ public class Config
public static boolean ALLOW_RAID_BOSS_PETRIFIED;
public static boolean ALLOW_LOW_LEVEL_TRADE;
public static boolean USE_CHAT_FILTER;
public static int MONSTER_RETURN_DELAY;
public static boolean SCROLL_STACKABLE;
public static boolean ALLOW_CHAR_KILL_PROTECT;
public static int CLAN_LEADER_COLOR;
@ -1929,7 +1928,6 @@ public class Config
HERO_COUNT = customServerConfig.getInt("HeroCount", 1);
CRUMA_TOWER_LEVEL_RESTRICT = customServerConfig.getInt("CrumaTowerLevelRestrict", 56);
ALLOW_RAID_BOSS_PETRIFIED = customServerConfig.getBoolean("AllowRaidBossPetrified", true);
MONSTER_RETURN_DELAY = customServerConfig.getInt("MonsterReturnDelay", 1200);
SCROLL_STACKABLE = customServerConfig.getBoolean("ScrollStackable", false);
ALLOW_CHAR_KILL_PROTECT = customServerConfig.getBoolean("AllowLowLvlProtect", false);
CLAN_LEADER_COLOR_ENABLED = customServerConfig.getBoolean("ClanLeaderNameColorEnabled", true);

View File

@ -478,21 +478,29 @@ public class AttackableAI extends CreatureAI
}
}
// Check if the actor is a Guard
if (_actor instanceof Guard)
{
// Order to the Guard to return to its home location because there's no target to attack
((Guard) _actor).returnHome();
}
// If this is a festival monster, then it remains in the same location.
if (_actor instanceof FestivalMonster)
// if (npc instanceof FestivalMonster)
// {
// return;
// }
// Check if the mob should not return to spawn point
if (!npc.canReturnToSpawnPoint()
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
{
return;
}
// Check if the mob should not return to spawn point
if (!npc.canReturnToSpawnPoint())
// Order this attackable to return to its spawn because there's no target to attack
if (!npc.isWalker() && ((getTarget() == null) || (getTarget().isPlayer() && (!getTarget().getActingPlayer().isAlikeDead() || getTarget().getActingPlayer().getAppearance().isInvisible()))))
{
npc.setWalking();
npc.returnHome();
return;
}
// Do not leave dead player
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
{
return;
}
@ -570,11 +578,6 @@ public class AttackableAI extends CreatureAI
}
else
{
if ((Config.MONSTER_RETURN_DELAY > 0) && (npc instanceof Monster) && !npc.isAlikeDead() && !npc.isDead() && (npc.getSpawn() != null) && !npc.isInsideRadius2D(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ(), Config.MAX_DRIFT_RANGE))
{
((Monster) _actor).returnHome();
}
// If NPC with fixed coord
x1 = (npc.getSpawn().getX() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
y1 = (npc.getSpawn().getY() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;

View File

@ -31,6 +31,7 @@ import org.l2jmobius.gameserver.enums.ItemLocation;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.Skill;
import org.l2jmobius.gameserver.model.WorldObject;
import org.l2jmobius.gameserver.model.actor.Attackable;
import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Playable;
import org.l2jmobius.gameserver.model.actor.Player;
@ -741,6 +742,10 @@ public class CreatureAI extends AbstractAI
return;
}
if (_actor.isNpc() && _actor.isAttackable())
{
((Attackable) _actor).setReturningToSpawnPoint(false);
}
clientStoppedMoving();
// If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE

View File

@ -18,6 +18,7 @@ package org.l2jmobius.gameserver.handler.admincommandhandlers;
import java.util.StringTokenizer;
import org.l2jmobius.gameserver.ai.CtrlIntention;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.WorldObject;
@ -108,6 +109,16 @@ public class AdminEffects implements IAdminCommandHandler
activeChar.decayMe();
activeChar.broadcastUserInfo();
activeChar.spawnMe();
for (Creature target : activeChar.getKnownList().getKnownCharacters())
{
if ((target != null) && (target.getTarget() == activeChar))
{
target.setTarget(null);
target.abortAttack();
target.abortCast();
target.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
}
}
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
}
else
@ -123,6 +134,16 @@ public class AdminEffects implements IAdminCommandHandler
activeChar.decayMe();
activeChar.broadcastUserInfo();
activeChar.spawnMe();
for (Creature target : activeChar.getKnownList().getKnownCharacters())
{
if ((target != null) && (target.getTarget() == activeChar))
{
target.setTarget(null);
target.abortAttack();
target.abortCast();
target.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
}
}
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
}
else if (command.startsWith("admin_vis"))

View File

@ -580,6 +580,11 @@ public abstract class WorldObject
return false;
}
public boolean isWalker()
{
return false;
}
/**
* Calculates 2D distance between this WorldObject and given x, y, z.
* @param x the X coordinate

View File

@ -39,6 +39,7 @@ import org.l2jmobius.gameserver.instancemanager.EventDropManager;
import org.l2jmobius.gameserver.model.CommandChannel;
import org.l2jmobius.gameserver.model.DropCategory;
import org.l2jmobius.gameserver.model.DropData;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.Party;
import org.l2jmobius.gameserver.model.SoulCrystal;
import org.l2jmobius.gameserver.model.actor.instance.Door;
@ -3040,6 +3041,17 @@ public class Attackable extends Npc
_commandChannelLastAttack = channelLastAttack;
}
public void returnHome()
{
clearAggroList();
if (hasAI() && (getSpawn() != null))
{
setReturningToSpawnPoint(true);
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ()));
}
}
private static class CommandChannelTimer implements Runnable
{
private final Attackable _monster;

View File

@ -5389,10 +5389,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
// Movement checks.
if (Config.PATHFINDING > 0)
{
final double originalDistance = distance;
final int originalX = x;
final int originalY = y;
int originalX = x;
int originalY = y;
final int originalZ = z;
final double originalDistance = distance;
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
if (isOnGeodataPath())
@ -5452,31 +5452,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
{
// Path calculation -- overrides previous movement check
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
// If not found and it is an Attackable, attempt to find closest path to move location.
if (!found && isNpc() && isAttackable())
{
if (isPlayer() && !_isFlying && !isInWater)
int xMin = Math.min(curX, originalX);
int xMax = Math.max(curX, originalX);
int yMin = Math.min(curY, originalY);
int yMax = Math.max(curY, originalY);
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
xMin -= maxDiff;
xMax += maxDiff;
yMin -= maxDiff;
yMax += maxDiff;
int destinationX = 0;
int destinationY = 0;
double shortDistance = Double.MAX_VALUE;
double tempDistance;
List<AbstractNodeLoc> tempPath;
for (int sX = xMin; sX < xMax; sX += 500)
{
return;
for (int sY = yMin; sY < yMax; sY += 500)
{
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
if (tempDistance < shortDistance)
{
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceId(), false);
found = (tempPath != null) && (tempPath.size() > 1);
if (found)
{
shortDistance = tempDistance;
m.geoPath = tempPath;
destinationX = sX;
destinationY = sY;
}
}
}
}
found = (m.geoPath != null) && (m.geoPath.size() > 1);
if (found)
{
originalX = destinationX;
originalY = destinationY;
}
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
// {
// return;
// }
// if (isSummon() && !((Summon) this).getFollowStatus())
// {
// return;
// }
m.disregardingGeodata = true;
x = originalX;
y = originalY;
z = originalZ;
distance = originalDistance;
}
else
if (found)
{
m.onGeodataPathIndex = 0; // on first segment
m.onGeodataPathIndex = 0; // On first segment.
m.geoPathGtx = gtx;
m.geoPathGty = gty;
m.geoPathAccurateTx = originalX;
@ -5491,6 +5515,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
sin = dy / distance;
cos = dx / distance;
}
else // No path found.
{
if (isPlayer() && !_isFlying && !isInWater)
{
return;
}
m.disregardingGeodata = true;
x = originalX;
y = originalY;
z = originalZ;
distance = originalDistance;
}
}
}

View File

@ -110,9 +110,7 @@ public class Commander extends Attackable
return _homeY;
}
/**
* This method forces guard to return to home location previously set
*/
@Override
public void returnHome()
{
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))

View File

@ -100,15 +100,14 @@ public class FortSiegeGuard extends Attackable
return false;
}
/**
* This method forces guard to return to home location previously set
*/
@Override
public void returnHome()
{
if (getWalkSpeed() <= 0)
{
return;
}
if (!isInsideRadius2D(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ(), 40))
{
setReturningToSpawnPoint(true);

View File

@ -118,14 +118,13 @@ public class Guard extends Attackable
return _homeX;
}
/**
* Notify the GuardInstance to return to its home location (AI_INTENTION_MOVE_TO) and clear its _aggroList.
*/
@Override
public void returnHome()
{
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 150))
{
clearAggroList();
setReturningToSpawnPoint(true);
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(_homeX, _homeY, _homeZ, 0));
}
}

View File

@ -26,7 +26,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.knownlist.MonsterKnownList;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.spawn.Spawn;
import org.l2jmobius.gameserver.util.MinionList;
/**
@ -68,18 +67,6 @@ public class Monster extends Attackable
return (MonsterKnownList) super.getKnownList();
}
public void returnHome()
{
ThreadPool.schedule(() ->
{
final Spawn mobSpawn = getSpawn();
if (!isInCombat() && !isAlikeDead() && !isDead() && (mobSpawn != null) && !isInsideRadius2D(mobSpawn.getX(), mobSpawn.getY(), mobSpawn.getZ(), Config.MAX_DRIFT_RANGE))
{
teleToLocation(mobSpawn.getX(), mobSpawn.getY(), mobSpawn.getZ());
}
}, Config.MONSTER_RETURN_DELAY * 1000);
}
/**
* Return True if the attacker is not another Monster.
* @param attacker the attacker

View File

@ -68,6 +68,12 @@ public class NpcWalker extends Npc
((NpcWalkerAI) getAI()).setHomeZ(getZ());
}
@Override
public boolean isWalker()
{
return true;
}
/**
* Sends a chat to all _knowObjects.
* @param chat message to say

View File

@ -109,9 +109,7 @@ public class SiegeGuard extends Attackable
return _homeY;
}
/**
* This method forces guard to return to home location previously set
*/
@Override
public void returnHome()
{
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))

View File

@ -24,7 +24,6 @@ import java.util.logging.Logger;
import org.l2jmobius.Config;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.sql.TerritoryTable;
import org.l2jmobius.gameserver.data.xml.WalkerRouteData;
import org.l2jmobius.gameserver.data.xml.ZoneData;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.instancemanager.IdManager;
@ -449,7 +448,7 @@ public class Spawn
final WaterZone water = ZoneData.getInstance().getZone(newlocx, newlocy, newlocz, WaterZone.class);
// If random spawn system is enabled.
if (Config.ENABLE_RANDOM_MONSTER_SPAWNS && npc.isMonster() && !npc.isQuestMonster() && (WalkerRouteData.getInstance().getRouteForNpc(npc.getNpcId()) == null) && (getInstanceId() == 0) && !npc.isRaid() && !npc.isMinion() && !npc.isFlying() && (water == null) && !Config.MOBS_LIST_NOT_RANDOM.contains(npc.getNpcId()))
if (Config.ENABLE_RANDOM_MONSTER_SPAWNS && npc.isMonster() && !npc.isQuestMonster() && !npc.isWalker() && (getInstanceId() == 0) && !npc.isRaid() && !npc.isMinion() && !npc.isFlying() && (water == null) && !Config.MOBS_LIST_NOT_RANDOM.contains(npc.getNpcId()))
{
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);