|
@@ -16,6 +16,7 @@
|
|
* You should have received a copy of the GNU General Public License
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
*/
|
|
|
|
+
|
|
package com.l2jserver.gameserver.instancemanager;
|
|
package com.l2jserver.gameserver.instancemanager;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
@@ -33,7 +34,10 @@ import com.l2jserver.gameserver.engines.DocumentParser;
|
|
import com.l2jserver.gameserver.model.L2CharPosition;
|
|
import com.l2jserver.gameserver.model.L2CharPosition;
|
|
import com.l2jserver.gameserver.model.L2NpcWalkerNode;
|
|
import com.l2jserver.gameserver.model.L2NpcWalkerNode;
|
|
import com.l2jserver.gameserver.model.L2WalkRoute;
|
|
import com.l2jserver.gameserver.model.L2WalkRoute;
|
|
|
|
+import com.l2jserver.gameserver.model.Location;
|
|
import com.l2jserver.gameserver.model.actor.L2Npc;
|
|
import com.l2jserver.gameserver.model.actor.L2Npc;
|
|
|
|
+import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
|
|
|
|
+import com.l2jserver.gameserver.model.quest.Quest;
|
|
import com.l2jserver.gameserver.network.NpcStringId;
|
|
import com.l2jserver.gameserver.network.NpcStringId;
|
|
import com.l2jserver.util.Rnd;
|
|
import com.l2jserver.util.Rnd;
|
|
|
|
|
|
@@ -48,48 +52,171 @@ public class WalkingManager extends DocumentParser
|
|
private static final byte REPEAT_GO_FIRST = 1;
|
|
private static final byte REPEAT_GO_FIRST = 1;
|
|
private static final byte REPEAT_TELE_FIRST = 2;
|
|
private static final byte REPEAT_TELE_FIRST = 2;
|
|
private static final byte REPEAT_RANDOM = 3;
|
|
private static final byte REPEAT_RANDOM = 3;
|
|
-
|
|
|
|
|
|
+
|
|
protected Map<Integer, L2WalkRoute> _routes = new HashMap<>(); // all available routes
|
|
protected Map<Integer, L2WalkRoute> _routes = new HashMap<>(); // all available routes
|
|
private final Map<Integer, WalkInfo> _activeRoutes = new HashMap<>(); // each record represents NPC, moving by predefined route from _routes, and moving progress
|
|
private final Map<Integer, WalkInfo> _activeRoutes = new HashMap<>(); // each record represents NPC, moving by predefined route from _routes, and moving progress
|
|
-
|
|
|
|
|
|
+ private final Map<Integer, NpcRoutesHolder> _routesToAttach = new HashMap<>(); // each record represents NPC and all available routes for it
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Holds depending between NPC's spawn point and route
|
|
|
|
+ */
|
|
|
|
+ private class NpcRoutesHolder
|
|
|
|
+ {
|
|
|
|
+ private final Map<String, Integer> _correspondences;
|
|
|
|
+
|
|
|
|
+ public NpcRoutesHolder()
|
|
|
|
+ {
|
|
|
|
+ _correspondences = new HashMap<>();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Add correspondence between specific route and specific spawn point
|
|
|
|
+ * @param routeId id of route
|
|
|
|
+ * @param loc Location of spawn point
|
|
|
|
+ */
|
|
|
|
+ public void addRoute(int routeId, Location loc)
|
|
|
|
+ {
|
|
|
|
+ _correspondences.put(getUniqueKey(loc), routeId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param npc
|
|
|
|
+ * @return route id for given NPC.
|
|
|
|
+ */
|
|
|
|
+ public int getRouteId(L2Npc npc)
|
|
|
|
+ {
|
|
|
|
+ if (npc.getSpawn() != null)
|
|
|
|
+ {
|
|
|
|
+ String key = getUniqueKey(npc.getSpawn().getSpawnLocation());
|
|
|
|
+ return _correspondences.containsKey(key) ? _correspondences.get(key) : -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param loc
|
|
|
|
+ * @return unique text string for given Location.
|
|
|
|
+ */
|
|
|
|
+ private String getUniqueKey(Location loc)
|
|
|
|
+ {
|
|
|
|
+ return (loc.getX() + "-" + loc.getY() + "-" + loc.getZ());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Holds info about current walk progress
|
|
|
|
+ */
|
|
private class WalkInfo
|
|
private class WalkInfo
|
|
{
|
|
{
|
|
protected ScheduledFuture<?> _walkCheckTask;
|
|
protected ScheduledFuture<?> _walkCheckTask;
|
|
protected boolean _blocked = false;
|
|
protected boolean _blocked = false;
|
|
protected boolean _suspended = false;
|
|
protected boolean _suspended = false;
|
|
- protected boolean _nodeArrived = false;
|
|
|
|
|
|
+ protected boolean _stoppedByAttack = false;
|
|
protected int _currentNode = 0;
|
|
protected int _currentNode = 0;
|
|
protected boolean _forward = true; // Determines first --> last or first <-- last direction
|
|
protected boolean _forward = true; // Determines first --> last or first <-- last direction
|
|
private final int _routeId;
|
|
private final int _routeId;
|
|
-
|
|
|
|
|
|
+ protected long _lastActionTime; // Debug field
|
|
|
|
+
|
|
public WalkInfo(int routeId)
|
|
public WalkInfo(int routeId)
|
|
{
|
|
{
|
|
_routeId = routeId;
|
|
_routeId = routeId;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return id of route of this WalkInfo.
|
|
|
|
+ */
|
|
protected L2WalkRoute getRoute()
|
|
protected L2WalkRoute getRoute()
|
|
{
|
|
{
|
|
return _routes.get(_routeId);
|
|
return _routes.get(_routeId);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return current node of this WalkInfo.
|
|
|
|
+ */
|
|
protected L2NpcWalkerNode getCurrentNode()
|
|
protected L2NpcWalkerNode getCurrentNode()
|
|
{
|
|
{
|
|
return getRoute().getNodeList().get(_currentNode);
|
|
return getRoute().getNodeList().get(_currentNode);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Calculate next node for this WalkInfo and send debug message from given npc
|
|
|
|
+ * @param npc NPC to debug message to be sent from
|
|
|
|
+ */
|
|
|
|
+ protected void calculateNextNode(L2Npc npc)
|
|
|
|
+ {
|
|
|
|
+ // Check this first, within the bounds of random moving, we have no conception of "first" or "last" node
|
|
|
|
+ if (getRoute().getRepeatType() == REPEAT_RANDOM)
|
|
|
|
+ {
|
|
|
|
+ int newNode = _currentNode;
|
|
|
|
+
|
|
|
|
+ while (newNode == _currentNode)
|
|
|
|
+ {
|
|
|
|
+ newNode = Rnd.get(getRoute().getNodesCount());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _currentNode = newNode;
|
|
|
|
+ npc.sendDebugMessage("Route id: " + getRoute().getId() + ", next random node is " + _currentNode);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ if (_forward)
|
|
|
|
+ {
|
|
|
|
+ _currentNode++;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ _currentNode--;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (_currentNode == getRoute().getNodesCount()) // Last node arrived
|
|
|
|
+ {
|
|
|
|
+ npc.sendDebugMessage("Route id: " + getRoute().getId() + ", last node arrived");
|
|
|
|
+
|
|
|
|
+ if (!getRoute().repeatWalk())
|
|
|
|
+ {
|
|
|
|
+ cancelMoving(npc);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (getRoute().getRepeatType())
|
|
|
|
+ {
|
|
|
|
+ case REPEAT_GO_BACK:
|
|
|
|
+ _forward = false;
|
|
|
|
+ _currentNode -= 2;
|
|
|
|
+ break;
|
|
|
|
+ case REPEAT_GO_FIRST:
|
|
|
|
+ _currentNode = 0;
|
|
|
|
+ break;
|
|
|
|
+ case REPEAT_TELE_FIRST:
|
|
|
|
+ npc.teleToLocation(npc.getSpawn().getLocx(), npc.getSpawn().getLocy(), npc.getSpawn().getLocz());
|
|
|
|
+ _currentNode = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ else if (_currentNode == -1) // First node arrived, when direction is first <-- last
|
|
|
|
+ {
|
|
|
|
+ _currentNode = 1;
|
|
|
|
+ _forward = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
protected WalkingManager()
|
|
protected WalkingManager()
|
|
{
|
|
{
|
|
load();
|
|
load();
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
public final void load()
|
|
public final void load()
|
|
{
|
|
{
|
|
parseDatapackFile("data/Routes.xml");
|
|
parseDatapackFile("data/Routes.xml");
|
|
_log.info(getClass().getSimpleName() + ": Loaded " + _routes.size() + " walking routes.");
|
|
_log.info(getClass().getSimpleName() + ": Loaded " + _routes.size() + " walking routes.");
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
protected void parseDocument()
|
|
protected void parseDocument()
|
|
{
|
|
{
|
|
@@ -122,7 +249,7 @@ public class WalkingManager extends DocumentParser
|
|
{
|
|
{
|
|
repeatType = -1;
|
|
repeatType = -1;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
final List<L2NpcWalkerNode> list = new ArrayList<>();
|
|
final List<L2NpcWalkerNode> list = new ArrayList<>();
|
|
for (Node r = d.getFirstChild(); r != null; r = r.getNextSibling())
|
|
for (Node r = d.getFirstChild(); r != null; r = r.getNextSibling())
|
|
{
|
|
{
|
|
@@ -133,7 +260,7 @@ public class WalkingManager extends DocumentParser
|
|
int y = parseInt(attrs, "Y");
|
|
int y = parseInt(attrs, "Y");
|
|
int z = parseInt(attrs, "Z");
|
|
int z = parseInt(attrs, "Z");
|
|
int delay = parseInt(attrs, "delay");
|
|
int delay = parseInt(attrs, "delay");
|
|
-
|
|
|
|
|
|
+
|
|
String chatString = null;
|
|
String chatString = null;
|
|
NpcStringId npcString = null;
|
|
NpcStringId npcString = null;
|
|
Node node = attrs.getNamedItem("string");
|
|
Node node = attrs.getNamedItem("string");
|
|
@@ -169,18 +296,84 @@ public class WalkingManager extends DocumentParser
|
|
}
|
|
}
|
|
list.add(new L2NpcWalkerNode(0, npcString, chatString, x, y, z, delay, parseBoolean(attrs, "run")));
|
|
list.add(new L2NpcWalkerNode(0, npcString, chatString, x, y, z, delay, parseBoolean(attrs, "run")));
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ else if (r.getNodeName().equals("target"))
|
|
|
|
+ {
|
|
|
|
+ NamedNodeMap attrs = r.getAttributes();
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ int npcId = Integer.parseInt(attrs.getNamedItem("id").getNodeValue());
|
|
|
|
+ int x = 0, y = 0, z = 0;
|
|
|
|
+
|
|
|
|
+ x = Integer.parseInt(attrs.getNamedItem("spawnX").getNodeValue());
|
|
|
|
+ y = Integer.parseInt(attrs.getNamedItem("spawnY").getNodeValue());
|
|
|
|
+ z = Integer.parseInt(attrs.getNamedItem("spawnZ").getNodeValue());
|
|
|
|
+
|
|
|
|
+ NpcRoutesHolder holder = _routesToAttach.containsKey(npcId) ? _routesToAttach.get(npcId) : new NpcRoutesHolder();
|
|
|
|
+ holder.addRoute(routeId, new Location(x, y, z));
|
|
|
|
+ _routesToAttach.put(npcId, holder);
|
|
|
|
+ }
|
|
|
|
+ catch (Exception e)
|
|
|
|
+ {
|
|
|
|
+ _log.warning("Walking Manager: Error in target definition for route ID: " + routeId);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
L2WalkRoute newRoute = new L2WalkRoute(routeId, list, repeat, false, repeatType);
|
|
L2WalkRoute newRoute = new L2WalkRoute(routeId, list, repeat, false, repeatType);
|
|
_routes.put(routeId, newRoute);
|
|
_routes.put(routeId, newRoute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param npc NPC to check
|
|
|
|
+ * @return {@code true} if given NPC, or its leader is controlled by Walking Manager and moves currently.
|
|
|
|
+ */
|
|
|
|
+ public boolean isOnWalk(L2Npc npc)
|
|
|
|
+ {
|
|
|
|
+ L2MonsterInstance monster = null;
|
|
|
|
+
|
|
|
|
+ if (npc.isMonster())
|
|
|
|
+ {
|
|
|
|
+ if (((L2MonsterInstance) npc).getLeader() == null)
|
|
|
|
+ {
|
|
|
|
+ monster = (L2MonsterInstance) npc;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ monster = ((L2MonsterInstance) npc).getLeader();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (((monster != null) && !isRegistered(monster)) || !isRegistered(npc))
|
|
|
|
+ {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ WalkInfo walk = monster != null ? _activeRoutes.get(monster.getObjectId()) : _activeRoutes.get(npc.getObjectId());
|
|
|
|
+
|
|
|
|
+ if (walk._stoppedByAttack || walk._suspended)
|
|
|
|
+ {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param npc NPC to check
|
|
|
|
+ * @return {@code true} if given NPC controlled by Walking Manager.
|
|
|
|
+ */
|
|
public boolean isRegistered(L2Npc npc)
|
|
public boolean isRegistered(L2Npc npc)
|
|
{
|
|
{
|
|
return _activeRoutes.containsKey(npc.getObjectId());
|
|
return _activeRoutes.containsKey(npc.getObjectId());
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Start to move given NPC by given route
|
|
|
|
+ * @param npc NPC to move
|
|
|
|
+ * @param routeId id of route to move by
|
|
|
|
+ */
|
|
public void startMoving(final L2Npc npc, final int routeId)
|
|
public void startMoving(final L2Npc npc, final int routeId)
|
|
{
|
|
{
|
|
if (_routes.containsKey(routeId) && (npc != null) && !npc.isDead()) // check, if these route and NPC present
|
|
if (_routes.containsKey(routeId) && (npc != null) && !npc.isDead()) // check, if these route and NPC present
|
|
@@ -191,14 +384,29 @@ public class WalkingManager extends DocumentParser
|
|
if ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
|
|
if ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
|
|
{
|
|
{
|
|
WalkInfo walk = new WalkInfo(routeId);
|
|
WalkInfo walk = new WalkInfo(routeId);
|
|
- // walk._lastActionTime = System.currentTimeMillis();
|
|
|
|
|
|
+
|
|
|
|
+ if (npc.isDebug())
|
|
|
|
+ {
|
|
|
|
+ walk._lastActionTime = System.currentTimeMillis();
|
|
|
|
+ }
|
|
|
|
+
|
|
L2NpcWalkerNode node = walk.getCurrentNode();
|
|
L2NpcWalkerNode node = walk.getCurrentNode();
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ // adjust next waypoint, if NPC spawns at first waypoint
|
|
|
|
+ if ((npc.getX() == node.getMoveX()) && (npc.getY() == node.getMoveY()))
|
|
|
|
+ {
|
|
|
|
+ walk.calculateNextNode(npc);
|
|
|
|
+ node = walk.getCurrentNode();
|
|
|
|
+ npc.sendDebugMessage("Route id " + routeId + ", spawn point is same with first waypoint, adjusted to next");
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!npc.isInsideRadius(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 3000, true, false))
|
|
if (!npc.isInsideRadius(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 3000, true, false))
|
|
{
|
|
{
|
|
|
|
+ npc.sendDebugMessage("Route id " + routeId + ", NPC is too far from starting point, walking will no start");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ npc.sendDebugMessage("Starting to move at route " + routeId);
|
|
npc.setIsRunning(node.getRunning());
|
|
npc.setIsRunning(node.getRunning());
|
|
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
|
|
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
|
|
walk._walkCheckTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new Runnable()
|
|
walk._walkCheckTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new Runnable()
|
|
@@ -209,13 +417,14 @@ public class WalkingManager extends DocumentParser
|
|
startMoving(npc, routeId);
|
|
startMoving(npc, routeId);
|
|
}
|
|
}
|
|
}, 60000, 60000); // start walk check task, for resuming walk after fight
|
|
}, 60000, 60000); // start walk check task, for resuming walk after fight
|
|
-
|
|
|
|
|
|
+
|
|
npc.getKnownList().startTrackingTask();
|
|
npc.getKnownList().startTrackingTask();
|
|
-
|
|
|
|
|
|
+
|
|
_activeRoutes.put(npc.getObjectId(), walk); // register route
|
|
_activeRoutes.put(npc.getObjectId(), walk); // register route
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
|
|
+ npc.sendDebugMessage("Trying to start move at route " + routeId + ", but cannot now, scheduled");
|
|
ThreadPoolManager.getInstance().scheduleGeneral(new Runnable()
|
|
ThreadPoolManager.getInstance().scheduleGeneral(new Runnable()
|
|
{
|
|
{
|
|
@Override
|
|
@Override
|
|
@@ -232,66 +441,34 @@ public class WalkingManager extends DocumentParser
|
|
if ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
|
|
if ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
|
|
{
|
|
{
|
|
WalkInfo walk = _activeRoutes.get(npc.getObjectId());
|
|
WalkInfo walk = _activeRoutes.get(npc.getObjectId());
|
|
-
|
|
|
|
|
|
+
|
|
// Prevent call simultaneously from scheduled task and onArrived() or temporarily stop walking for resuming in future
|
|
// Prevent call simultaneously from scheduled task and onArrived() or temporarily stop walking for resuming in future
|
|
if (walk._blocked || walk._suspended)
|
|
if (walk._blocked || walk._suspended)
|
|
{
|
|
{
|
|
|
|
+ npc.sendDebugMessage("Trying continue to move at route " + routeId + ", but cannot now (operation is blocked)");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
walk._blocked = true;
|
|
walk._blocked = true;
|
|
- // Check this first, within the bounds of random moving, we have no conception of "first" or "last" node
|
|
|
|
- if ((walk.getRoute().getRepeatType() == REPEAT_RANDOM) && walk._nodeArrived)
|
|
|
|
- {
|
|
|
|
- int newNode = walk._currentNode;
|
|
|
|
-
|
|
|
|
- while (newNode == walk._currentNode)
|
|
|
|
- {
|
|
|
|
- newNode = Rnd.get(walk.getRoute().getNodesCount());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- walk._currentNode = newNode;
|
|
|
|
- walk._nodeArrived = false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- else if (walk._currentNode == walk.getRoute().getNodesCount()) // Last node arrived
|
|
|
|
- {
|
|
|
|
- if (!walk.getRoute().repeatWalk())
|
|
|
|
- {
|
|
|
|
- cancelMoving(npc);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- switch (walk.getRoute().getRepeatType())
|
|
|
|
- {
|
|
|
|
- case REPEAT_GO_BACK:
|
|
|
|
- walk._forward = false;
|
|
|
|
- walk._currentNode -= 2;
|
|
|
|
- break;
|
|
|
|
- case REPEAT_GO_FIRST:
|
|
|
|
- walk._currentNode = 0;
|
|
|
|
- break;
|
|
|
|
- case REPEAT_TELE_FIRST:
|
|
|
|
- npc.teleToLocation(npc.getSpawn().getLocx(), npc.getSpawn().getLocy(), npc.getSpawn().getLocz());
|
|
|
|
- walk._currentNode = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- else if (walk._currentNode == -1) // First node arrived, when direction is first <-- last
|
|
|
|
- {
|
|
|
|
- walk._currentNode = 1;
|
|
|
|
- walk._forward = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
L2NpcWalkerNode node = walk.getCurrentNode();
|
|
L2NpcWalkerNode node = walk.getCurrentNode();
|
|
|
|
+ npc.sendDebugMessage("Route id: " + routeId + ", continue to node " + walk._currentNode);
|
|
npc.setIsRunning(node.getRunning());
|
|
npc.setIsRunning(node.getRunning());
|
|
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
|
|
npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
|
|
walk._blocked = false;
|
|
walk._blocked = false;
|
|
|
|
+ walk._stoppedByAttack = false;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ npc.sendDebugMessage("Trying continue to move at route " + routeId + ", but cannot now (wrong AI state)");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Cancel NPC moving permanently
|
|
|
|
+ * @param npc NPC to cancel
|
|
|
|
+ */
|
|
public synchronized void cancelMoving(L2Npc npc)
|
|
public synchronized void cancelMoving(L2Npc npc)
|
|
{
|
|
{
|
|
if (_activeRoutes.containsKey(npc.getObjectId()))
|
|
if (_activeRoutes.containsKey(npc.getObjectId()))
|
|
@@ -301,7 +478,11 @@ public class WalkingManager extends DocumentParser
|
|
npc.getKnownList().stopTrackingTask();
|
|
npc.getKnownList().stopTrackingTask();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Resumes previously stopped moving
|
|
|
|
+ * @param npc NPC to resume
|
|
|
|
+ */
|
|
public void resumeMoving(final L2Npc npc)
|
|
public void resumeMoving(final L2Npc npc)
|
|
{
|
|
{
|
|
if (!_activeRoutes.containsKey(npc.getObjectId()))
|
|
if (!_activeRoutes.containsKey(npc.getObjectId()))
|
|
@@ -311,86 +492,132 @@ public class WalkingManager extends DocumentParser
|
|
|
|
|
|
WalkInfo walk = _activeRoutes.get(npc.getObjectId());
|
|
WalkInfo walk = _activeRoutes.get(npc.getObjectId());
|
|
walk._suspended = false;
|
|
walk._suspended = false;
|
|
|
|
+ walk._stoppedByAttack = false;
|
|
startMoving(npc, walk.getRoute().getId());
|
|
startMoving(npc, walk.getRoute().getId());
|
|
}
|
|
}
|
|
-
|
|
|
|
- public void stopMoving(L2Npc npc, boolean suspend)
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Pause NPC moving until it will be resumed
|
|
|
|
+ * @param npc NPC to pause moving
|
|
|
|
+ * @param suspend {@code true} if moving was temporarily suspended for some reasons of AI-controlling script
|
|
|
|
+ * @param stoppedByAttack {@code true} if moving was suspended because of NPC was attacked or desired to attack
|
|
|
|
+ */
|
|
|
|
+ public void stopMoving(L2Npc npc, boolean suspend, boolean stoppedByAttack)
|
|
{
|
|
{
|
|
- if (!_activeRoutes.containsKey(npc.getObjectId()))
|
|
|
|
|
|
+ L2MonsterInstance monster = null;
|
|
|
|
+
|
|
|
|
+ if (npc.isMonster())
|
|
|
|
+ {
|
|
|
|
+ if (((L2MonsterInstance) npc).getLeader() == null)
|
|
|
|
+ {
|
|
|
|
+ monster = (L2MonsterInstance) npc;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ monster = ((L2MonsterInstance) npc).getLeader();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (((monster != null) && !isRegistered(monster)) || !isRegistered(npc))
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
- WalkInfo walk = _activeRoutes.get(npc.getObjectId());
|
|
|
|
|
|
+
|
|
|
|
+ WalkInfo walk = monster != null ? _activeRoutes.get(monster.getObjectId()) : _activeRoutes.get(npc.getObjectId());
|
|
|
|
+
|
|
walk._suspended = suspend;
|
|
walk._suspended = suspend;
|
|
- npc.stopMove(null);
|
|
|
|
- npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
|
|
|
+ walk._stoppedByAttack = stoppedByAttack;
|
|
|
|
+
|
|
|
|
+ if (monster != null)
|
|
|
|
+ {
|
|
|
|
+ monster.stopMove(null);
|
|
|
|
+ monster.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ npc.stopMove(null);
|
|
|
|
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Manage "node arriving"-related tasks: schedule move to next node; send ON_NODE_ARRIVED event to Quest script
|
|
|
|
+ * @param npc NPC to manage
|
|
|
|
+ */
|
|
public void onArrived(final L2Npc npc)
|
|
public void onArrived(final L2Npc npc)
|
|
{
|
|
{
|
|
if (_activeRoutes.containsKey(npc.getObjectId()))
|
|
if (_activeRoutes.containsKey(npc.getObjectId()))
|
|
{
|
|
{
|
|
|
|
+ // Notify quest
|
|
|
|
+ if (npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_NODE_ARRIVED) != null)
|
|
|
|
+ {
|
|
|
|
+ for (Quest quest : npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_NODE_ARRIVED))
|
|
|
|
+ {
|
|
|
|
+ quest.notifyNodeArrived(npc);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
WalkInfo walk = _activeRoutes.get(npc.getObjectId());
|
|
WalkInfo walk = _activeRoutes.get(npc.getObjectId());
|
|
-
|
|
|
|
|
|
+
|
|
// Opposite should not happen... but happens sometime
|
|
// Opposite should not happen... but happens sometime
|
|
if ((walk._currentNode >= 0) && (walk._currentNode < walk.getRoute().getNodesCount()))
|
|
if ((walk._currentNode >= 0) && (walk._currentNode < walk.getRoute().getNodesCount()))
|
|
{
|
|
{
|
|
L2NpcWalkerNode node = walk.getRoute().getNodeList().get(walk._currentNode);
|
|
L2NpcWalkerNode node = walk.getRoute().getNodeList().get(walk._currentNode);
|
|
if ((node.getMoveX() == npc.getX()) && (node.getMoveY() == npc.getY()))
|
|
if ((node.getMoveX() == npc.getX()) && (node.getMoveY() == npc.getY()))
|
|
{
|
|
{
|
|
- walk._nodeArrived = true;
|
|
|
|
- if (walk.getRoute().getRepeatType() != REPEAT_RANDOM)
|
|
|
|
- {
|
|
|
|
- if (walk._forward)
|
|
|
|
- {
|
|
|
|
- walk._currentNode++;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- walk._currentNode--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- int delay;
|
|
|
|
-
|
|
|
|
- if (walk._currentNode >= walk.getRoute().getNodesCount())
|
|
|
|
- {
|
|
|
|
- delay = walk.getRoute().getLastNode().getDelay();
|
|
|
|
- }
|
|
|
|
- else if (walk._currentNode < 0)
|
|
|
|
- {
|
|
|
|
- delay = walk.getRoute().getNodeList().get(0).getDelay();
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
|
|
+ npc.sendDebugMessage("Route id: " + walk.getRoute().getId() + ", arrived to node " + walk._currentNode);
|
|
|
|
+ npc.sendDebugMessage("Done in " + ((System.currentTimeMillis() - walk._lastActionTime) / 1000) + " s.");
|
|
|
|
+ walk.calculateNextNode(npc);
|
|
|
|
+ int delay = walk.getCurrentNode().getDelay();
|
|
|
|
+ walk._blocked = true; // prevents to be ran from walk check task, if there is delay in this node.
|
|
|
|
+
|
|
|
|
+ if (npc.isDebug())
|
|
{
|
|
{
|
|
- delay = walk.getCurrentNode().getDelay();
|
|
|
|
|
|
+ walk._lastActionTime = System.currentTimeMillis();
|
|
}
|
|
}
|
|
-
|
|
|
|
- walk._blocked = true; // prevents to be ran from walk check task, if there is delay in this node.
|
|
|
|
- // walk._lastActionTime = System.currentTimeMillis();
|
|
|
|
ThreadPoolManager.getInstance().scheduleGeneral(new ArrivedTask(npc, walk), 100 + (delay * 1000L));
|
|
ThreadPoolManager.getInstance().scheduleGeneral(new ArrivedTask(npc, walk), 100 + (delay * 1000L));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Manage "on death"-related tasks: permanently cancel moving of died NPC
|
|
|
|
+ * @param npc NPC to manage
|
|
|
|
+ */
|
|
public void onDeath(L2Npc npc)
|
|
public void onDeath(L2Npc npc)
|
|
{
|
|
{
|
|
cancelMoving(npc);
|
|
cancelMoving(npc);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Manage "on spawn"-related tasks: start NPC moving, if there is route attached to its spawn point
|
|
|
|
+ * @param npc NPC to manage
|
|
|
|
+ */
|
|
|
|
+ public void onSpawn(L2Npc npc)
|
|
|
|
+ {
|
|
|
|
+ if (_routesToAttach.containsKey(npc.getNpcId()))
|
|
|
|
+ {
|
|
|
|
+ int routeId = _routesToAttach.get(npc.getNpcId()).getRouteId(npc);
|
|
|
|
+
|
|
|
|
+ if (routeId > 0)
|
|
|
|
+ {
|
|
|
|
+ startMoving(npc, routeId);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
private class ArrivedTask implements Runnable
|
|
private class ArrivedTask implements Runnable
|
|
{
|
|
{
|
|
WalkInfo _walk;
|
|
WalkInfo _walk;
|
|
L2Npc _npc;
|
|
L2Npc _npc;
|
|
-
|
|
|
|
|
|
+
|
|
public ArrivedTask(L2Npc npc, WalkInfo walk)
|
|
public ArrivedTask(L2Npc npc, WalkInfo walk)
|
|
{
|
|
{
|
|
_npc = npc;
|
|
_npc = npc;
|
|
_walk = walk;
|
|
_walk = walk;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
public void run()
|
|
public void run()
|
|
{
|
|
{
|
|
@@ -398,12 +625,12 @@ public class WalkingManager extends DocumentParser
|
|
startMoving(_npc, _walk.getRoute().getId());
|
|
startMoving(_npc, _walk.getRoute().getId());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
public static final WalkingManager getInstance()
|
|
public static final WalkingManager getInstance()
|
|
{
|
|
{
|
|
return SingletonHolder._instance;
|
|
return SingletonHolder._instance;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private static class SingletonHolder
|
|
private static class SingletonHolder
|
|
{
|
|
{
|
|
protected static final WalkingManager _instance = new WalkingManager();
|
|
protected static final WalkingManager _instance = new WalkingManager();
|