|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00042 #include <global.h> 00043 00045 #define HEURISTIC_ERROR -666. 00046 00047 #ifdef DEBUG_PATHFINDING 00048 static int searched_nodes = 0; 00049 #endif 00050 00052 #define PATHFINDER_QUEUE_SIZE 100 00053 00054 static int pathfinder_queue_first = 0; 00056 static int pathfinder_queue_last = 0; 00058 static struct 00059 { 00061 object *waypoint; 00062 00064 tag_t wp_count; 00065 } pathfinder_queue[PATHFINDER_QUEUE_SIZE]; 00066 00068 #define PATHFINDER_NODEBUF 300 00069 00070 static int pathfinder_nodebuf_next = 0; 00072 static path_node pathfinder_nodebuf[PATHFINDER_NODEBUF]; 00073 00078 static int pathfinder_queue_enqueue(object *waypoint) 00079 { 00080 /* Queue full? */ 00081 if (pathfinder_queue_last == pathfinder_queue_first - 1 || (pathfinder_queue_first == 0 && pathfinder_queue_last == PATHFINDER_QUEUE_SIZE - 1)) 00082 { 00083 return 0; 00084 } 00085 00086 pathfinder_queue[pathfinder_queue_last].waypoint = waypoint; 00087 pathfinder_queue[pathfinder_queue_last].wp_count = waypoint->count; 00088 00089 if (++pathfinder_queue_last >= PATHFINDER_QUEUE_SIZE) 00090 { 00091 pathfinder_queue_last = 0; 00092 } 00093 00094 return 1; 00095 } 00096 00101 static object *pathfinder_queue_dequeue(int *count) 00102 { 00103 object *waypoint; 00104 00105 /* Queue empty? */ 00106 if (pathfinder_queue_last == pathfinder_queue_first) 00107 { 00108 return NULL; 00109 } 00110 00111 waypoint = pathfinder_queue[pathfinder_queue_first].waypoint; 00112 *count = pathfinder_queue[pathfinder_queue_first].wp_count; 00113 00114 if (++pathfinder_queue_first >= PATHFINDER_QUEUE_SIZE) 00115 { 00116 pathfinder_queue_first = 0; 00117 } 00118 00119 return waypoint; 00120 } 00121 00125 void request_new_path(object *waypoint) 00126 { 00127 if (!waypoint || QUERY_FLAG(waypoint, FLAG_WP_PATH_REQUESTED)) 00128 { 00129 return; 00130 } 00131 00132 #ifdef DEBUG_PATHFINDING 00133 LOG(llevDebug, "request_new_path(): enqueing path request for >%s< -> >%s<\n", waypoint->env->name, waypoint->name); 00134 #endif 00135 00136 if (pathfinder_queue_enqueue(waypoint)) 00137 { 00138 SET_FLAG(waypoint, FLAG_WP_PATH_REQUESTED); 00139 waypoint->owner = waypoint->env; 00140 waypoint->ownercount = waypoint->env->count; 00141 } 00142 } 00143 00146 object *get_next_requested_path() 00147 { 00148 object *waypoint; 00149 tag_t count; 00150 00151 do 00152 { 00153 waypoint = pathfinder_queue_dequeue((void *) &count); 00154 00155 if (waypoint == NULL) 00156 { 00157 return NULL; 00158 } 00159 00160 /* Verify the waypoint and its monster */ 00161 if (!OBJECT_VALID(waypoint, count) || !OBJECT_VALID(waypoint->owner, waypoint->ownercount) || !(QUERY_FLAG(waypoint, FLAG_CURSED) || QUERY_FLAG(waypoint, FLAG_DAMNED)) || (QUERY_FLAG(waypoint, FLAG_DAMNED) && !OBJECT_VALID(waypoint->enemy, waypoint->enemy_count))) 00162 { 00163 waypoint = NULL; 00164 } 00165 } 00166 while (waypoint == NULL); 00167 00168 #ifdef DEBUG_PATHFINDING 00169 LOG(llevDebug, "get_next_requested_path(): dequeued '%s' -> '%s'\n", waypoint->owner->name, waypoint->name); 00170 #endif 00171 00172 CLEAR_FLAG(waypoint, FLAG_WP_PATH_REQUESTED); 00173 return waypoint; 00174 } 00175 00184 static path_node *make_node(mapstruct *map, sint16 x, sint16 y, uint16 cost, path_node *parent) 00185 { 00186 path_node *node; 00187 00188 /* Out of memory? */ 00189 if (pathfinder_nodebuf_next == PATHFINDER_NODEBUF) 00190 { 00191 #ifdef DEBUG_PATHFINDING 00192 LOG(llevDebug, "make_node(): Out of static buffer memory (this is not a problem)\n"); 00193 #endif 00194 return NULL; 00195 } 00196 00197 node = &pathfinder_nodebuf[pathfinder_nodebuf_next++]; 00198 00199 node->next = NULL; 00200 node->prev = NULL; 00201 node->parent = parent; 00202 node->map = map; 00203 node->x = x; 00204 node->y = y; 00205 node->cost = cost; 00206 node->heuristic = 0.0; 00207 00208 return node; 00209 } 00210 00215 static void remove_node(path_node *node, path_node **list) 00216 { 00217 if (node->prev) 00218 { 00219 node->prev->next = node->next; 00220 } 00221 else 00222 { 00223 *list = node->next; 00224 } 00225 00226 if (node->next) 00227 { 00228 node->next->prev = node->prev; 00229 } 00230 00231 node->next = node->prev = NULL; 00232 } 00233 00238 static void insert_node(path_node *node, path_node **list) 00239 { 00240 if (*list) 00241 { 00242 (*list)->prev = node; 00243 } 00244 00245 node->next = *list; 00246 node->prev = NULL; 00247 00248 *list = node; 00249 } 00250 00256 static void insert_priority_node(path_node *node, path_node **list) 00257 { 00258 path_node *tmp, *last, *insert_before = NULL; 00259 00260 /* Find node to insert before */ 00261 for (tmp = *list; tmp; tmp = tmp->next) 00262 { 00263 last = tmp; 00264 00265 if (node->heuristic <= tmp->heuristic) 00266 { 00267 insert_before = tmp; 00268 break; 00269 } 00270 } 00271 00272 /* Insert first */ 00273 if (insert_before == *list) 00274 { 00275 insert_node(node, list); 00276 } 00277 /* Insert last */ 00278 else if (!insert_before) 00279 { 00280 node->next = NULL; 00281 node->prev = last; 00282 last->next = node; 00283 } 00284 /* Insert in middle */ 00285 else 00286 { 00287 node->next = insert_before; 00288 node->prev = insert_before->prev; 00289 insert_before->prev = node; 00290 00291 if (node->prev) 00292 { 00293 node->prev->next = node; 00294 } 00295 } 00296 00297 /* Print out the values of the prioqueue -> should be ordered */ 00298 #if 0 00299 LOG(llevInfo, "post: "); 00300 00301 for (tmp = *list; tmp; tmp = tmp->next) 00302 { 00303 LOG(llevInfo, "%.3f ", tmp->heuristic); 00304 } 00305 00306 LOG(llevInfo, "\n"); 00307 #endif 00308 } 00309 00327 shstr *encode_path(path_node *path) 00328 { 00329 char buf[HUGE_BUF]; 00330 char *bufptr = buf; 00331 mapstruct *last_map = NULL; 00332 path_node *tmp; 00333 00334 for (tmp = path; tmp; tmp = tmp->next) 00335 { 00336 if (tmp->map != last_map) 00337 { 00338 bufptr += sprintf(bufptr, "%s%s", last_map ? "\n" : "", tmp->map->path); 00339 last_map = tmp->map; 00340 } 00341 00342 bufptr += sprintf(bufptr, " %d,%d", tmp->x, tmp->y); 00343 } 00344 00345 return add_string(buf); 00346 } 00347 00368 int get_path_next(shstr *buf, sint16 *off, shstr **mappath, mapstruct **map, int *x, int *y) 00369 { 00370 const char *coord_start = buf + *off, *coord_end, *map_def = coord_start; 00371 00372 if (buf == NULL || *map == NULL) 00373 { 00374 LOG(llevBug, "get_path_next(): Illegal parameters.\n"); 00375 return 0; 00376 } 00377 00378 if (*mappath == NULL || **mappath == '\0') 00379 { 00380 /* Scan backwards from requested offset to previous linebreak or start of string */ 00381 for (map_def = coord_start; map_def > buf && *(map_def - 1) != '\n'; map_def--) 00382 { 00383 } 00384 } 00385 00386 /* Extract map path if any at the current position (this part is only used when we go between 00387 * map tiles, or when we extract the first step) */ 00388 if (!isdigit(*map_def)) 00389 { 00390 /* Temporary buffer for map path extraction */ 00391 char map_name[HUGE_BUF]; 00392 /* Find the end of the map path */ 00393 const char *mapend = strchr(map_def, ' '); 00394 00395 if (mapend == NULL) 00396 { 00397 LOG(llevBug, "get_path_next(): No delimeter after map name in path description '%s' off %d\n", buf, *off); 00398 return 0; 00399 } 00400 00401 strncpy(map_name, map_def, mapend - map_def); 00402 map_name[mapend - map_def] = '\0'; 00403 00404 /* Store the new map path in the given shared string */ 00405 FREE_AND_COPY_HASH(*mappath, map_name); 00406 00407 /* Adjust coordinate pointer to point after map path */ 00408 if (!isdigit(*coord_start)) 00409 { 00410 coord_start = mapend + 1; 00411 } 00412 } 00413 00414 /* Select the map we are aiming at */ 00415 /* TODO: Handle unique maps? */ 00416 if (*mappath) 00417 { 00418 /* We assume map name is already normalized */ 00419 if (!*map || (*map)->path != *mappath) 00420 { 00421 *map = ready_map_name(*mappath, MAP_NAME_SHARED); 00422 } 00423 } 00424 00425 if (*map == NULL) 00426 { 00427 LOG(llevBug, "BIG: get_path_next(): Couldn't load map from description '%s' off %d\n", buf, *off); 00428 return 0; 00429 } 00430 00431 /* Get the requested coordinate pair */ 00432 coord_end = coord_start + strcspn(coord_start, " \n"); 00433 00434 if (coord_end == coord_start || sscanf(coord_start, "%d,%d", x, y) != 2) 00435 { 00436 LOG(llevBug, "get_path_next(): Illegal coordinate pair in '%s' off %d\n", buf, *off); 00437 return 0; 00438 } 00439 00440 /* Adjust coordinates to be on the safe side */ 00441 *map = get_map_from_coord(*map, x, y); 00442 00443 if (*map == NULL) 00444 { 00445 LOG(llevBug, "get_path_next(): Location (%d, %d) is out of map\n", *x, *y); 00446 return 0; 00447 } 00448 00449 /* Adjust the offset */ 00450 *off = coord_end - buf + (*coord_end ? 1 : 0); 00451 00452 return 1; 00453 } 00454 00463 path_node *compress_path(path_node *path) 00464 { 00465 path_node *tmp, *next; 00466 int last_dir; 00467 rv_vector v; 00468 00469 #ifdef DEBUG_PATHFINDING 00470 int removed_nodes = 0, total_nodes = 2; 00471 #endif 00472 00473 /* Rules: 00474 * - always leave first and last path nodes 00475 * - if the movement direction of node n to n+1 is the same 00476 * as for n-1 to n, then remove node n. */ 00477 00478 /* Guarantee at least length 3 */ 00479 if (path == NULL || path->next == NULL) 00480 { 00481 return path; 00482 } 00483 00484 next = path->next; 00485 00486 get_rangevector_from_mapcoords(path->map, path->x, path->y, next->map, next->x, next->y, &v, RV_MANHATTAN_DISTANCE); 00487 last_dir = v.direction; 00488 00489 for (tmp = next; tmp && tmp->next; tmp = next) 00490 { 00491 next = tmp->next; 00492 00493 #ifdef DEBUG_PATHFINDING 00494 total_nodes++; 00495 #endif 00496 get_rangevector_from_mapcoords(tmp->map, tmp->x, tmp->y, next->map, next->x, next->y, &v, RV_MANHATTAN_DISTANCE); 00497 00498 if (last_dir == v.direction) 00499 { 00500 remove_node(tmp, &path); 00501 #ifdef DEBUG_PATHFINDING 00502 removed_nodes++; 00503 #endif 00504 } 00505 else 00506 { 00507 last_dir = v.direction; 00508 } 00509 } 00510 00511 #ifdef DEBUG_PATHFINDING 00512 LOG(llevDebug, "compress_path(): removed %d nodes of %d (%.0f%%)\n", removed_nodes, total_nodes, (float)removed_nodes * 100.0 / (float)total_nodes); 00513 #endif 00514 00515 return path; 00516 } 00517 00531 static float distance_heuristic(path_node *start, path_node *current, path_node *goal) 00532 { 00533 rv_vector v1, v2; 00534 float h; 00535 00536 /* Diagonal distance (not manhattan distance or euclidean distance!) */ 00537 if (goal->map == current->map) 00538 { 00539 /* Avoid a function call in simple case */ 00540 v1.distance_x = current->x - goal->x; 00541 v1.distance_y = current->y - goal->y; 00542 v1.distance = MAX(abs(v1.distance_x), abs(v1.distance_y)); 00543 } 00544 else 00545 { 00546 if (!get_rangevector_from_mapcoords(current->map, current->x, current->y, goal->map, goal->x, goal->y, &v1, RV_RECURSIVE_SEARCH | RV_DIAGONAL_DISTANCE)) 00547 { 00548 return HEURISTIC_ERROR; 00549 } 00550 } 00551 00552 h = (float) v1.distance; 00553 00554 /* Add straight-line preference by calculating cross product */ 00555 /* (gives better performance on open areas _and_ nicer-looking paths) */ 00556 if (goal->map == start->map) 00557 { 00558 /* Avoid a function call in simple case */ 00559 v2.distance_x = start->x - goal->x; 00560 v2.distance_y = start->y - goal->y; 00561 } 00562 else 00563 { 00564 if (!get_rangevector_from_mapcoords(start->map, start->x, start->y, goal->map, goal->x, goal->y, &v2, RV_RECURSIVE_SEARCH | RV_NO_DISTANCE)) 00565 { 00566 return HEURISTIC_ERROR; 00567 } 00568 } 00569 00570 h += abs(v1.distance_x * v2.distance_y - v2.distance_x * v1.distance_y) * 0.001f; 00571 00572 return h; 00573 } 00574 00585 static int find_neighbours(path_node *node, path_node **open_list, path_node *start, path_node *goal, object *op, uint32 id) 00586 { 00587 int i, x2, y2; 00588 mapstruct *map; 00589 int block; 00590 00591 for (i = 1; i < 9; i++) 00592 { 00593 x2 = node->x + freearr_x[i]; 00594 y2 = node->y + freearr_y[i]; 00595 00596 map = get_map_from_coord(node->map, &x2, &y2); 00597 00598 if (map && !QUERY_MAP_TILE_VISITED(map, x2, y2, id)) 00599 { 00600 SET_MAP_TILE_VISITED(map, x2, y2, id); 00601 00602 #ifdef DEBUG_PATHFINDING 00603 searched_nodes++; 00604 #endif 00605 00606 if (QUERY_FLAG(HEAD(op), FLAG_WIZPASS)) 00607 { 00608 block = 0; 00609 } 00610 /* Multi-arch or not? (blocked_link_2 works for normal archs too, but is more expensive) */ 00611 else if (op->head || op->more) 00612 { 00613 block = blocked_link_2(op, map, x2, y2); 00614 } 00615 else 00616 { 00617 block = blocked(op, map, x2, y2, op->terrain_flag); 00618 /* Check for possible door opening */ 00619 /* TODO: increase path cost if we have to open doors? */ 00620 if (block == P_DOOR_CLOSED && open_door(op, map, x2, y2, 0)) 00621 { 00622 block = 0; 00623 } 00624 } 00625 00626 if (!block) 00627 { 00628 path_node *new_node; 00629 00630 if ((new_node = make_node(map, (sint16)x2, (sint16)y2, (uint16)(node->cost + 1), node))) 00631 { 00632 new_node->heuristic = distance_heuristic(start, new_node, goal); 00633 00634 if (new_node->heuristic == HEURISTIC_ERROR) 00635 { 00636 return 0; 00637 } 00638 00639 insert_priority_node(new_node, open_list); 00640 } 00641 else 00642 { 00643 return 0; 00644 } 00645 } 00646 00647 /* TODO: might need to reopen neighbor nodes if their cost can be lowered from the new node. 00648 * (This requires us to store pointers to the tiles instead of bitmap.) 00649 * (Probably only required if we add different path costs to different terrains, 00650 * or support for doors/teleporters) */ 00651 } 00652 } 00653 00654 return 1; 00655 } 00656 00667 path_node *find_path(object *op, mapstruct *map1, int x1, int y1, mapstruct *map2, int x2, int y2) 00668 { 00669 /* Closed nodes have been examined. Open are to be examined */ 00670 path_node *open_list, *closed_list; 00671 path_node *found_path = NULL; 00672 path_node start, goal; 00673 static uint32 traversal_id = 0; 00674 00675 /* Avoid overflow of traversal_id */ 00676 if (traversal_id == UINT32_MAX) 00677 { 00678 mapstruct *m; 00679 00680 for (m = first_map; m; m = m->next) 00681 { 00682 m->pathfinding_id = 0; 00683 } 00684 00685 traversal_id = 0; 00686 LOG(llevDebug, "find_path(): Resetting traversal id\n"); 00687 } 00688 00689 traversal_id++; 00690 00691 pathfinder_nodebuf_next = 0; 00692 00693 start.x = x1; 00694 start.y = y1; 00695 start.map = map1; 00696 00697 goal.x = x2; 00698 goal.y = y2; 00699 goal.map = map2; 00700 00701 /* The initial tile */ 00702 open_list = make_node(map1, (sint16)x1, (sint16)y1, 0, NULL); 00703 open_list->heuristic = distance_heuristic(&start, open_list, &goal); 00704 closed_list = NULL; 00705 SET_MAP_TILE_VISITED(map1, x1, y1, traversal_id); 00706 00707 while (open_list) 00708 { 00709 /* Pick best node from open_list */ 00710 path_node *tmp = open_list; 00711 00712 /* Move node from open list to closed list */ 00713 remove_node(tmp, &open_list); 00714 insert_node(tmp, &closed_list); 00715 00716 /* Reached the goal? (Or at least the tile next to it?) */ 00717 if (tmp->heuristic <= 1.2) 00718 { 00719 path_node *tmp2; 00720 00721 /* Move all nodes used in the path to the path list */ 00722 for (tmp2 = tmp; tmp2; tmp2 = tmp2->parent) 00723 { 00724 remove_node(tmp2, &closed_list); 00725 insert_node(tmp2, &found_path); 00726 } 00727 00728 break; 00729 } 00730 else 00731 { 00732 if (!find_neighbours(tmp, &open_list, &start, &goal, op, traversal_id)) 00733 { 00734 break; 00735 } 00736 } 00737 } 00738 00739 /* If no path was found, pick the path to the node closest to the target */ 00740 if (found_path == NULL) 00741 { 00742 path_node *best_node = NULL; 00743 path_node *tmp; 00744 00745 /* Move best from the open list to the closed list */ 00746 if (open_list) 00747 { 00748 tmp = open_list; 00749 remove_node(tmp, &open_list); 00750 insert_node(tmp, &closed_list); 00751 } 00752 00753 /* Scan through the closed list for a closer tile */ 00754 for (tmp = closed_list; tmp; tmp = tmp->next) 00755 { 00756 if (best_node == NULL || tmp->heuristic < best_node->heuristic || (tmp->heuristic == best_node->heuristic && tmp->cost < best_node->cost)) 00757 { 00758 best_node = tmp; 00759 } 00760 } 00761 00762 /* Create a path if we found anything */ 00763 if (best_node) 00764 { 00765 for (tmp = best_node; tmp; tmp = tmp->parent) 00766 { 00767 remove_node(tmp, &closed_list); 00768 insert_node(tmp, &found_path); 00769 } 00770 } 00771 } 00772 00773 #ifdef DEBUG_PATHFINDING 00774 LOG(llevDebug, "find_path(): Explored %d tiles, stored %d.\n", searched_nodes, pathfinder_nodebuf_next); 00775 searched_nodes = 0; 00776 00777 /* This writes out the explored tiles on the source map. Useful for heuristic tweaking */ 00778 # if 0 00779 { 00780 int y, x; 00781 00782 for (y = 0; y < map1->height; y++) 00783 { 00784 for (x = 0; x < map1->height; x++) 00785 { 00786 LOG(llevInfo, "%c", (map1->bitmap[y] & (1U << x)) ? 'X' : '-'); 00787 } 00788 00789 LOG(llevInfo, "\n"); 00790 } 00791 } 00792 # endif 00793 #endif 00794 00795 return found_path; 00796 }
1.7.4