CellNodeBuffer.java 8.3 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.pathfinding.cellnodes;
  16. import java.util.concurrent.locks.ReentrantLock;
  17. import javolution.util.FastList;
  18. import com.l2jserver.Config;
  19. /**
  20. * @author DS Credits to Diamond
  21. */
  22. public class CellNodeBuffer
  23. {
  24. private static final byte EAST = 1;
  25. private static final byte WEST = 2;
  26. private static final byte SOUTH = 4;
  27. private static final byte NORTH = 8;
  28. private static final byte NSWE_ALL = 15;
  29. private static final byte NSWE_NONE = 0;
  30. private static final int MAX_ITERATIONS = 3500;
  31. private final ReentrantLock _lock = new ReentrantLock();
  32. private final int _mapSize;
  33. private final CellNode[][] _buffer;
  34. private int _baseX = 0;
  35. private int _baseY = 0;
  36. private int _targetX = 0;
  37. private int _targetY = 0;
  38. private short _targetZ = 0;
  39. private long _timeStamp = 0;
  40. private long _lastElapsedTime = 0;
  41. private CellNode _current = null;
  42. public CellNodeBuffer(int size)
  43. {
  44. _mapSize = size;
  45. _buffer = new CellNode[_mapSize][_mapSize];
  46. }
  47. public final boolean lock()
  48. {
  49. return _lock.tryLock();
  50. }
  51. public final CellNode findPath(int x, int y, short z, int tx, int ty, short tz)
  52. {
  53. _timeStamp = System.currentTimeMillis();
  54. _baseX = x + ((tx - x - _mapSize) / 2); // middle of the line (x,y) - (tx,ty)
  55. _baseY = y + ((ty - y - _mapSize) / 2); // will be in the center of the buffer
  56. _targetX = tx;
  57. _targetY = ty;
  58. _targetZ = tz;
  59. _current = getNode(x, y, z);
  60. _current.setCost(getCost(x, y, z, Config.HIGH_WEIGHT));
  61. for (int count = 0; count < MAX_ITERATIONS; count++)
  62. {
  63. if ((_current.getLoc().getNodeX() == _targetX) && (_current.getLoc().getNodeY() == _targetY) && (Math.abs(_current.getLoc().getZ() - _targetZ) < 64))
  64. {
  65. return _current; // found
  66. }
  67. getNeighbors();
  68. if (_current.getNext() == null)
  69. {
  70. return null; // no more ways
  71. }
  72. _current = _current.getNext();
  73. }
  74. return null;
  75. }
  76. public final void free()
  77. {
  78. _current = null;
  79. CellNode node;
  80. for (int i = 0; i < _mapSize; i++)
  81. {
  82. for (int j = 0; j < _mapSize; j++)
  83. {
  84. node = _buffer[i][j];
  85. if (node != null)
  86. {
  87. node.free();
  88. }
  89. }
  90. }
  91. _lock.unlock();
  92. _lastElapsedTime = System.currentTimeMillis() - _timeStamp;
  93. }
  94. public final long getElapsedTime()
  95. {
  96. return _lastElapsedTime;
  97. }
  98. public final FastList<CellNode> debugPath()
  99. {
  100. FastList<CellNode> result = new FastList<>();
  101. for (CellNode n = _current; n.getParent() != null; n = (CellNode) n.getParent())
  102. {
  103. result.add(n);
  104. n.setCost(-n.getCost());
  105. }
  106. for (int i = 0; i < _mapSize; i++)
  107. {
  108. for (int j = 0; j < _mapSize; j++)
  109. {
  110. CellNode n = _buffer[i][j];
  111. if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
  112. {
  113. continue;
  114. }
  115. result.add(n);
  116. }
  117. }
  118. return result;
  119. }
  120. private final void getNeighbors()
  121. {
  122. final short NSWE = ((NodeLoc) _current.getLoc()).getNSWE();
  123. if (NSWE == NSWE_NONE)
  124. {
  125. return;
  126. }
  127. final int x = _current.getLoc().getNodeX();
  128. final int y = _current.getLoc().getNodeY();
  129. final short z = _current.getLoc().getZ();
  130. CellNode nodeE = null;
  131. CellNode nodeS = null;
  132. CellNode nodeW = null;
  133. CellNode nodeN = null;
  134. // East
  135. if ((NSWE & EAST) != 0)
  136. {
  137. nodeE = addNode(x + 1, y, z, false);
  138. }
  139. // South
  140. if ((NSWE & SOUTH) != 0)
  141. {
  142. nodeS = addNode(x, y + 1, z, false);
  143. }
  144. // West
  145. if ((NSWE & WEST) != 0)
  146. {
  147. nodeW = addNode(x - 1, y, z, false);
  148. }
  149. // North
  150. if ((NSWE & NORTH) != 0)
  151. {
  152. nodeN = addNode(x, y - 1, z, false);
  153. }
  154. if (Config.ADVANCED_DIAGONAL_STRATEGY)
  155. {
  156. // SouthEast
  157. if ((nodeE != null) && (nodeS != null))
  158. {
  159. if (((((NodeLoc) nodeE.getLoc()).getNSWE() & SOUTH) != 0) && ((((NodeLoc) nodeS.getLoc()).getNSWE() & EAST) != 0))
  160. {
  161. addNode(x + 1, y + 1, z, true);
  162. }
  163. }
  164. // SouthWest
  165. if ((nodeS != null) && (nodeW != null))
  166. {
  167. if (((((NodeLoc) nodeW.getLoc()).getNSWE() & SOUTH) != 0) && ((((NodeLoc) nodeS.getLoc()).getNSWE() & WEST) != 0))
  168. {
  169. addNode(x - 1, y + 1, z, true);
  170. }
  171. }
  172. // NorthEast
  173. if ((nodeN != null) && (nodeE != null))
  174. {
  175. if (((((NodeLoc) nodeE.getLoc()).getNSWE() & NORTH) != 0) && ((((NodeLoc) nodeN.getLoc()).getNSWE() & EAST) != 0))
  176. {
  177. addNode(x + 1, y - 1, z, true);
  178. }
  179. }
  180. // NorthWest
  181. if ((nodeN != null) && (nodeW != null))
  182. {
  183. if (((((NodeLoc) nodeW.getLoc()).getNSWE() & NORTH) != 0) && ((((NodeLoc) nodeN.getLoc()).getNSWE() & WEST) != 0))
  184. {
  185. addNode(x - 1, y - 1, z, true);
  186. }
  187. }
  188. }
  189. }
  190. private final CellNode getNode(int x, int y, short z)
  191. {
  192. final int aX = x - _baseX;
  193. if ((aX < 0) || (aX >= _mapSize))
  194. {
  195. return null;
  196. }
  197. final int aY = y - _baseY;
  198. if ((aY < 0) || (aY >= _mapSize))
  199. {
  200. return null;
  201. }
  202. CellNode result = _buffer[aX][aY];
  203. if (result == null)
  204. {
  205. result = new CellNode(new NodeLoc(x, y, z));
  206. _buffer[aX][aY] = result;
  207. }
  208. else if (!result.isInUse())
  209. {
  210. result.setInUse();
  211. // reinit node if needed
  212. if (result.getLoc() != null)
  213. {
  214. ((NodeLoc) result.getLoc()).set(x, y, z);
  215. }
  216. else
  217. {
  218. result.setLoc(new NodeLoc(x, y, z));
  219. }
  220. }
  221. return result;
  222. }
  223. private final CellNode addNode(int x, int y, short z, boolean diagonal)
  224. {
  225. CellNode newNode = getNode(x, y, z);
  226. if (newNode == null)
  227. {
  228. return null;
  229. }
  230. if (newNode.getCost() >= 0)
  231. {
  232. return newNode;
  233. }
  234. final short geoZ = newNode.getLoc().getZ();
  235. final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
  236. float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
  237. if ((((NodeLoc) newNode.getLoc()).getNSWE() != NSWE_ALL) || (stepZ > 16))
  238. {
  239. weight = Config.HIGH_WEIGHT;
  240. }
  241. else
  242. {
  243. if (isHighWeight(x + 1, y, geoZ))
  244. {
  245. weight = Config.MEDIUM_WEIGHT;
  246. }
  247. else if (isHighWeight(x - 1, y, geoZ))
  248. {
  249. weight = Config.MEDIUM_WEIGHT;
  250. }
  251. else if (isHighWeight(x, y + 1, geoZ))
  252. {
  253. weight = Config.MEDIUM_WEIGHT;
  254. }
  255. else if (isHighWeight(x, y - 1, geoZ))
  256. {
  257. weight = Config.MEDIUM_WEIGHT;
  258. }
  259. }
  260. newNode.setParent(_current);
  261. newNode.setCost(getCost(x, y, geoZ, weight));
  262. CellNode node = _current;
  263. int count = 0;
  264. while ((node.getNext() != null) && (count < (MAX_ITERATIONS * 4)))
  265. {
  266. count++;
  267. if (node.getNext().getCost() > newNode.getCost())
  268. {
  269. // insert node into a chain
  270. newNode.setNext(node.getNext());
  271. break;
  272. }
  273. node = node.getNext();
  274. }
  275. if (count == (MAX_ITERATIONS * 4))
  276. {
  277. System.err.println("Pathfinding: too long loop detected, cost:" + newNode.getCost());
  278. }
  279. node.setNext(newNode); // add last
  280. return newNode;
  281. }
  282. private final boolean isHighWeight(int x, int y, short z)
  283. {
  284. final CellNode result = getNode(x, y, z);
  285. if (result == null)
  286. {
  287. return true;
  288. }
  289. if (((NodeLoc) result.getLoc()).getNSWE() != NSWE_ALL)
  290. {
  291. return true;
  292. }
  293. if (Math.abs(result.getLoc().getZ() - z) > 16)
  294. {
  295. return true;
  296. }
  297. return false;
  298. }
  299. private final double getCost(int x, int y, short z, float weight)
  300. {
  301. final int dX = x - _targetX;
  302. final int dY = y - _targetY;
  303. final int dZ = z - _targetZ;
  304. // Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16
  305. double result = Math.sqrt((dX * dX) + (dY * dY) + ((dZ * dZ) / 256));
  306. if (result > weight)
  307. {
  308. result += weight;
  309. }
  310. if (result > Float.MAX_VALUE)
  311. {
  312. result = Float.MAX_VALUE;
  313. }
  314. return result;
  315. }
  316. }