PathFinding.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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 net.sf.l2j.gameserver.pathfinding;
  16. import java.util.LinkedList;
  17. import java.util.List;
  18. import javolution.util.FastList;
  19. import net.sf.l2j.Config;
  20. import net.sf.l2j.gameserver.GeoData;
  21. import net.sf.l2j.gameserver.model.L2World;
  22. import net.sf.l2j.gameserver.pathfinding.cellnodes.CellPathFinding;
  23. import net.sf.l2j.gameserver.pathfinding.geonodes.GeoPathFinding;
  24. import net.sf.l2j.gameserver.pathfinding.utils.BinaryNodeHeap;
  25. import net.sf.l2j.gameserver.pathfinding.utils.FastNodeList;
  26. /**
  27. *
  28. * @author -Nemesiss-
  29. */
  30. public abstract class PathFinding
  31. {
  32. private static PathFinding _instance;
  33. public static PathFinding getInstance()
  34. {
  35. if (_instance == null)
  36. {
  37. if (!Config.GEODATA_CELLFINDING)
  38. {
  39. //Higher Memory Usage, Smaller Cpu Usage
  40. return GeoPathFinding.getInstance();
  41. }
  42. else // Cell pathfinding, calculated directly from geodata files
  43. {
  44. return CellPathFinding.getInstance();
  45. }
  46. }
  47. return _instance;
  48. }
  49. public abstract boolean pathNodesExist(short regionoffset);
  50. public abstract List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz);
  51. public abstract Node[] readNeighbors(Node n, int idx);
  52. public List<AbstractNodeLoc> search(Node start, Node end)
  53. {
  54. // The simplest grid-based pathfinding.
  55. // Drawback is not having higher cost for diagonal movement (means funny routes)
  56. // Could be optimized e.g. not to calculate backwards as far as forwards.
  57. // List of Visited Nodes
  58. LinkedList<Node> visited = new LinkedList<Node>();
  59. // List of Nodes to Visit
  60. LinkedList<Node> to_visit = new LinkedList<Node>();
  61. to_visit.add(start);
  62. int i = 0;
  63. while (i < 800)
  64. {
  65. Node node;
  66. try
  67. {
  68. node = to_visit.removeFirst();
  69. }
  70. catch (Exception e)
  71. {
  72. // No Path found
  73. return null;
  74. }
  75. if (node.equals(end)) //path found!
  76. return constructPath(node);
  77. else
  78. {
  79. i++;
  80. visited.add(node);
  81. node.attachNeighbors();
  82. Node[] neighbors = node.getNeighbors();
  83. if (neighbors == null) continue;
  84. for (Node n : neighbors)
  85. {
  86. if (!visited.contains(n) && !to_visit.contains(n))
  87. {
  88. n.setParent(node);
  89. to_visit.add(n);
  90. }
  91. }
  92. }
  93. }
  94. //No Path found
  95. return null;
  96. }
  97. public List<AbstractNodeLoc> searchByClosest(Node start, Node end)
  98. {
  99. // Note: This is the version for cell-based calculation, maybe 10x harder
  100. // on cpu than from block-based pathnode files. However produces better routes.
  101. // Always continues checking from the closest to target non-blocked
  102. // node from to_visit list. There's extra length in path if needed
  103. // to go backwards/sideways but when moving generally forwards, this is extra fast
  104. // and accurate. And can reach insane distances (try it with 8000 nodes..).
  105. // Minimum required node count would be around 300-400.
  106. // Generally returns a bit (only a bit) more intelligent looking routes than
  107. // the basic version. Not a true distance image (which would increase CPU
  108. // load) level of intelligence though.
  109. // List of Visited Nodes
  110. FastNodeList visited = new FastNodeList(3500);
  111. // List of Nodes to Visit
  112. LinkedList<Node> to_visit = new LinkedList<Node>();
  113. to_visit.add(start);
  114. int targetx = end.getLoc().getNodeX();
  115. int targety = end.getLoc().getNodeY();
  116. int targetz = end.getLoc().getZ();
  117. int dx, dy, dz;
  118. boolean added;
  119. int i = 0;
  120. while (i < 3500)
  121. {
  122. Node node;
  123. try
  124. {
  125. node = to_visit.removeFirst();
  126. }
  127. catch (Exception e)
  128. {
  129. // No Path found
  130. return null;
  131. }
  132. i++;
  133. visited.add(node);
  134. node.attachNeighbors();
  135. if (node.equals(end)) {
  136. //path found! note that node z coordinate is updated only in attach
  137. //to improve performance (alternative: much more checks)
  138. //System.out.println("path found, i:"+i);
  139. return constructPath(node);
  140. }
  141. Node[] neighbors = node.getNeighbors();
  142. if (neighbors == null) continue;
  143. for (Node n : neighbors)
  144. {
  145. if (!visited.containsRev(n) && !to_visit.contains(n))
  146. {
  147. added = false;
  148. n.setParent(node);
  149. dx = targetx - n.getLoc().getNodeX();
  150. dy = targety - n.getLoc().getNodeY();
  151. dz = targetz - n.getLoc().getZ();
  152. n.setCost(dx*dx+dy*dy+dz*dz/*+n.getCost()*/);
  153. for (int index = 0; index < to_visit.size(); index++)
  154. {
  155. // supposed to find it quite early..
  156. if (to_visit.get(index).getCost() > n.getCost())
  157. {
  158. to_visit.add(index, n);
  159. added = true;
  160. break;
  161. }
  162. }
  163. if (!added) to_visit.addLast(n);
  164. }
  165. }
  166. }
  167. //No Path found
  168. //System.out.println("no path found");
  169. return null;
  170. }
  171. public List<AbstractNodeLoc> searchByClosest2(Node start, Node end)
  172. {
  173. // Always continues checking from the closest to target non-blocked
  174. // node from to_visit list. There's extra length in path if needed
  175. // to go backwards/sideways but when moving generally forwards, this is extra fast
  176. // and accurate. And can reach insane distances (try it with 800 nodes..).
  177. // Minimum required node count would be around 300-400.
  178. // Generally returns a bit (only a bit) more intelligent looking routes than
  179. // the basic version. Not a true distance image (which would increase CPU
  180. // load) level of intelligence though.
  181. // List of Visited Nodes
  182. FastNodeList visited = new FastNodeList(550);
  183. // List of Nodes to Visit
  184. LinkedList<Node> to_visit = new LinkedList<Node>();
  185. to_visit.add(start);
  186. int targetx = end.getLoc().getNodeX();
  187. int targety = end.getLoc().getNodeY();
  188. int dx, dy;
  189. boolean added;
  190. int i = 0;
  191. while (i < 550)
  192. {
  193. Node node;
  194. try
  195. {
  196. node = to_visit.removeFirst();
  197. }
  198. catch (Exception e)
  199. {
  200. // No Path found
  201. return null;
  202. }
  203. if (node.equals(end)) //path found!
  204. return constructPath2(node);
  205. else
  206. {
  207. i++;
  208. visited.add(node);
  209. node.attachNeighbors();
  210. Node[] neighbors = node.getNeighbors();
  211. if (neighbors == null) continue;
  212. for (Node n : neighbors)
  213. {
  214. if (!visited.containsRev(n) && !to_visit.contains(n))
  215. {
  216. added = false;
  217. n.setParent(node);
  218. dx = targetx - n.getLoc().getNodeX();
  219. dy = targety - n.getLoc().getNodeY();
  220. n.setCost(dx*dx+dy*dy);
  221. for (int index = 0; index < to_visit.size(); index++)
  222. {
  223. // supposed to find it quite early..
  224. if (to_visit.get(index).getCost() > n.getCost())
  225. {
  226. to_visit.add(index, n);
  227. added = true;
  228. break;
  229. }
  230. }
  231. if (!added) to_visit.addLast(n);
  232. }
  233. }
  234. }
  235. }
  236. //No Path found
  237. return null;
  238. }
  239. public List<AbstractNodeLoc> searchAStar(Node start, Node end)
  240. {
  241. // Not operational yet?
  242. int start_x = start.getLoc().getX();
  243. int start_y = start.getLoc().getY();
  244. int end_x = end.getLoc().getX();
  245. int end_y = end.getLoc().getY();
  246. //List of Visited Nodes
  247. FastNodeList visited = new FastNodeList(800);//TODO! Add limit to cfg
  248. // List of Nodes to Visit
  249. BinaryNodeHeap to_visit = new BinaryNodeHeap(800);
  250. to_visit.add(start);
  251. int i = 0;
  252. while (i < 800)//TODO! Add limit to cfg
  253. {
  254. Node node;
  255. try
  256. {
  257. node = to_visit.removeFirst();
  258. }
  259. catch (Exception e)
  260. {
  261. // No Path found
  262. return null;
  263. }
  264. if (node.equals(end)) //path found!
  265. return constructPath(node);
  266. else
  267. {
  268. visited.add(node);
  269. node.attachNeighbors();
  270. for (Node n : node.getNeighbors())
  271. {
  272. if (!visited.contains(n) && !to_visit.contains(n))
  273. {
  274. i++;
  275. n.setParent(node);
  276. n.setCost(Math.abs(start_x - n.getLoc().getNodeX())+Math.abs(start_y - n.getLoc().getNodeY())
  277. +Math.abs(end_x - n.getLoc().getNodeX())+Math.abs(end_y - n.getLoc().getNodeY()));
  278. to_visit.add(n);
  279. }
  280. }
  281. }
  282. }
  283. //No Path found
  284. return null;
  285. }
  286. public List<AbstractNodeLoc> constructPath(Node node)
  287. {
  288. LinkedList<AbstractNodeLoc> path = new LinkedList<AbstractNodeLoc>();
  289. int previousdirectionx = -1000;
  290. int previousdirectiony = -1000;
  291. int directionx;
  292. int directiony;
  293. while (node.getParent() != null)
  294. {
  295. // only add a new route point if moving direction changes
  296. if (node.getParent().getParent() != null // to check and clean diagonal movement
  297. && Math.abs(node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX()) == 1
  298. && Math.abs(node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY()) == 1)
  299. {
  300. directionx = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX();
  301. directiony = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY();
  302. }
  303. else
  304. {
  305. directionx = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
  306. directiony = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
  307. }
  308. if(directionx != previousdirectionx || directiony != previousdirectiony)
  309. {
  310. previousdirectionx = directionx;
  311. previousdirectiony = directiony;
  312. path.addFirst(node.getLoc());
  313. }
  314. node = node.getParent();
  315. }
  316. // then LOS based filtering to reduce the number of route points
  317. if (path.size() > 4)
  318. {
  319. //System.out.println("pathsize:"+path.size());
  320. List<Integer> valueList = new FastList<Integer>();
  321. for (int index = 0; index < path.size()-3; index = index +3)
  322. {
  323. //System.out.println("Attempt filter");
  324. if (GeoData.getInstance().canMoveFromToTarget(path.get(index).getNodeX(), path.get(index).getNodeY(), path.get(index).getZ(), path.get(index+3).getNodeX(), path.get(index+3).getNodeY(), path.get(index+3).getZ()))
  325. {
  326. //System.out.println("filtering i:"+(index+1));
  327. valueList.add(index+1);
  328. valueList.add(index+2);
  329. }
  330. }
  331. for (int index = valueList.size()-1; index >= 0; index--)
  332. {
  333. path.remove(valueList.get(index).intValue());
  334. }
  335. //System.out.println("pathsize:"+path.size());
  336. }
  337. return path;
  338. }
  339. public List<AbstractNodeLoc> constructPath2(Node node)
  340. {
  341. LinkedList<AbstractNodeLoc> path = new LinkedList<AbstractNodeLoc>();
  342. int previousdirectionx = -1000;
  343. int previousdirectiony = -1000;
  344. int directionx;
  345. int directiony;
  346. while (node.getParent() != null)
  347. {
  348. // only add a new route point if moving direction changes
  349. directionx = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
  350. directiony = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
  351. if(directionx != previousdirectionx || directiony != previousdirectiony)
  352. {
  353. previousdirectionx = directionx;
  354. previousdirectiony = directiony;
  355. path.addFirst(node.getLoc());
  356. }
  357. node = node.getParent();
  358. }
  359. return path;
  360. }
  361. /**
  362. * Convert geodata position to pathnode position
  363. * @param geo_pos
  364. * @return pathnode position
  365. */
  366. public short getNodePos(int geo_pos)
  367. {
  368. return (short)(geo_pos >> 3); //OK?
  369. }
  370. /**
  371. * Convert node position to pathnode block position
  372. * @param geo_pos
  373. * @return pathnode block position (0...255)
  374. */
  375. public short getNodeBlock(int node_pos)
  376. {
  377. return (short)(node_pos % 256);
  378. }
  379. public byte getRegionX(int node_pos)
  380. {
  381. return (byte)((node_pos >> 8) + 15);
  382. }
  383. public byte getRegionY(int node_pos)
  384. {
  385. return (byte)((node_pos >> 8) + 10);
  386. }
  387. public short getRegionOffset(byte rx, byte ry)
  388. {
  389. return (short)((rx << 5) + ry);
  390. }
  391. /**
  392. * Convert pathnode x to World x position
  393. * @param node_x, rx
  394. * @return
  395. */
  396. public int calculateWorldX(short node_x)
  397. {
  398. return L2World.MAP_MIN_X + node_x * 128 + 48 ;
  399. }
  400. /**
  401. * Convert pathnode y to World y position
  402. * @param node_y
  403. * @return
  404. */
  405. public int calculateWorldY(short node_y)
  406. {
  407. return L2World.MAP_MIN_Y + node_y * 128 + 48 ;
  408. }
  409. }