navi.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. #include "../config/core.hpp"
  2. #ifdef GENERATE_NAVI
  3. #include <sys/stat.h>
  4. #include <algorithm>
  5. #include <cstring>
  6. #include <fstream>
  7. #include <iostream>
  8. #include <chrono>
  9. #include <queue>
  10. #include <vector>
  11. #include "../common/db.hpp"
  12. #include "../common/showmsg.hpp"
  13. #include "../common/malloc.hpp"
  14. #include "map.hpp"
  15. #include "mob.hpp"
  16. #include "navi.hpp"
  17. #include "npc.hpp"
  18. #include "path.hpp"
  19. std::string filePrefix = "generated/clientside/data/luafiles514/lua files/navigation/";
  20. #define SET_OPEN 0
  21. #define SET_CLOSED 1
  22. #define PATH_DIR_NORTH 1
  23. #define PATH_DIR_WEST 2
  24. #define PATH_DIR_SOUTH 4
  25. #define PATH_DIR_EAST 8
  26. // @name Structures and defines for A* pathfinding
  27. // @{
  28. // Path node
  29. struct path_node {
  30. struct path_node *parent; // pointer to parent
  31. short x; // x coord
  32. short y; // y coord
  33. short g_cost; // Actual cost from start to this node
  34. short f_cost; // g_cost + heuristic(this, goal)
  35. short flag; // SET_OPEN / SET_CLOSED
  36. };
  37. /// Binary heap of path nodes
  38. BHEAP_STRUCT_DECL(node_heap, struct path_node*);
  39. static BHEAP_STRUCT_VAR(node_heap, g_open_set); // use static heap for all path calculations
  40. // it get's initialized in do_init_path, freed in do_final_path.
  41. /// Comparator for binary heap of path nodes (minimum cost at top)
  42. #define NODE_MINTOPCMP(i,j) ((i)->f_cost - (j)->f_cost)
  43. #define calc_index(x,y) (((x)+(y)*MAX_WALKPATH_NAVI) & (MAX_WALKPATH_NAVI*MAX_WALKPATH_NAVI-1))
  44. /// Estimates the cost from (x0,y0) to (x1,y1).
  45. /// This is inadmissible (overestimating) heuristic used by game client.
  46. #define heuristic(x0, y0, x1, y1) (MOVE_COST * (abs((x1) - (x0)) + abs((y1) - (y0)))) // Manhattan distance
  47. /// @}
  48. // Translates dx,dy into walking direction
  49. static enum directions walk_choices [3][3] =
  50. {
  51. {DIR_NORTHWEST,DIR_NORTH,DIR_NORTHEAST},
  52. {DIR_WEST,DIR_CENTER,DIR_EAST},
  53. {DIR_SOUTHWEST,DIR_SOUTH,DIR_SOUTHEAST},
  54. };
  55. /// @name A* pathfinding related functions
  56. /// @{
  57. /// Pushes path_node to the binary node_heap.
  58. /// Ensures there is enough space in array to store new element.
  59. #define swap_ptrcast_pathnode(a, b) swap_ptrcast(struct path_node *, a, b)
  60. static void heap_push_node(struct node_heap *heap, struct path_node *node)
  61. {
  62. #ifndef __clang_analyzer__ // TODO: Figure out why clang's static analyzer doesn't like this
  63. BHEAP_ENSURE2(*heap, 1, 256, struct path_node **);
  64. BHEAP_PUSH2(*heap, node, NODE_MINTOPCMP, swap_ptrcast_pathnode);
  65. #endif // __clang_analyzer__
  66. }
  67. /// Updates path_node in the binary node_heap.
  68. static int heap_update_node(struct node_heap *heap, struct path_node *node)
  69. {
  70. int i;
  71. ARR_FIND(0, BHEAP_LENGTH(*heap), i, BHEAP_DATA(*heap)[i] == node);
  72. if (i == BHEAP_LENGTH(*heap)) {
  73. ShowError("heap_update_node: node not found\n");
  74. return 1;
  75. }
  76. BHEAP_UPDATE(*heap, i, NODE_MINTOPCMP, swap_ptrcast_pathnode);
  77. return 0;
  78. }
  79. // end 1:1 copy of definitions from path.cpp
  80. // So we don't have to allocate every time, use static structures
  81. static struct path_node tp[MAX_WALKPATH_NAVI * MAX_WALKPATH_NAVI + 1];
  82. static int tpused[MAX_WALKPATH_NAVI * MAX_WALKPATH_NAVI + 1];
  83. /// Path_node processing in A* pathfinding.
  84. /// Adds new node to heap and updates/re-adds old ones if necessary.
  85. static int add_path(struct node_heap *heap, int16 x, int16 y, int g_cost, struct path_node *parent, int h_cost)
  86. {
  87. int i = calc_index(x, y);
  88. if (tpused[i] && tpused[i] == 1 + (x << 16 | y)) { // We processed this node before
  89. if (g_cost < tp[i].g_cost) { // New path to this node is better than old one
  90. // Update costs and parent
  91. tp[i].g_cost = g_cost;
  92. tp[i].parent = parent;
  93. tp[i].f_cost = g_cost + h_cost;
  94. if (tp[i].flag == SET_CLOSED) {
  95. heap_push_node(heap, &tp[i]); // Put it in open set again
  96. }
  97. else if (heap_update_node(heap, &tp[i])) {
  98. return 1;
  99. }
  100. tp[i].flag = SET_OPEN;
  101. }
  102. return 0;
  103. }
  104. if (tpused[i]) // Index is already taken; see `tp` array FIXME for details
  105. return 1;
  106. // New node
  107. tp[i].x = x;
  108. tp[i].y = y;
  109. tp[i].g_cost = g_cost;
  110. tp[i].parent = parent;
  111. tp[i].f_cost = g_cost + h_cost;
  112. tp[i].flag = SET_OPEN;
  113. tpused[i] = 1 + (x << 16 | y);
  114. heap_push_node(heap, &tp[i]);
  115. return 0;
  116. }
  117. ///@}
  118. /*==========================================
  119. * path search (from)->(dest)
  120. * wpd: path info will be written here
  121. * cell: type of obstruction to check for
  122. *
  123. * Note: uses global g_open_set, therefore this method can't be called in parallel or recursivly.
  124. *------------------------------------------*/
  125. bool navi_path_search(struct navi_walkpath_data *wpd, const struct navi_pos *from, const struct navi_pos *dest, cell_chk cell) {
  126. register int i, x, y, dx = 0, dy = 0;
  127. struct map_data *mapdata = map_getmapdata(from->m);
  128. struct navi_walkpath_data s_wpd;
  129. if (wpd == NULL)
  130. wpd = &s_wpd; // use dummy output variable
  131. if (from->m != dest->m)
  132. return false;
  133. if (!mapdata->cell)
  134. return false;
  135. //Do not check starting cell as that would get you stuck.
  136. if (from->x < 0 || from->x > mapdata->xs || from->y < 0 || from->y > mapdata->ys /*|| map_getcellp(mapdata,x0,y0,cell)*/)
  137. return false;
  138. // Check destination cell
  139. if (dest->x < 0 || dest->x > mapdata->xs || dest->y < 0 || dest->y > mapdata->ys || map_getcellp(mapdata, dest->x, dest->y, cell))
  140. return false;
  141. if (from->x == dest->x && from->y == dest->y) {
  142. wpd->path_len = 0;
  143. wpd->path_pos = 0;
  144. return true;
  145. }
  146. struct path_node *current, *it;
  147. int xs = mapdata->xs - 1;
  148. int ys = mapdata->ys - 1;
  149. int len = 0;
  150. int j;
  151. // A* (A-star) pathfinding
  152. // We always use A* for finding walkpaths because it is what game client uses.
  153. // Easy pathfinding cuts corners of non-walkable cells, but client always walks around it.
  154. BHEAP_RESET(g_open_set);
  155. memset(tpused, 0, sizeof(tpused));
  156. // Start node
  157. i = calc_index(from->x, from->y);
  158. tp[i].parent = NULL;
  159. tp[i].x = from->x;
  160. tp[i].y = from->y;
  161. tp[i].g_cost = 0;
  162. tp[i].f_cost = heuristic(from->x, from->y, dest->x, dest->y);
  163. tp[i].flag = SET_OPEN;
  164. tpused[i] = 1 + (from->x << 16 | from->y);
  165. heap_push_node(&g_open_set, &tp[i]); // Put start node to 'open' set
  166. for (;;) {
  167. int e = 0; // error flag
  168. // Saves allowed directions for the current cell. Diagonal directions
  169. // are only allowed if both directions around it are allowed. This is
  170. // to prevent cutting corner of nearby wall.
  171. // For example, you can only go NW from the current cell, if you can
  172. // go N *and* you can go W. Otherwise you need to walk around the
  173. // (corner of the) non-walkable cell.
  174. int allowed_dirs = 0;
  175. int g_cost;
  176. if (BHEAP_LENGTH(g_open_set) == 0) {
  177. return false;
  178. }
  179. current = BHEAP_PEEK(g_open_set); // Look for the lowest f_cost node in the 'open' set
  180. BHEAP_POP2(g_open_set, NODE_MINTOPCMP, swap_ptrcast_pathnode); // Remove it from 'open' set
  181. x = current->x;
  182. y = current->y;
  183. g_cost = current->g_cost;
  184. current->flag = SET_CLOSED; // Add current node to 'closed' set
  185. if (x == dest->x && y == dest->y) {
  186. break;
  187. }
  188. if (y < ys && !map_getcellp(mapdata, x, y+1, cell)) allowed_dirs |= PATH_DIR_NORTH;
  189. if (y > 0 && !map_getcellp(mapdata, x, y-1, cell)) allowed_dirs |= PATH_DIR_SOUTH;
  190. if (x < xs && !map_getcellp(mapdata, x+1, y, cell)) allowed_dirs |= PATH_DIR_EAST;
  191. if (x > 0 && !map_getcellp(mapdata, x-1, y, cell)) allowed_dirs |= PATH_DIR_WEST;
  192. #define chk_dir(d) ((allowed_dirs & (d)) == (d))
  193. // Process neighbors of current node
  194. if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_EAST) && !map_getcellp(mapdata, x+1, y-1, cell))
  195. e += add_path(&g_open_set, x+1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y-1, dest->x, dest->y)); // (x+1, y-1) 5
  196. if (chk_dir(PATH_DIR_EAST))
  197. e += add_path(&g_open_set, x+1, y, g_cost + MOVE_COST, current, heuristic(x+1, y, dest->x, dest->y)); // (x+1, y) 6
  198. if (chk_dir(PATH_DIR_NORTH|PATH_DIR_EAST) && !map_getcellp(mapdata, x+1, y+1, cell))
  199. e += add_path(&g_open_set, x+1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y+1, dest->x, dest->y)); // (x+1, y+1) 7
  200. if (chk_dir(PATH_DIR_NORTH))
  201. e += add_path(&g_open_set, x, y+1, g_cost + MOVE_COST, current, heuristic(x, y+1, dest->x, dest->y)); // (x, y+1) 0
  202. if (chk_dir(PATH_DIR_NORTH|PATH_DIR_WEST) && !map_getcellp(mapdata, x-1, y+1, cell))
  203. e += add_path(&g_open_set, x-1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y+1, dest->x, dest->y)); // (x-1, y+1) 1
  204. if (chk_dir(PATH_DIR_WEST))
  205. e += add_path(&g_open_set, x-1, y, g_cost + MOVE_COST, current, heuristic(x-1, y, dest->x, dest->y)); // (x-1, y) 2
  206. if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_WEST) && !map_getcellp(mapdata, x-1, y-1, cell))
  207. e += add_path(&g_open_set, x-1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y-1, dest->x, dest->y)); // (x-1, y-1) 3
  208. if (chk_dir(PATH_DIR_SOUTH))
  209. e += add_path(&g_open_set, x, y-1, g_cost + MOVE_COST, current, heuristic(x, y-1, dest->x, dest->y)); // (x, y-1) 4
  210. #undef chk_dir
  211. if (e) {
  212. return false;
  213. }
  214. }
  215. for (it = current; it->parent != NULL; it = it->parent, len++);
  216. if (len > sizeof(wpd->path)) {
  217. return false;
  218. }
  219. // Recreate path
  220. wpd->path_len = len;
  221. wpd->path_pos = 0;
  222. for (it = current, j = len-1; j >= 0; it = it->parent, j--) {
  223. dx = it->x - it->parent->x;
  224. dy = it->y - it->parent->y;
  225. wpd->path[j] = walk_choices[-dy + 1][dx + 1];
  226. }
  227. return true;
  228. }
  229. bool fileExists(const std::string& path) {
  230. std::ifstream in;
  231. in.open(path);
  232. if (in.is_open()) {
  233. in.close();
  234. return true;
  235. } else {
  236. return false;
  237. }
  238. }
  239. void write_header(std::ostream &os, std::string name) {
  240. os << name << " = {\n";
  241. }
  242. void write_footer(std::ostream &os) {
  243. os << "}\n";
  244. }
  245. // 5001 = normal, 5002 = airport/airship, 5003 = indoor maps
  246. // 5001 = default
  247. // 5002 = airport/airship
  248. // 5003 = maps that are segmented
  249. // 5005 = 5003 + monsters??? i really have no clue
  250. // maybe it's maps that you must leave to reach parts of it
  251. // for example, ptr_fild04?
  252. int map_type(const struct map_data * m) {
  253. bool segmented = false;
  254. bool has_mob = false;
  255. if (strstr(m->name, "air"))
  256. return 5002;
  257. // this is n^2, yikes!
  258. if (std::find_if(m->navi.warps_outof.begin(), m->navi.warps_outof.end(), [&m](const navi_link* link) {
  259. return std::find_if(m->navi.warps_outof.begin(), m->navi.warps_outof.end(), [&link](const navi_link* link2) {
  260. // find if any two warps in a map cannot be reached
  261. return !navi_path_search(nullptr, &link->pos, &link2->pos, CELL_CHKNOREACH);
  262. }) != m->navi.warps_into.end();
  263. }) != m->navi.warps_into.end())
  264. segmented = true;
  265. if (m->moblist[0] != nullptr) {
  266. has_mob = true;
  267. }
  268. if (segmented && has_mob) {
  269. return 5005;
  270. } else if (segmented) {
  271. return 5003;
  272. }
  273. return 5001;
  274. }
  275. void write_map(std::ostream& os, const struct map_data * m) {
  276. os << "\t{\"" << m->name << "\", \"" << m->name << "\", ";
  277. os << map_type(m) << ", " << m->xs << ", " << m->ys << "},\n";
  278. }
  279. void write_warp(std::ostream& os, const struct navi_link &nl) {
  280. const struct map_data *msrc, *mdest;
  281. msrc = map_getmapdata(nl.pos.m);
  282. mdest = map_getmapdata(nl.warp_dest.m);
  283. if (msrc == nullptr || mdest == nullptr)
  284. return;
  285. os << "\t{";
  286. os << "\"" << msrc->name << "\", ";
  287. os << nl.id << ", "; // gid
  288. // 200 = warp , 201 = npc script, 202 = Kafra Dungeon Warp,
  289. // 203 = Cool Event Dungeon Warp, 204 Kafra/Cool Event/Alberta warp,
  290. // 205 = airport (currently we only support warps)
  291. os << ((nl.npc->subtype == NPCTYPE_WARP) ? 200 : 201) << ", ";
  292. // sprite id, 99999 = warp portal
  293. os << ((nl.npc->vd.class_ == JT_WARPNPC) ? 99999 : (int)nl.npc->vd.class_) << ", ";
  294. if (nl.name.empty())
  295. os << "\"" << msrc->name << "_" << mdest->name << "_" << nl.id << "\", ";
  296. else
  297. os << "\"" << nl.name << "\", ";
  298. os << "\"\", "; // unique name
  299. os << nl.pos.x << ", " << nl.pos.y << ", ";
  300. os << "\"" << mdest->name << "\", ";
  301. os << nl.warp_dest.x << ", " << nl.warp_dest.y;
  302. os << "},\n";
  303. }
  304. void write_npc(std::ostream &os, const struct npc_data *nd) {
  305. if (nd == nullptr) {
  306. ShowError("Unable to find NPC ID for NPC '%s'. Skipping...\n", nd->exname);
  307. return;
  308. }
  309. std::string name = nd->name;
  310. std::string visible_name = name.substr(0, name.find('#'));
  311. const struct map_data *m;
  312. m = map_getmapdata(nd->navi.pos.m);
  313. os << "\t{ ";
  314. os << "\"" << m->name << "\", ";
  315. os << nd->navi.id << ", ";
  316. os << ((nd->subtype == NPCTYPE_SHOP || nd->subtype == NPCTYPE_CASHSHOP) ? 102 : 101) << ", ";
  317. os << nd->class_ << ", ";
  318. os << "\"" << visible_name << "\", ";
  319. os << "\"\", ";
  320. os << nd->navi.pos.x << ", " << nd->navi.pos.y;
  321. os << "},\n";
  322. }
  323. void write_spawn(std::ostream &os, const struct map_data * m, const std::shared_ptr<s_mob_db> mobinfo, int amount, int idx) {
  324. os << "\t{";
  325. os << "\"" << m->name << "\", ";
  326. os << "" << idx << ", ";
  327. os << "" << (mobinfo->mexp ? 301 : 300) << ", ";
  328. #if PACKETVER >= 20140000
  329. os << "" << (amount << 16 | (mobinfo->vd.class_ & 0xFFFF)) << ", ";
  330. #else
  331. os << "\t\t" << mobinfo->vd.class_ << ", ";
  332. #endif
  333. os << "\"" << mobinfo->jname.c_str() << "\", "; //c_str'ed because the strings have been resized to 24
  334. os << "\"" << mobinfo->sprite.c_str() << "\", ";
  335. os << "" << mobinfo->lv << ", ";
  336. os << "" <<
  337. (((mobinfo->status.ele_lv * 20 + mobinfo->status.def_ele) << 16)
  338. | (mobinfo->status.size << 8)
  339. | mobinfo->status.race);
  340. os << "},\n";
  341. }
  342. void write_object_lists() {
  343. auto mob_file = std::ofstream(filePrefix + "./navi_mob_krpri.lub");
  344. auto links_file = std::ofstream(filePrefix + "./navi_link_krpri.lub");
  345. auto npc_file = std::ofstream(filePrefix + "./navi_npc_krpri.lub");
  346. auto map_file = std::ofstream(filePrefix + "./navi_map_krpri.lub");
  347. int warp_count = 0;
  348. int npc_count = 0;
  349. int spawn_count = 0;
  350. write_header(links_file, "Navi_Link");
  351. write_header(npc_file, "Navi_Npc");
  352. write_header(mob_file, "Navi_Mob");
  353. write_header(map_file, "Navi_Map");
  354. for (int mapid = 0; mapid < map_num; mapid++) {
  355. auto m = map_getmapdata(mapid);
  356. // Warps/NPCs
  357. for (int npcidx = 0; npcidx < m->npc_num; npcidx++) {
  358. struct npc_data *nd = m->npc[npcidx];
  359. if (nd == nullptr)
  360. continue;
  361. if (nd->subtype == NPCTYPE_WARP) {
  362. int target = nd->navi.warp_dest.m;
  363. if (target < 0)
  364. continue;
  365. nd->navi.id = 13350 + warp_count++;
  366. write_warp(links_file, nd->navi);
  367. m->navi.warps_outof.push_back(&nd->navi);
  368. map[target].navi.warps_into.push_back(&nd->navi);
  369. } else { // Other NPCs
  370. if (nd->class_ == -1 || nd->class_ == JT_HIDDEN_NPC
  371. || nd->class_ == JT_HIDDEN_WARP_NPC || nd->class_ == JT_GUILD_FLAG
  372. || nd->navi.hidden)
  373. continue;
  374. nd->navi.id = 11984 + npc_count;
  375. write_npc(npc_file, nd);
  376. m->navi.npcs.push_back(nd);
  377. for (auto &link : nd->links) {
  378. int target = link.warp_dest.m;
  379. if (target < 0)
  380. continue;
  381. link.id = 13350 + warp_count++;
  382. write_warp(links_file, link);
  383. m->navi.warps_outof.push_back(&link);
  384. map[target].navi.warps_into.push_back(&link);
  385. }
  386. npc_count++;
  387. }
  388. }
  389. // Mobs
  390. for (int mobidx = 0; mobidx < MAX_MOB_LIST_PER_MAP; mobidx++) {
  391. if (m->moblist[mobidx] == nullptr)
  392. continue;
  393. const auto mobinfo = mob_db.find(m->moblist[mobidx]->id);
  394. if (mobinfo == nullptr)
  395. continue;
  396. write_spawn(mob_file, m, mobinfo, m->moblist[mobidx]->num, 17104 + spawn_count);
  397. spawn_count++;
  398. }
  399. write_map(map_file, m);
  400. }
  401. ShowStatus("Generated %d maps\n", map_num);
  402. ShowStatus("Generated %d npcs\n", npc_count);
  403. ShowStatus("Generated %d mobs\n", spawn_count);
  404. ShowStatus("Generated %d links\n", warp_count);
  405. write_footer(map_file);
  406. write_footer(links_file);
  407. write_footer(npc_file);
  408. write_footer(mob_file);
  409. }
  410. void write_map_header(std::ostream &os, const struct map_data * m) {
  411. os << "\t\"" << m->name << "\", " << m->navi.npcs.size() << ",\n";
  412. os << "\t{\n";
  413. }
  414. /*
  415. Given an NPC in a map (nd)
  416. For every warp into the map (warp)
  417. Find a path from nd to warp
  418. */
  419. void write_npc_distance(std::ostream &os, const struct npc_data * nd, const struct map_data * msrc) {
  420. os << "\t\t{ " << nd->navi.id << ", -- (" << nd->name << " " << msrc->name << ", " << nd->navi.pos.x << ", " << nd->navi.pos.y << ")\n";
  421. for (const auto warp : msrc->navi.warps_into) {
  422. struct navi_walkpath_data wpd = {0};
  423. auto mdest = map_getmapdata(warp->pos.m);
  424. // Find a path from the npc to the warp destination
  425. // The warp is into the map, so this makes sense
  426. if (!navi_path_search(&wpd, &nd->navi.pos, &warp->warp_dest, CELL_CHKNOREACH)) {
  427. continue;
  428. }
  429. os << "\t\t\t{ \"" << mdest->name << "\", " << warp->id << ", " << std::to_string(wpd.path_len) << "}, -- (" << msrc->name << ", " << warp->pos.x << ", " << warp->pos.y << ")\n";
  430. }
  431. os << "\t\t\t{\"\", 0, 0}\n";
  432. os << "\t\t},\n";
  433. }
  434. void write_npc_distances() {
  435. auto dist_npc_file = std::ofstream(filePrefix + "./navi_npcdistance_krpri.lub");
  436. write_header(dist_npc_file, "Navi_NpcDistance");
  437. for (int mapid = 0; mapid < map_num; mapid++) {
  438. auto m = map_getmapdata(mapid);
  439. if (m->navi.npcs.size() == 0) {
  440. // ShowStatus("Skipped %s NPC distance table, no NPCs in map (%d/%d)\n", map[m].name, m, map_num);
  441. continue;
  442. }
  443. if (m->navi.warps_into.size() == 0) {
  444. // ShowStatus("Skipped %s NPC distance table, no warps into map (%d/%d)\n", map[m].name, m, map_num);
  445. continue;
  446. }
  447. write_map_header(dist_npc_file, m);
  448. for (auto nd : m->navi.npcs) {
  449. write_npc_distance(dist_npc_file, nd, m);
  450. }
  451. dist_npc_file << "\t},\n";
  452. }
  453. ShowStatus("Generated NPC Distances for %d maps\n", map_num);
  454. write_footer(dist_npc_file);
  455. }
  456. void write_mapdist_header(std::ostream &os, const struct map_data * m) {
  457. os << "\t\"" << m->name << "\", " << m->navi.warps_outof.size() << ",\n";
  458. os << "\t{\n";
  459. }
  460. /*
  461. Given a warp out of a map (warp1, m)
  462. for every warp out of the map (warp2)
  463. find a path from the warp1->src to warp2->src
  464. Add this as a "P" Warp
  465. for every warp out of warp1->dest.m (warp3)
  466. find a path from warp1->dest to warp3->src
  467. Add this as an "E" Warp
  468. */
  469. void write_map_distance(std::ostream &os, const struct navi_link * warp1, const struct map_data * m) {
  470. os << "\t\t{ " << warp1->id << ", -- (" << " " << m->name << ", " << warp1->pos.x << ", " << warp1->pos.y << ")\n";
  471. // for (const auto warp2 : m->navi.warps_outof) {
  472. // struct navi_walkpath_data wpd = {0};
  473. // if (warp1 == warp2)
  474. // continue;
  475. // if (!navi_path_search(&wpd, &warp1->pos, &warp2->pos, CELL_CHKNOREACH))
  476. // continue;
  477. // os << "\t\t\t{ \"P\", " << warp2->id << ", " << std::to_string(wpd.path_len) << "}, -- ReachableFromSrc warp (" << m->name << ", " << warp2->pos.x << ", " << warp2->pos.y << ")\n";
  478. // }
  479. for (const auto warp3 : map_getmapdata(warp1->warp_dest.m)->navi.warps_outof) {
  480. struct navi_walkpath_data wpd = {0};
  481. if (!navi_path_search(&wpd, &warp1->warp_dest, &warp3->pos, CELL_CHKNOREACH))
  482. continue;
  483. os << "\t\t\t{ \"E\", " << warp3->id << ", " << std::to_string(wpd.path_len) << "}, -- ReachableFromDst warp (" << map_getmapdata(warp3->pos.m)->name << ", " << warp3->pos.x << ", " << warp3->pos.y << ")\n";
  484. }
  485. os << "\t\t\t{\"NULL\", 0, 0}\n";
  486. os << "\t\t},\n";
  487. }
  488. void write_map_distances() {
  489. auto dist_map_file = std::ofstream(filePrefix + "./navi_linkdistance_krpri.lub");
  490. write_header(dist_map_file, "Navi_Distance");
  491. for (int mapid = 0; mapid < map_num; mapid++) {
  492. const struct map_data * m = map_getmapdata(mapid);
  493. write_mapdist_header(dist_map_file, m);
  494. for (auto nd : m->navi.warps_outof) {
  495. write_map_distance(dist_map_file, nd, m);
  496. }
  497. dist_map_file << "\t},\n";
  498. }
  499. ShowStatus("Generated Map Distances for %d maps\n", map_num);
  500. write_footer(dist_map_file);
  501. }
  502. void navi_create_lists() {
  503. BHEAP_INIT(g_open_set);
  504. auto starttime = std::chrono::system_clock::now();
  505. if (!fileExists(filePrefix)) {
  506. ShowError("File directory %s does not exist.\n", filePrefix.c_str());
  507. ShowInfo("Create the directory and rerun map-server");
  508. exit(1);
  509. }
  510. npc_event_runall(script_config.navi_generate_name);
  511. write_object_lists();
  512. auto currenttime = std::chrono::system_clock::now();
  513. ShowInfo("Object lists took %ums\n", std::chrono::duration_cast<std::chrono::milliseconds>(currenttime - starttime));
  514. starttime = std::chrono::system_clock::now();
  515. write_npc_distances();
  516. currenttime = std::chrono::system_clock::now();
  517. ShowInfo("NPC Distances took %ums\n", std::chrono::duration_cast<std::chrono::milliseconds>(currenttime - starttime));
  518. starttime = std::chrono::system_clock::now();
  519. write_map_distances();
  520. currenttime = std::chrono::system_clock::now();
  521. ShowInfo("Link Distances took %ums\n", std::chrono::duration_cast<std::chrono::milliseconds>(currenttime - starttime));
  522. BHEAP_CLEAR(g_open_set);
  523. }
  524. #endif