path.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "path.hpp"
  4. #include <cmath>
  5. #include <cstdio>
  6. #include <cstdlib>
  7. #include <cstring>
  8. #include <common/cbasetypes.hpp>
  9. #include <common/db.hpp>
  10. #include <common/malloc.hpp>
  11. #include <common/nullpo.hpp>
  12. #include <common/random.hpp>
  13. #include <common/showmsg.hpp>
  14. #include "battle.hpp"
  15. #include "map.hpp"
  16. #define SET_OPEN 0
  17. #define SET_CLOSED 1
  18. #define PATH_DIR_NORTH 1
  19. #define PATH_DIR_WEST 2
  20. #define PATH_DIR_SOUTH 4
  21. #define PATH_DIR_EAST 8
  22. /// @name Structures and defines for A* pathfinding
  23. /// @{
  24. /// Path node
  25. struct path_node {
  26. struct path_node *parent; ///< pointer to parent (for path reconstruction)
  27. short x; ///< X-coordinate
  28. short y; ///< Y-coordinate
  29. short g_cost; ///< Actual cost from start to this node
  30. short f_cost; ///< g_cost + heuristic(this, goal)
  31. short flag; ///< SET_OPEN / SET_CLOSED
  32. };
  33. /// Binary heap of path nodes
  34. BHEAP_STRUCT_DECL(node_heap, struct path_node*);
  35. static BHEAP_STRUCT_VAR(node_heap, g_open_set); // use static heap for all path calculations
  36. // it get's initialized in do_init_path, freed in do_final_path.
  37. /// Comparator for binary heap of path nodes (minimum cost at top)
  38. #define NODE_MINTOPCMP(i,j) ((i)->f_cost - (j)->f_cost)
  39. #define calc_index(x,y) (((x)+(y)*MAX_WALKPATH) & (MAX_WALKPATH*MAX_WALKPATH-1))
  40. /// Estimates the cost from (x0,y0) to (x1,y1).
  41. /// This is inadmissible (overestimating) heuristic used by game client.
  42. #define heuristic(x0, y0, x1, y1) (MOVE_COST * (abs((x1) - (x0)) + abs((y1) - (y0)))) // Manhattan distance
  43. /// @}
  44. // Translates dx,dy into walking direction
  45. static enum directions walk_choices [3][3] =
  46. {
  47. {DIR_NORTHWEST,DIR_NORTH,DIR_NORTHEAST},
  48. {DIR_WEST,DIR_CENTER,DIR_EAST},
  49. {DIR_SOUTHWEST,DIR_SOUTH,DIR_SOUTHEAST},
  50. };
  51. void do_init_path(){
  52. BHEAP_INIT(g_open_set); // [fwi]: BHEAP_STRUCT_VAR already initialized the heap, this is rudendant & just for code-conformance/readability
  53. }//
  54. void do_final_path(){
  55. BHEAP_CLEAR(g_open_set);
  56. }//
  57. /*==========================================
  58. * Find the closest reachable cell, 'count' cells away from (x0,y0) in direction (dx,dy).
  59. * Income after the coordinates of the blow
  60. *------------------------------------------*/
  61. int path_blownpos(int16 m,int16 x0,int16 y0,int16 dx,int16 dy,int count)
  62. {
  63. struct map_data *mapdata = map_getmapdata(m);
  64. if( !mapdata->cell )
  65. return -1;
  66. if( count>25 ){ //Cap to prevent too much processing...?
  67. ShowWarning("path_blownpos: count too many %d !\n",count);
  68. count=25;
  69. }
  70. if( dx > 1 || dx < -1 || dy > 1 || dy < -1 ){
  71. ShowError("path_blownpos: illegal dx=%d or dy=%d !\n",dx,dy);
  72. dx=(dx>0)?1:((dx<0)?-1:0);
  73. dy=(dy>0)?1:((dy<0)?-1:0);
  74. }
  75. while( count > 0 && (dx != 0 || dy != 0) )
  76. {
  77. if( !map_getcellp(mapdata,x0+dx,y0+dy,CELL_CHKPASS) )
  78. {
  79. if (battle_config.path_blown_halt)
  80. break;
  81. else
  82. {// attempt partial movement
  83. int fx = ( dx != 0 && map_getcellp(mapdata,x0+dx,y0,CELL_CHKPASS) );
  84. int fy = ( dy != 0 && map_getcellp(mapdata,x0,y0+dy,CELL_CHKPASS) );
  85. if( fx && fy )
  86. {
  87. if(rnd_chance(50, 100))
  88. dx=0;
  89. else
  90. dy=0;
  91. }
  92. if( !fx )
  93. dx=0;
  94. if( !fy )
  95. dy=0;
  96. }
  97. }
  98. x0 += dx;
  99. y0 += dy;
  100. count--;
  101. }
  102. return (x0<<16)|y0; //TODO: use 'struct point' here instead?
  103. }
  104. /*==========================================
  105. * is ranged attack from (x0,y0) to (x1,y1) possible?
  106. *------------------------------------------*/
  107. bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16 x1,int16 y1,cell_chk cell)
  108. {
  109. int dx, dy;
  110. int wx = 0, wy = 0;
  111. int weight;
  112. struct map_data *mapdata = map_getmapdata(m);
  113. struct shootpath_data s_spd;
  114. if( spd == nullptr )
  115. spd = &s_spd; // use dummy output variable
  116. if (!mapdata->cell)
  117. return false;
  118. dx = (x1 - x0);
  119. if (dx < 0) {
  120. std::swap(x0, x1);
  121. std::swap(y0, y1);
  122. dx = -dx;
  123. }
  124. dy = (y1 - y0);
  125. spd->rx = spd->ry = 0;
  126. spd->len = 1;
  127. spd->x[0] = x0;
  128. spd->y[0] = y0;
  129. if (dx > abs(dy)) {
  130. weight = dx;
  131. spd->ry = 1;
  132. } else {
  133. weight = abs(y1 - y0);
  134. spd->rx = 1;
  135. }
  136. while (x0 != x1 || y0 != y1)
  137. {
  138. wx += dx;
  139. wy += dy;
  140. if (wx >= weight) {
  141. wx -= weight;
  142. x0++;
  143. }
  144. if (wy >= weight) {
  145. wy -= weight;
  146. y0++;
  147. } else if (wy < 0) {
  148. wy += weight;
  149. y0--;
  150. }
  151. if( spd->len<MAX_WALKPATH )
  152. {
  153. spd->x[spd->len] = x0;
  154. spd->y[spd->len] = y0;
  155. spd->len++;
  156. }
  157. if ((x0 != x1 || y0 != y1) && map_getcellp(mapdata,x0,y0,cell))
  158. return false;
  159. }
  160. return true;
  161. }
  162. /// @name A* pathfinding related functions
  163. /// @{
  164. /// Pushes path_node to the binary node_heap.
  165. /// Ensures there is enough space in array to store new element.
  166. static void heap_push_node(struct node_heap *heap, struct path_node *node)
  167. {
  168. #ifndef __clang_analyzer__ // TODO: Figure out why clang's static analyzer doesn't like this
  169. BHEAP_ENSURE2(*heap, 1, 256, struct path_node **);
  170. BHEAP_PUSH2(*heap, node, NODE_MINTOPCMP);
  171. #endif // __clang_analyzer__
  172. }
  173. /// Updates path_node in the binary node_heap.
  174. static int heap_update_node(struct node_heap *heap, struct path_node *node)
  175. {
  176. int i;
  177. ARR_FIND(0, BHEAP_LENGTH(*heap), i, BHEAP_DATA(*heap)[i] == node);
  178. if (i == BHEAP_LENGTH(*heap)) {
  179. ShowError("heap_update_node: node not found\n");
  180. return 1;
  181. }
  182. BHEAP_UPDATE(*heap, i, NODE_MINTOPCMP);
  183. return 0;
  184. }
  185. /// Path_node processing in A* pathfinding.
  186. /// Adds new node to heap and updates/re-adds old ones if necessary.
  187. static int add_path(struct node_heap *heap, struct path_node *tp, int16 x, int16 y, int g_cost, struct path_node *parent, int h_cost)
  188. {
  189. int i = calc_index(x, y);
  190. if (tp[i].x == x && tp[i].y == y) { // We processed this node before
  191. if (g_cost < tp[i].g_cost) { // New path to this node is better than old one
  192. // Update costs and parent
  193. tp[i].g_cost = g_cost;
  194. tp[i].parent = parent;
  195. tp[i].f_cost = g_cost + h_cost;
  196. if (tp[i].flag == SET_CLOSED) {
  197. heap_push_node(heap, &tp[i]); // Put it in open set again
  198. }
  199. else if (heap_update_node(heap, &tp[i])) {
  200. return 1;
  201. }
  202. tp[i].flag = SET_OPEN;
  203. }
  204. return 0;
  205. }
  206. if (tp[i].x || tp[i].y) // Index is already taken; see `tp` array FIXME for details
  207. return 1;
  208. // New node
  209. tp[i].x = x;
  210. tp[i].y = y;
  211. tp[i].g_cost = g_cost;
  212. tp[i].parent = parent;
  213. tp[i].f_cost = g_cost + h_cost;
  214. tp[i].flag = SET_OPEN;
  215. heap_push_node(heap, &tp[i]);
  216. return 0;
  217. }
  218. ///@}
  219. /*==========================================
  220. * path search (x0,y0)->(x1,y1)
  221. * wpd: path info will be written here
  222. * flag: &1 = easy path search only
  223. * flag: &2 = call path_search_long instead
  224. * cell: type of obstruction to check for
  225. *
  226. * Note: uses global g_open_set, therefore this method can't be called in parallel or recursivly.
  227. *------------------------------------------*/
  228. bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int flag, cell_chk cell)
  229. {
  230. int i, x, y, dx = 0, dy = 0;
  231. struct map_data *mapdata = map_getmapdata(m);
  232. struct walkpath_data s_wpd;
  233. if (flag&2)
  234. return path_search_long(nullptr, m, x0, y0, x1, y1, cell);
  235. if (wpd == nullptr)
  236. wpd = &s_wpd; // use dummy output variable
  237. if (!mapdata->cell)
  238. return false;
  239. //Do not check starting cell as that would get you stuck.
  240. if (x0 < 0 || x0 >= mapdata->xs || y0 < 0 || y0 >= mapdata->ys /*|| map_getcellp(mapdata,x0,y0,cell)*/)
  241. return false;
  242. // Check destination cell
  243. if (x1 < 0 || x1 >= mapdata->xs || y1 < 0 || y1 >= mapdata->ys || map_getcellp(mapdata,x1,y1,cell))
  244. return false;
  245. if (flag&1) {
  246. // Try finding direct path to target
  247. // Direct path goes diagonally first, then in straight line.
  248. // calculate (sgn(x1-x0), sgn(y1-y0))
  249. dx = ((dx = x1-x0)) ? ((dx<0) ? -1 : 1) : 0;
  250. dy = ((dy = y1-y0)) ? ((dy<0) ? -1 : 1) : 0;
  251. x = x0; // Current position = starting cell
  252. y = y0;
  253. i = 0;
  254. while( i < ARRAYLENGTH(wpd->path) ) {
  255. wpd->path[i] = walk_choices[-dy + 1][dx + 1];
  256. i++;
  257. x += dx; // Advance current position
  258. y += dy;
  259. if( x == x1 ) dx = 0; // destination x reached, no longer move along x-axis
  260. if( y == y1 ) dy = 0; // destination y reached, no longer move along y-axis
  261. if( dx == 0 && dy == 0 )
  262. break; // success
  263. if( map_getcellp(mapdata,x,y,cell) )
  264. break; // obstacle = failure
  265. }
  266. if( x == x1 && y == y1 ) { // easy path successful.
  267. wpd->path_len = i;
  268. wpd->path_pos = 0;
  269. return true;
  270. }
  271. return false; // easy path unsuccessful
  272. } else { // !(flag&1)
  273. // FIXME: This array is too small to ensure all paths shorter than MAX_WALKPATH
  274. // can be found without node collision: calc_index(node1) = calc_index(node2).
  275. // Figure out more proper size or another way to keep track of known nodes.
  276. struct path_node tp[MAX_WALKPATH * MAX_WALKPATH];
  277. struct path_node *current, *it;
  278. int xs = mapdata->xs - 1;
  279. int ys = mapdata->ys - 1;
  280. int len = 0;
  281. int j;
  282. // A* (A-star) pathfinding
  283. // We always use A* for finding walkpaths because it is what game client uses.
  284. // Easy pathfinding cuts corners of non-walkable cells, but client always walks around it.
  285. BHEAP_RESET(g_open_set);
  286. memset(tp, 0, sizeof(tp));
  287. // Start node
  288. i = calc_index(x0, y0);
  289. tp[i].parent = nullptr;
  290. tp[i].x = x0;
  291. tp[i].y = y0;
  292. tp[i].g_cost = 0;
  293. tp[i].f_cost = heuristic(x0, y0, x1, y1);
  294. tp[i].flag = SET_OPEN;
  295. heap_push_node(&g_open_set, &tp[i]); // Put start node to 'open' set
  296. for(;;) {
  297. int e = 0; // error flag
  298. // Saves allowed directions for the current cell. Diagonal directions
  299. // are only allowed if both directions around it are allowed. This is
  300. // to prevent cutting corner of nearby wall.
  301. // For example, you can only go NW from the current cell, if you can
  302. // go N *and* you can go W. Otherwise you need to walk around the
  303. // (corner of the) non-walkable cell.
  304. int allowed_dirs = 0;
  305. int g_cost;
  306. if (BHEAP_LENGTH(g_open_set) == 0) {
  307. return false;
  308. }
  309. current = BHEAP_PEEK(g_open_set); // Look for the lowest f_cost node in the 'open' set
  310. BHEAP_POP2(g_open_set, NODE_MINTOPCMP); // Remove it from 'open' set
  311. x = current->x;
  312. y = current->y;
  313. g_cost = current->g_cost;
  314. current->flag = SET_CLOSED; // Add current node to 'closed' set
  315. if (x == x1 && y == y1) {
  316. break;
  317. }
  318. if (y < ys && !map_getcellp(mapdata, x, y+1, cell)) allowed_dirs |= PATH_DIR_NORTH;
  319. if (y > 0 && !map_getcellp(mapdata, x, y-1, cell)) allowed_dirs |= PATH_DIR_SOUTH;
  320. if (x < xs && !map_getcellp(mapdata, x+1, y, cell)) allowed_dirs |= PATH_DIR_EAST;
  321. if (x > 0 && !map_getcellp(mapdata, x-1, y, cell)) allowed_dirs |= PATH_DIR_WEST;
  322. #define chk_dir(d) ((allowed_dirs & (d)) == (d))
  323. // Process neighbors of current node
  324. if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_EAST) && !map_getcellp(mapdata, x+1, y-1, cell))
  325. e += add_path(&g_open_set, tp, x+1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y-1, x1, y1)); // (x+1, y-1) 5
  326. if (chk_dir(PATH_DIR_EAST))
  327. e += add_path(&g_open_set, tp, x+1, y, g_cost + MOVE_COST, current, heuristic(x+1, y, x1, y1)); // (x+1, y) 6
  328. if (chk_dir(PATH_DIR_NORTH|PATH_DIR_EAST) && !map_getcellp(mapdata, x+1, y+1, cell))
  329. e += add_path(&g_open_set, tp, x+1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y+1, x1, y1)); // (x+1, y+1) 7
  330. if (chk_dir(PATH_DIR_NORTH))
  331. e += add_path(&g_open_set, tp, x, y+1, g_cost + MOVE_COST, current, heuristic(x, y+1, x1, y1)); // (x, y+1) 0
  332. if (chk_dir(PATH_DIR_NORTH|PATH_DIR_WEST) && !map_getcellp(mapdata, x-1, y+1, cell))
  333. e += add_path(&g_open_set, tp, x-1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y+1, x1, y1)); // (x-1, y+1) 1
  334. if (chk_dir(PATH_DIR_WEST))
  335. e += add_path(&g_open_set, tp, x-1, y, g_cost + MOVE_COST, current, heuristic(x-1, y, x1, y1)); // (x-1, y) 2
  336. if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_WEST) && !map_getcellp(mapdata, x-1, y-1, cell))
  337. e += add_path(&g_open_set, tp, x-1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y-1, x1, y1)); // (x-1, y-1) 3
  338. if (chk_dir(PATH_DIR_SOUTH))
  339. e += add_path(&g_open_set, tp, x, y-1, g_cost + MOVE_COST, current, heuristic(x, y-1, x1, y1)); // (x, y-1) 4
  340. #undef chk_dir
  341. if (e) {
  342. return false;
  343. }
  344. }
  345. for (it = current; it->parent != nullptr; it = it->parent, len++);
  346. if (len > sizeof(wpd->path))
  347. return false;
  348. // Recreate path
  349. wpd->path_len = len;
  350. wpd->path_pos = 0;
  351. for (it = current, j = len-1; j >= 0; it = it->parent, j--) {
  352. dx = it->x - it->parent->x;
  353. dy = it->y - it->parent->y;
  354. wpd->path[j] = walk_choices[-dy + 1][dx + 1];
  355. }
  356. return true;
  357. } // A* end
  358. return false;
  359. }
  360. //Distance functions, taken from http://www.flipcode.com/articles/article_fastdistance.shtml
  361. bool check_distance(int dx, int dy, int distance)
  362. {
  363. #ifdef CIRCULAR_AREA
  364. //In this case, we just do a square comparison. Add 1 tile grace for diagonal range checks.
  365. return (dx*dx + dy*dy <= distance*distance + (dx&&dy?1:0));
  366. #else
  367. if (dx < 0) dx = -dx;
  368. if (dy < 0) dy = -dy;
  369. return ((dx<dy?dy:dx) <= distance);
  370. #endif
  371. }
  372. uint32 distance(int dx, int dy)
  373. {
  374. #ifdef CIRCULAR_AREA
  375. uint32 min, max;
  376. if ( dx < 0 ) dx = -dx;
  377. if ( dy < 0 ) dy = -dy;
  378. //There appears to be something wrong with the approximation below when either dx/dy is 0! [Skotlex]
  379. if ( dx == 0 ) return dy;
  380. if ( dy == 0 ) return dx;
  381. if ( dx < dy )
  382. {
  383. min = dx;
  384. max = dy;
  385. } else {
  386. min = dy;
  387. max = dx;
  388. }
  389. // coefficients equivalent to ( 123/128 * max ) and ( 51/128 * min )
  390. return ((( max << 8 ) + ( max << 3 ) - ( max << 4 ) - ( max << 1 ) +
  391. ( min << 7 ) - ( min << 5 ) + ( min << 3 ) - ( min << 1 )) >> 8 );
  392. #else
  393. if (dx < 0) dx = -dx;
  394. if (dy < 0) dy = -dy;
  395. return (dx<dy?dy:dx);
  396. #endif
  397. }
  398. /**
  399. * The client uses a circular distance instead of the square one. The circular distance
  400. * is only used by units sending their attack commands via the client (not monsters).
  401. * @param dx: Horizontal distance
  402. * @param dy: Vertical distance
  403. * @param distance: Distance to check against
  404. * @return Within distance(1); Not within distance(0);
  405. */
  406. bool check_distance_client(int dx, int dy, int distance)
  407. {
  408. if(distance < 0) distance = 0;
  409. return (distance_client(dx,dy) <= distance);
  410. }
  411. /**
  412. * The client uses a circular distance instead of the square one. The circular distance
  413. * is only used by units sending their attack commands via the client (not monsters).
  414. * @param dx: Horizontal distance
  415. * @param dy: Vertical distance
  416. * @return Circular distance
  417. */
  418. int distance_client(int dx, int dy)
  419. {
  420. double temp_dist = sqrt((double)(dx*dx + dy*dy));
  421. //Bonus factor used by client
  422. //This affects even horizontal/vertical lines so they are one cell longer than expected
  423. temp_dist -= 0.1;
  424. if(temp_dist < 0) temp_dist = 0;
  425. return ((int)temp_dist);
  426. }
  427. bool direction_diagonal( enum directions direction ){
  428. return direction == DIR_NORTHWEST || direction == DIR_SOUTHWEST || direction == DIR_SOUTHEAST || direction == DIR_NORTHEAST;
  429. }
  430. bool direction_opposite( enum directions direction ){
  431. if( direction == DIR_CENTER || direction == DIR_MAX ){
  432. return direction;
  433. }else{
  434. return static_cast<enum directions>( ( direction + DIR_MAX / 2 ) % DIR_MAX );
  435. }
  436. }