WalkingManager.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver.gameserver.instancemanager;
  16. import java.util.ArrayList;
  17. import java.util.HashMap;
  18. import java.util.List;
  19. import java.util.Map;
  20. import java.util.concurrent.ScheduledFuture;
  21. import org.w3c.dom.NamedNodeMap;
  22. import org.w3c.dom.Node;
  23. import com.l2jserver.gameserver.ThreadPoolManager;
  24. import com.l2jserver.gameserver.ai.CtrlIntention;
  25. import com.l2jserver.gameserver.engines.DocumentParser;
  26. import com.l2jserver.gameserver.model.L2CharPosition;
  27. import com.l2jserver.gameserver.model.L2NpcWalkerNode;
  28. import com.l2jserver.gameserver.model.L2WalkRoute;
  29. import com.l2jserver.gameserver.model.actor.L2Npc;
  30. import com.l2jserver.gameserver.network.NpcStringId;
  31. import com.l2jserver.util.Rnd;
  32. /**
  33. * This class manages walking monsters.
  34. * @author GKR
  35. */
  36. public class WalkingManager extends DocumentParser
  37. {
  38. //Repeat style: 0 - go back, 1 - go to first point (circle style), 2 - teleport to first point (conveyor style), 3 - random walking between points.
  39. private static final byte REPEAT_GO_BACK = 0;
  40. private static final byte REPEAT_GO_FIRST = 1;
  41. private static final byte REPEAT_TELE_FIRST = 2;
  42. private static final byte REPEAT_RANDOM = 3;
  43. protected Map<Integer, L2WalkRoute> _routes = new HashMap<>(); //all available routes
  44. private Map<Integer, WalkInfo> _activeRoutes = new HashMap<>(); //each record represents NPC, moving by predefined route from _routes, and moving progress
  45. private class WalkInfo
  46. {
  47. protected ScheduledFuture<?> _walkCheckTask;
  48. protected boolean _blocked = false;
  49. protected boolean _suspended = false;
  50. protected boolean _nodeArrived = false;
  51. protected int _currentNode = 0;
  52. protected boolean _forward = true; //Determines first --> last or first <-- last direction
  53. private int _routeId;
  54. public WalkInfo(int routeId)
  55. {
  56. _routeId = routeId;
  57. }
  58. protected L2WalkRoute getRoute()
  59. {
  60. return _routes.get(_routeId);
  61. }
  62. protected L2NpcWalkerNode getCurrentNode()
  63. {
  64. return getRoute().getNodeList().get(_currentNode);
  65. }
  66. }
  67. protected WalkingManager()
  68. {
  69. load();
  70. }
  71. @Override
  72. public final void load()
  73. {
  74. parseDatapackFile("data/Routes.xml");
  75. _log.info(getClass().getSimpleName() + ": Loaded " + _routes.size() + " walking routes.");
  76. }
  77. @Override
  78. protected void parseDocument()
  79. {
  80. Node n = getCurrentDocument().getFirstChild();
  81. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  82. {
  83. if (d.getNodeName().equals("route"))
  84. {
  85. final Integer routeId = parseInteger(d.getAttributes(), "id");
  86. boolean repeat = parseBoolean(d.getAttributes(), "repeat");
  87. String repeatStyle = d.getAttributes().getNamedItem("repeatStyle").getNodeValue();
  88. byte repeatType;
  89. if (repeatStyle.equalsIgnoreCase("back"))
  90. repeatType = REPEAT_GO_BACK;
  91. else if (repeatStyle.equalsIgnoreCase("cycle"))
  92. repeatType = REPEAT_GO_FIRST;
  93. else if (repeatStyle.equalsIgnoreCase("conveyor"))
  94. repeatType = REPEAT_TELE_FIRST;
  95. else if (repeatStyle.equalsIgnoreCase("random"))
  96. repeatType = REPEAT_RANDOM;
  97. else
  98. repeatType = -1;
  99. final List<L2NpcWalkerNode> list = new ArrayList<>();
  100. for (Node r = d.getFirstChild(); r != null; r = r.getNextSibling())
  101. {
  102. if (r.getNodeName().equals("point"))
  103. {
  104. NamedNodeMap attrs = r.getAttributes();
  105. int x = parseInt(attrs, "X");
  106. int y = parseInt(attrs, "Y");
  107. int z = parseInt(attrs, "Z");
  108. int delay = parseInt(attrs, "delay");
  109. String chatString = null;
  110. NpcStringId npcString = null;
  111. Node node = attrs.getNamedItem("string");
  112. if (node != null)
  113. chatString = node.getNodeValue();
  114. else
  115. {
  116. node = attrs.getNamedItem("npcString");
  117. if (node != null)
  118. {
  119. npcString = NpcStringId.getNpcStringId(node.getNodeValue());
  120. if (npcString == null)
  121. {
  122. _log.warning(getClass().getSimpleName() + ": Unknown npcstring '" + node.getNodeValue() + ".");
  123. continue;
  124. }
  125. }
  126. else
  127. {
  128. node = attrs.getNamedItem("npcStringId");
  129. if (node != null)
  130. {
  131. npcString = NpcStringId.getNpcStringId(Integer.parseInt(node.getNodeValue()));
  132. if (npcString == null)
  133. {
  134. _log.warning(getClass().getSimpleName() + ": Unknown npcstring '" + node.getNodeValue() + ".");
  135. continue;
  136. }
  137. }
  138. }
  139. }
  140. list.add(new L2NpcWalkerNode(0, npcString, chatString, x, y, z, delay, parseBoolean(attrs, "run")));
  141. }
  142. }
  143. L2WalkRoute newRoute = new L2WalkRoute(routeId, list, repeat, false, repeatType);
  144. _routes.put(routeId, newRoute);
  145. }
  146. }
  147. }
  148. public boolean isRegistered(L2Npc npc)
  149. {
  150. return _activeRoutes.containsKey(npc.getObjectId());
  151. }
  152. public void startMoving(final L2Npc npc, final int routeId)
  153. {
  154. if (_routes.containsKey(routeId) && npc != null && !npc.isDead()) //check, if these route and NPC present
  155. {
  156. if (!_activeRoutes.containsKey(npc.getObjectId())) //new walk task
  157. {
  158. //only if not already moved / not engaged in battle... should not happens if called on spawn
  159. if (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE || npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
  160. {
  161. WalkInfo walk = new WalkInfo(routeId);
  162. //walk._lastActionTime = System.currentTimeMillis();
  163. L2NpcWalkerNode node = walk.getCurrentNode();
  164. if (!npc.isInsideRadius(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 3000, true, false)) //too far from first point, decline further operations
  165. return;
  166. npc.setIsRunning(node.getRunning());
  167. npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
  168. walk._walkCheckTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new Runnable() {
  169. @Override
  170. public void run()
  171. {
  172. startMoving(npc, routeId);
  173. }
  174. }, 60000, 60000); //start walk check task, for resuming walk after fight
  175. npc.getKnownList().startTrackingTask();
  176. _activeRoutes.put(npc.getObjectId(), walk); //register route
  177. }
  178. else //try a bit later
  179. ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() {
  180. @Override
  181. public void run()
  182. {
  183. startMoving(npc, routeId);
  184. }
  185. }, 60000);
  186. }
  187. else //walk was stopped due to some reason (arrived to node, script action, fight or something else), resume it
  188. {
  189. if (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE || npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
  190. {
  191. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  192. //Prevent call simultaneously from scheduled task and onArrived() or temporarily stop walking for resuming in future
  193. if (walk._blocked || walk._suspended)
  194. return;
  195. walk._blocked = true;
  196. //Check this first, within the bounds of random moving, we have no conception of "first" or "last" node
  197. if (walk.getRoute().getRepeatType() == REPEAT_RANDOM && walk._nodeArrived)
  198. {
  199. int newNode = walk._currentNode;
  200. while (newNode == walk._currentNode)
  201. newNode = Rnd.get(walk.getRoute().getNodesCount());
  202. walk._currentNode = newNode;
  203. walk._nodeArrived = false;
  204. }
  205. else if (walk._currentNode == walk.getRoute().getNodesCount()) //Last node arrived
  206. {
  207. if (!walk.getRoute().repeatWalk())
  208. {
  209. cancelMoving(npc);
  210. return;
  211. }
  212. switch (walk.getRoute().getRepeatType())
  213. {
  214. case REPEAT_GO_BACK:
  215. walk._forward = false;
  216. walk._currentNode -=2;
  217. break;
  218. case REPEAT_GO_FIRST:
  219. walk._currentNode = 0;
  220. break;
  221. case REPEAT_TELE_FIRST:
  222. npc.teleToLocation(npc.getSpawn().getLocx(),npc.getSpawn().getLocy(),npc.getSpawn().getLocz());
  223. walk._currentNode = 0;
  224. }
  225. }
  226. else if (walk._currentNode == -1) //First node arrived, when direction is first <-- last
  227. {
  228. walk._currentNode = 1;
  229. walk._forward = true;
  230. }
  231. L2NpcWalkerNode node = walk.getCurrentNode();
  232. npc.setIsRunning(node.getRunning());
  233. npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
  234. walk._blocked = false;
  235. }
  236. }
  237. }
  238. }
  239. public synchronized void cancelMoving(L2Npc npc)
  240. {
  241. if (_activeRoutes.containsKey(npc.getObjectId()))
  242. {
  243. final WalkInfo walk = _activeRoutes.remove(npc.getObjectId());
  244. walk._walkCheckTask.cancel(true);
  245. npc.getKnownList().stopTrackingTask();
  246. }
  247. }
  248. public void resumeMoving(final L2Npc npc)
  249. {
  250. if (!_activeRoutes.containsKey(npc.getObjectId()))
  251. return;
  252. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  253. walk._suspended = false;
  254. startMoving(npc, walk.getRoute().getId());
  255. }
  256. public void stopMoving(L2Npc npc, boolean suspend)
  257. {
  258. if (!_activeRoutes.containsKey(npc.getObjectId()))
  259. return;
  260. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  261. walk._suspended = suspend;
  262. npc.stopMove(null);
  263. npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  264. }
  265. public void onArrived(final L2Npc npc)
  266. {
  267. if (_activeRoutes.containsKey(npc.getObjectId()))
  268. {
  269. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  270. //Opposite should not happen... but happens sometime
  271. if (walk._currentNode >= 0 && walk._currentNode < walk.getRoute().getNodesCount())
  272. {
  273. L2NpcWalkerNode node = walk.getRoute().getNodeList().get(walk._currentNode);
  274. if (node.getMoveX() == npc.getX() && node.getMoveY() == npc.getY())
  275. {
  276. walk._nodeArrived = true;
  277. if (walk.getRoute().getRepeatType() != REPEAT_RANDOM)
  278. {
  279. if (walk._forward)
  280. walk._currentNode++;
  281. else
  282. walk._currentNode--;
  283. }
  284. int delay;
  285. if (walk._currentNode >= walk.getRoute().getNodesCount())
  286. delay = walk.getRoute().getLastNode().getDelay();
  287. else if (walk._currentNode < 0)
  288. delay = walk.getRoute().getNodeList().get(0).getDelay();
  289. else
  290. delay = walk.getCurrentNode().getDelay();
  291. walk._blocked = true; //prevents to be ran from walk check task, if there is delay in this node.
  292. //walk._lastActionTime = System.currentTimeMillis();
  293. ThreadPoolManager.getInstance().scheduleGeneral(new ArrivedTask(npc, walk), 100 + delay * 1000L);
  294. }
  295. }
  296. }
  297. }
  298. public void onDeath(L2Npc npc)
  299. {
  300. cancelMoving(npc);
  301. }
  302. private class ArrivedTask implements Runnable
  303. {
  304. WalkInfo _walk;
  305. L2Npc _npc;
  306. public ArrivedTask(L2Npc npc, WalkInfo walk)
  307. {
  308. _npc = npc;
  309. _walk = walk;
  310. }
  311. @Override
  312. public void run()
  313. {
  314. _walk._blocked = false;
  315. startMoving(_npc,_walk. getRoute().getId());
  316. }
  317. }
  318. public static final WalkingManager getInstance()
  319. {
  320. return SingletonHolder._instance;
  321. }
  322. private static class SingletonHolder
  323. {
  324. protected static final WalkingManager _instance = new WalkingManager();
  325. }
  326. }