WalkingManager.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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.io.File;
  17. import java.util.List;
  18. import java.util.Map;
  19. import java.util.concurrent.ScheduledFuture;
  20. import java.util.logging.Level;
  21. import java.util.logging.Logger;
  22. import javax.xml.parsers.DocumentBuilderFactory;
  23. import javolution.util.FastList;
  24. import javolution.util.FastMap;
  25. import org.w3c.dom.Document;
  26. import org.w3c.dom.NamedNodeMap;
  27. import org.w3c.dom.Node;
  28. import com.l2jserver.Config;
  29. import com.l2jserver.gameserver.Announcements;
  30. import com.l2jserver.gameserver.ThreadPoolManager;
  31. import com.l2jserver.gameserver.ai.CtrlIntention;
  32. import com.l2jserver.gameserver.model.L2CharPosition;
  33. import com.l2jserver.gameserver.model.L2NpcWalkerNode;
  34. import com.l2jserver.gameserver.model.L2WalkRoute;
  35. import com.l2jserver.gameserver.model.actor.L2Npc;
  36. import com.l2jserver.gameserver.network.NpcStringId;
  37. import com.l2jserver.util.Rnd;
  38. /**
  39. * This class manages walking monsters.
  40. * @author GKR
  41. */
  42. public class WalkingManager
  43. {
  44. private static final Logger _log = Logger.getLogger(WalkingManager.class.getName());
  45. //Repeat style: 0 - go back, 1 - go to first point (circle style), 2 - teleport to first point (conveyor style), 3 - random walking between points.
  46. private static final byte REPEAT_GO_BACK = 0;
  47. private static final byte REPEAT_GO_FIRST = 1;
  48. private static final byte REPEAT_TELE_FIRST = 2;
  49. private static final byte REPEAT_RANDOM = 3;
  50. private Map<Integer, L2WalkRoute> _routes; //all available routes
  51. private Map<Integer, WalkInfo> _activeRoutes; //each record represents NPC, moving by predefined route from _routes, and moving progress
  52. private class WalkInfo
  53. {
  54. private ScheduledFuture<?> _walkCheckTask;
  55. private boolean _blocked = false;
  56. private boolean _suspended = false;
  57. private boolean _nodeArrived = false;
  58. private int _currentNode = 0;
  59. private boolean _forward = true; //Determines first --> last or first <-- last direction
  60. private int _routeId;
  61. public WalkInfo(int routeId)
  62. {
  63. _routeId = routeId;
  64. }
  65. private L2WalkRoute getRoute()
  66. {
  67. return _routes.get(_routeId);
  68. }
  69. private L2NpcWalkerNode getCurrentNode()
  70. {
  71. return getRoute().getNodeList().get(_currentNode);
  72. }
  73. }
  74. public static final WalkingManager getInstance()
  75. {
  76. return SingletonHolder._instance;
  77. }
  78. private WalkingManager()
  79. {
  80. _routes = new FastMap<Integer, L2WalkRoute>();
  81. _activeRoutes = new FastMap<Integer, WalkInfo>();
  82. load();
  83. }
  84. private final void load()
  85. {
  86. _log.info("WalkingManager: Loading walking routes...");
  87. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  88. factory.setValidating(false);
  89. factory.setIgnoringComments(true);
  90. File file = new File(Config.DATAPACK_ROOT, "data/Routes.xml");
  91. Document doc = null;
  92. if (file.exists())
  93. {
  94. try
  95. {
  96. doc = factory.newDocumentBuilder().parse(file);
  97. }
  98. catch (Exception e)
  99. {
  100. _log.log(Level.WARNING, "Could not parse Routes.xml file: " + e.getMessage(), e);
  101. return;
  102. }
  103. Node n = doc.getFirstChild();
  104. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  105. {
  106. if (d.getNodeName().equals("route"))
  107. {
  108. boolean debug = false;
  109. int routeId = Integer.parseInt(d.getAttributes().getNamedItem("id").getNodeValue());
  110. boolean repeat = Boolean.parseBoolean(d.getAttributes().getNamedItem("repeat").getNodeValue());
  111. String repeatStyle = d.getAttributes().getNamedItem("repeatStyle").getNodeValue();
  112. byte repeatType;
  113. if (repeatStyle.equalsIgnoreCase("back"))
  114. repeatType = REPEAT_GO_BACK;
  115. else if (repeatStyle.equalsIgnoreCase("cycle"))
  116. repeatType = REPEAT_GO_FIRST;
  117. else if (repeatStyle.equalsIgnoreCase("conveyor"))
  118. repeatType = REPEAT_TELE_FIRST;
  119. else if (repeatStyle.equalsIgnoreCase("random"))
  120. repeatType = REPEAT_RANDOM;
  121. else
  122. repeatType = -1;
  123. List<L2NpcWalkerNode> list = new FastList<L2NpcWalkerNode>();
  124. for (Node r = d.getFirstChild(); r != null; r = r.getNextSibling())
  125. {
  126. if (r.getNodeName().equals("point"))
  127. {
  128. NamedNodeMap attrs = r.getAttributes();
  129. int x = Integer.parseInt(attrs.getNamedItem("X").getNodeValue());
  130. int y = Integer.parseInt(attrs.getNamedItem("Y").getNodeValue());
  131. int z = Integer.parseInt(attrs.getNamedItem("Z").getNodeValue());
  132. int delay = Integer.parseInt(attrs.getNamedItem("delay").getNodeValue());
  133. String chatString = null;
  134. NpcStringId npcString = null;
  135. Node node = attrs.getNamedItem("string");
  136. if (node != null)
  137. chatString = node.getNodeValue();
  138. else
  139. {
  140. node = attrs.getNamedItem("npcString");
  141. if (node != null)
  142. {
  143. npcString = NpcStringId.getNpcStringId(node.getNodeValue());
  144. if (npcString == null)
  145. {
  146. _log.log(Level.WARNING, "NpcWalkerRoutersTable: Unknown npcstring '" + node.getNodeValue() + ".");
  147. continue;
  148. }
  149. }
  150. else
  151. {
  152. node = attrs.getNamedItem("npcStringId");
  153. if (node != null)
  154. {
  155. npcString = NpcStringId.getNpcStringId(Integer.parseInt(node.getNodeValue()));
  156. if (npcString == null)
  157. {
  158. _log.log(Level.WARNING, "NpcWalkerRoutersTable: Unknown npcstring '" + node.getNodeValue() + ".");
  159. continue;
  160. }
  161. }
  162. }
  163. }
  164. boolean running = Boolean.parseBoolean(attrs.getNamedItem("run").getNodeValue());
  165. list.add(new L2NpcWalkerNode(0, npcString, chatString, x, y, z, delay, running));
  166. }
  167. else if (r.getNodeName().equals("stat"))
  168. {
  169. NamedNodeMap attrs = r.getAttributes();
  170. String name = attrs.getNamedItem("name").getNodeValue();
  171. String val = attrs.getNamedItem("val").getNodeValue();
  172. if (name.equalsIgnoreCase("debug"))
  173. debug = Boolean.parseBoolean(val);
  174. }
  175. }
  176. L2WalkRoute newRoute = new L2WalkRoute(routeId, list, repeat, false, repeatType);
  177. newRoute.setDebug(debug);
  178. _routes.put(routeId, newRoute);
  179. }
  180. }
  181. }
  182. _log.info("WalkingManager: loaded " + _routes.size() + " walking routes.");
  183. }
  184. public boolean isRegistered(L2Npc npc)
  185. {
  186. return _activeRoutes.containsKey(npc.getObjectId());
  187. }
  188. public void startMoving(final L2Npc npc, final int routeId)
  189. {
  190. if (_routes.containsKey(routeId) && npc != null && !npc.isDead()) //check, if these route and NPC present
  191. {
  192. if (!_activeRoutes.containsKey(npc.getObjectId())) //new walk task
  193. {
  194. //only if not already moved / not engaged in battle... should not happens if called on spawn
  195. if (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE || npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
  196. {
  197. WalkInfo walk = new WalkInfo(routeId);
  198. //walk._lastActionTime = System.currentTimeMillis();
  199. L2NpcWalkerNode node = walk.getCurrentNode();
  200. if (!npc.isInsideRadius(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 3000, true, false)) //too far from first point, decline further operations
  201. return;
  202. //Announcements.getInstance().announceToAll("Start to move!");
  203. npc.setIsRunning(node.getRunning());
  204. npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
  205. walk._walkCheckTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new Runnable() {
  206. @Override
  207. public void run()
  208. {
  209. startMoving(npc, routeId);
  210. }
  211. }, 60000, 60000); //start walk check task, for resuming walk after fight
  212. npc.getKnownList().startTrackingTask();
  213. _activeRoutes.put(npc.getObjectId(), walk); //register route
  214. }
  215. else //try a bit later
  216. ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() {
  217. @Override
  218. public void run()
  219. {
  220. startMoving(npc, routeId);
  221. }
  222. }, 60000);
  223. }
  224. else //walk was stopped due to some reason (arrived to node, script action, fight or something else), resume it
  225. {
  226. //Announcements.getInstance().announceToAll("Here_1!");
  227. if (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE || npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
  228. {
  229. //Announcements.getInstance().announceToAll("Here_2!");
  230. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  231. //Announcements.getInstance().announceToAll("X = " + Integer.toString(npc.getX()) + ", Y = " + Integer.toString(npc.getY()) + ", node = " + Integer.toString(walk._currentNode));
  232. //Prevent call simultaneosly from scheduled task and onArrived() or temporarily stop walking for resuming in future
  233. if (walk._blocked || walk._suspended)
  234. return;
  235. //Announcements.getInstance().announceToAll("Continue move!");
  236. walk._blocked = true;
  237. //Check this first, within the bounds of random moving, we have no conception of "first" or "last" node
  238. if (walk.getRoute().getRepeatType() == REPEAT_RANDOM && walk._nodeArrived)
  239. {
  240. int newNode = walk._currentNode;
  241. while (newNode == walk._currentNode)
  242. newNode = Rnd.get(walk.getRoute().getNodesCount());
  243. walk._currentNode = newNode;
  244. walk._nodeArrived = false;
  245. }
  246. else if (walk._currentNode == walk.getRoute().getNodesCount()) //Last node arrived
  247. {
  248. if (walk.getRoute().debug())
  249. Announcements.getInstance().announceToAll("Last node arrived!");
  250. if (!walk.getRoute().repeatWalk())
  251. {
  252. //Announcements.getInstance().announceToAll("Stoppping!");
  253. cancelMoving(npc);
  254. return;
  255. }
  256. switch (walk.getRoute().getRepeatType())
  257. {
  258. case REPEAT_GO_BACK:
  259. walk._forward = false;
  260. walk._currentNode -=2;
  261. break;
  262. case REPEAT_GO_FIRST:
  263. walk._currentNode = 0;
  264. break;
  265. case REPEAT_TELE_FIRST:
  266. npc.teleToLocation(npc.getSpawn().getLocx(),npc.getSpawn().getLocy(),npc.getSpawn().getLocz());
  267. walk._currentNode = 0;
  268. }
  269. }
  270. else if (walk._currentNode == -1) //First node arrived, when direction is first <-- last
  271. {
  272. walk._currentNode = 1;
  273. walk._forward = true;
  274. }
  275. L2NpcWalkerNode node = walk.getCurrentNode();
  276. npc.setIsRunning(node.getRunning());
  277. if (walk.getRoute().debug())
  278. Announcements.getInstance().announceToAll("Continue to node " + Integer.toString(walk._currentNode));
  279. npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(node.getMoveX(), node.getMoveY(), node.getMoveZ(), 0));
  280. walk._blocked = false;
  281. }
  282. }
  283. }
  284. }
  285. public void cancelMoving(L2Npc npc)
  286. {
  287. if (_activeRoutes.containsKey(npc.getObjectId()))
  288. {
  289. _activeRoutes.get(npc.getObjectId())._walkCheckTask.cancel(true);
  290. _activeRoutes.remove(npc.getObjectId());
  291. npc.getKnownList().stopTrackingTask();
  292. //Announcements.getInstance().announceToAll("Moving cancelled!");
  293. }
  294. }
  295. public void resumeMoving(final L2Npc npc)
  296. {
  297. if (!_activeRoutes.containsKey(npc.getObjectId()))
  298. return;
  299. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  300. walk._suspended = false;
  301. startMoving(npc, walk.getRoute().getId());
  302. }
  303. public void stopMoving(L2Npc npc, boolean suspend)
  304. {
  305. if (!_activeRoutes.containsKey(npc.getObjectId()))
  306. return;
  307. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  308. walk._suspended = suspend;
  309. npc.stopMove(null);
  310. npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  311. }
  312. public void onArrived(final L2Npc npc)
  313. {
  314. if (_activeRoutes.containsKey(npc.getObjectId()))
  315. {
  316. WalkInfo walk = _activeRoutes.get(npc.getObjectId());
  317. //Opposite should not happen... but happens sometime
  318. if (walk._currentNode >= 0 && walk._currentNode < walk.getRoute().getNodesCount())
  319. {
  320. L2NpcWalkerNode node = walk.getRoute().getNodeList().get(walk._currentNode);
  321. if (node.getMoveX() == npc.getX() && node.getMoveY() == npc.getY())
  322. {
  323. if (walk.getRoute().debug())
  324. {
  325. Announcements.getInstance().announceToAll("Arrived to node " + Integer.toString(walk._currentNode));
  326. //Announcements.getInstance().announceToAll("Done in " + Long.toString((System.currentTimeMillis() - walk._lastActionTime) / 1000) + " s.");
  327. }
  328. walk._nodeArrived = true;
  329. if (walk.getRoute().getRepeatType() != REPEAT_RANDOM)
  330. {
  331. if (walk._forward)
  332. walk._currentNode++;
  333. else
  334. walk._currentNode--;
  335. }
  336. int delay;
  337. if (walk._currentNode >= walk.getRoute().getNodesCount())
  338. delay = walk.getRoute().getLastNode().getDelay();
  339. else if (walk._currentNode < 0)
  340. delay = walk.getRoute().getNodeList().get(0).getDelay();
  341. else
  342. delay = walk.getCurrentNode().getDelay();
  343. walk._blocked = true; //prevents to be ran from walk check task, if there is delay in this node.
  344. //walk._lastActionTime = System.currentTimeMillis();
  345. ThreadPoolManager.getInstance().scheduleGeneral(new ArrivedTask(npc, walk), 100 + delay * 1000L);
  346. }
  347. }
  348. }
  349. }
  350. public void onDeath(L2Npc npc)
  351. {
  352. if (_activeRoutes.containsKey(npc.getObjectId()))
  353. cancelMoving(npc);
  354. }
  355. private class ArrivedTask implements Runnable
  356. {
  357. WalkInfo _walk;
  358. L2Npc _npc;
  359. public ArrivedTask(L2Npc npc, WalkInfo walk)
  360. {
  361. _npc = npc;
  362. _walk = walk;
  363. }
  364. @Override
  365. public void run()
  366. {
  367. _walk._blocked = false;
  368. startMoving(_npc,_walk. getRoute().getId());
  369. }
  370. }
  371. @SuppressWarnings("synthetic-access")
  372. private static class SingletonHolder
  373. {
  374. protected static final WalkingManager _instance = new WalkingManager();
  375. }
  376. }