Atrinik Server 2.5
server/pathfinder.c
Go to the documentation of this file.
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 }