WalkingManager.java 14 KB

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