/* PATHFINDING.C Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. and the "Aleph One" developers. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This license is contained in the file "COPYING", which is included with this source code; it is available online at http://www.gnu.org/licenses/gpl.html Monday, January 18, 1993 5:00:40 PM Friday, July 22, 1994 3:03:18 PM as a major change, we now return a path in all cases except the one where there are no free path slots or a monster cannot move at all. monsters unable to reach their destinations or monsters whose destinations are too far away will get effectively random and clipped paths, respectively. this was done to prevent out-of-control regeneration of paths. Friday, November 18, 1994 8:09:38 PM (Jason) if callers who ask for impossible paths need random paths it would be really nice just to give them a path to some node we just generated, rather than rebuild the entire node list and create a new path. Wednesday, June 14, 1995 8:35:28 AM (Jason) user data can now be passed into flood_map(). Feb 10, 2000 (Loren Petrich): Added dynamic-limits setting of MAXIMUM_PATHS */ #include #include #include #include "cseries.h" #include "map.h" #include "flood_map.h" #include "dynamic_limits.h" #ifdef env68k #pragma segment marathon #endif #ifdef DEBUG //#define VALIDATE_PATH_SPACE //#define VERIFY_PATH_SYNC #endif /* we should cut corners instead of blindly following map geometry (i.e., separate pathfinding as much as possible from the underlying polygon structure of the map), especially the final leg. //calculate_midpoint_of_shared_line should have some randomness (bad idea?) //it ocurrs to me that connecting midpoints of shared lines wonÕt always work (monsters will run through walls) //shadow polygons screw us up (a specific case of the general "weird polygon geometry" problem) //random paths arenÕt really random, and (even worse) tend to be symmetric */ /* ---------- constants */ // LP change: made this settable from the resource fork #define MAXIMUM_PATHS (get_dynamic_limit(_dynamic_limit_paths)) #define MAXIMUM_POINTS_PER_PATH 63 #define PATH_VALIDATION_AREA_SIZE 64*1024 /* ---------- structures */ struct path_definition /* 256 bytes */ { /* NONE is an empty path */ short current_step; short step_count; world_point2d points[MAXIMUM_POINTS_PER_PATH]; }; /* ---------- globals */ static struct path_definition *paths = NULL; #ifdef VERIFY_PATH_SYNC static byte *path_validation_area = NULL; static long path_validation_area_index; static short path_run_count; #endif /* ---------- private prototypes */ static void calculate_midpoint_of_shared_line(short polygon1, short polygon2, world_distance minimum_separation, world_point2d *midpoint); /* ---------- code */ void allocate_pathfinding_memory( void) { // Made reentrant because this is called every time MAXIMUM_PATHS is changed if (paths) delete []paths; paths= new path_definition[MAXIMUM_PATHS]; assert(paths); #ifdef VERIFY_PATH_SYNC if (path_validation_area) delete []path_validation_area; path_validation_area= new byte[PATH_VALIDATION_AREA_SIZE]; assert(path_validation_area); path_run_count= 0; #endif } void reset_paths( void) { short path_index; for (path_index=0;path_indexx, source_point->y, // destination_polygon_index, destination_point ? destination_point->x : 0, // destination_point ? destination_point->y : 0); /* used for calculating the source polygon, ages ago */ (void) (source_point); #ifdef VALIDATE_PATH_SPACE { short i; for (i=0;i=0&&paths[i].step_count<=MAXIMUM_POINTS_PER_PATH&&paths[i].current_step>=0&&paths[i].current_step<=paths[i].step_count, csprintf(temporary, "path #%d (%p) is fucked.", i, paths+i)); } } } #endif /* find a free path */ for (path_index=0;path_index0) /* if we have valid steps, extract the path */ { struct path_definition *path= paths+path_index; short last_polygon_index; //#ifdef DEBUG obj_set(*path, 0x80); //#endif path->step_count= step_count>MAXIMUM_POINTS_PER_PATH ? MAXIMUM_POINTS_PER_PATH : step_count; assert(path->step_count!=NONE); /* this would be bad */ path->current_step= 0; /* if we reached our destination (and itÕs not out-of-range), add it */ if (reached_destination && --step_countpoints[step_count]= *destination_point; /* add all the points up to but not including the source (if we have room) */ last_polygon_index= reverse_flood_map(); while ((polygon_index= reverse_flood_map())!=NONE) { if (--step_countpoints+step_count); // if (polygon_index!=source_polygon_index&&--step_countpoints+step_count); last_polygon_index= polygon_index; } assert(!step_count); /* we should be out of points */ // dprintf("path from %d to %d (%d steps);dm #%d #%d;g", source_polygon_index, destination_polygon_index, paths[path_index].step_count, // path_points, paths[path_index].step_count*sizeof(world_point2d)); #ifdef VERIFY_PATH_SYNC if (path_run_count==1) { objlist_copy(path_validation_area+path_validation_area_index, path->points, path->step_count); path_validation_area_index+= sizeof(world_point2d)*path->step_count; assert(path_validation_area_indexpoints, sizeof(world_point2d)*path->step_count)) { dprintf("path #%d at %p didnÕt match point list at %p", path_index, path, path_validation_area+path_validation_area_index); } path_validation_area_index+= sizeof(world_point2d*)*path->step_count; } #endif } else { path_index= NONE; } } #ifdef VALIDATE_PATH_SPACE { short i; for (i=0;i=0&&paths[i].step_count<=MAXIMUM_POINTS_PER_PATH&&paths[i].current_step>=0&&paths[i].current_step<=paths[i].step_count, csprintf(temporary, "path #%d (%p) is fucked.", i, paths+i)); } } } #endif return path_index; } bool move_along_path( short path_index, world_point2d *p) { struct path_definition *path; bool end_of_path= false; assert(path_index>=0&&path_indexstep_count!=NONE); vassert(path->current_step>=0&&path->current_step<=path->step_count, csprintf(temporary, "invalid current path step: #%d/#%d", path->current_step, path->step_count)); if (path->current_step==path->step_count) { path->step_count= NONE; end_of_path= true; } else { *p= path->points[path->current_step++]; // vwarn(valid_point2d(p), csprintf(temporary, "step #%d (%d,%d) of path %p looks bad;g;", path->current_step-1, p->x, p->y, path)); } return end_of_path; } void delete_path( short path_index) { assert(path_index>=0&&path_index=0&&paths[path_index].current_step<=paths[path_index].step_count, csprintf(temporary, "invalid current path step: #%d/#%d", paths[path_index].current_step, paths[path_index].step_count)); paths[path_index].step_count= NONE; } /* ---------- private code */ static void calculate_midpoint_of_shared_line( short polygon1, short polygon2, world_distance minimum_separation, world_point2d *midpoint) { short shared_line_index; world_distance range, origin; struct line_data *shared_line; struct endpoint_data *endpoint0, *endpoint1; shared_line_index= find_shared_line(polygon1, polygon2); assert(shared_line_index!=NONE); shared_line= get_line_data(shared_line_index); endpoint0= get_endpoint_data(shared_line->endpoint_indexes[0]); endpoint1= get_endpoint_data(shared_line->endpoint_indexes[1]); origin= 0; range= shared_line->length; if (ENDPOINT_IS_ELEVATION(endpoint0)) origin+= minimum_separation, range-= minimum_separation; if (ENDPOINT_IS_ELEVATION(endpoint1)) range-= minimum_separation; if (range<=0) { /* uhh... this line is really too small for us to pass through */ midpoint->x= endpoint0->vertex.x + (endpoint1->vertex.x-endpoint0->vertex.x)/2; midpoint->y= endpoint0->vertex.y + (endpoint1->vertex.y-endpoint0->vertex.y)/2; } else { world_distance dx= endpoint1->vertex.x-endpoint0->vertex.x; world_distance dy= endpoint1->vertex.y-endpoint0->vertex.y; world_distance offset= origin + ((global_random()*range)>>16); midpoint->x= endpoint0->vertex.x + (offset*dx)/shared_line->length; midpoint->y= endpoint0->vertex.y + (offset*dy)/shared_line->length; } } /* for debug purposes only (called from OVERHEAD_MAP.C) */ // LP: making these available for those wanting to check out the monster AI world_point2d *path_peek( short path_index, short *step_count) { struct path_definition *path; world_point2d *points= 0; path= paths+path_index; if (path->step_count!=NONE) { *step_count= path->step_count; points= path->points; } return points; } // LP addition: the total number of paths short GetNumberOfPaths() {return MAXIMUM_PATHS;}