diff --git a/Engine/ac/route_finder_impl_legacy.cpp b/Engine/ac/route_finder_impl_legacy.cpp index f4aed43742..81dff0aacf 100644 --- a/Engine/ac/route_finder_impl_legacy.cpp +++ b/Engine/ac/route_finder_impl_legacy.cpp @@ -49,6 +49,19 @@ static int waspossible = 1; static int suggestx; static int suggesty; +// Configuration for the pathfinder +struct PathfinderConfig +{ + const int MaxGranularity = 3; + + // Short sweep is performed in certain radius around requested destination, + // when searching for a nearest walkable area in the vicinity + const int ShortSweepRadius = 50; + int ShortSweepGranularity = 3; // variable, depending on loaded game version + // Full sweep is performed over a whole walkable area + const int FullSweepGranularity = 5; +}; + void init_pathfinder() { pathbackx = (int *)malloc(sizeof(int) * MAXPATHBACK); @@ -139,9 +152,8 @@ int find_nearest_walkable_area(Bitmap *tempw, int fromX, int fromY, int toX, int return 0; } -#define MAX_GRANULARITY 3 static int walk_area_granularity[MAX_WALK_AREAS]; -static int is_route_possible(int fromx, int fromy, int tox, int toy, Bitmap *wss) +static int is_route_possible(int fromx, int fromy, int tox, int toy, Bitmap *wss, const PathfinderConfig &pfc) { wallscreen = wss; suggestx = -1; @@ -205,7 +217,7 @@ static int is_route_possible(int fromx, int fromy, int tox, int toy, Bitmap *wss // find the average "width" of a path in this walkable area for (dd = 1; dd < MAX_WALK_AREAS; dd++) { if (walk_area_times[dd] == 0) { - walk_area_granularity[dd] = MAX_GRANULARITY; + walk_area_granularity[dd] = pfc.MaxGranularity; continue; } @@ -215,26 +227,26 @@ static int is_route_possible(int fromx, int fromy, int tox, int toy, Bitmap *wss else if (walk_area_granularity[dd] <= 15) walk_area_granularity[dd] = 3; else - walk_area_granularity[dd] = MAX_GRANULARITY; + walk_area_granularity[dd] = pfc.MaxGranularity; #ifdef DEBUG_PATHFINDER AGS::Common::Debug::Printf("area %d: Gran %d", dd, walk_area_granularity[dd]); #endif } - walk_area_granularity[0] = MAX_GRANULARITY; + walk_area_granularity[0] = pfc.MaxGranularity; tempw->FloodFill(fromx, fromy, 232); if (tempw->GetPixel(tox, toy) != 232) { // Destination pixel is not walkable - // Try the 100x100 square around the target first at 3-pixel granularity - int tryFirstX = tox - 50, tryToX = tox + 50; - int tryFirstY = toy - 50, tryToY = toy + 50; + // Try the N x N square around the target first at X-pixel granularity + int tryFirstX = tox - pfc.ShortSweepRadius, tryToX = tox + pfc.ShortSweepRadius; + int tryFirstY = toy - pfc.ShortSweepRadius, tryToY = toy + pfc.ShortSweepRadius; - if (!find_nearest_walkable_area(tempw, tryFirstX, tryFirstY, tryToX, tryToY, tox, toy, 3)) + if (!find_nearest_walkable_area(tempw, tryFirstX, tryFirstY, tryToX, tryToY, tox, toy, pfc.ShortSweepGranularity)) { // Nothing found, sweep the whole room at 5 pixel granularity - find_nearest_walkable_area(tempw, 0, 0, tempw->GetWidth(), tempw->GetHeight(), tox, toy, 5); + find_nearest_walkable_area(tempw, 0, 0, tempw->GetWidth(), tempw->GetHeight(), tox, toy, pfc.FullSweepGranularity); } delete tempw; @@ -396,7 +408,7 @@ static void round_down_coords(int &tmpx, int &tmpy) } } -static int find_route_dijkstra(int fromx, int fromy, int destx, int desty) +static int find_route_dijkstra(int fromx, int fromy, int destx, int desty, const PathfinderConfig &pfc) { int i, j; @@ -433,10 +445,10 @@ static int find_route_dijkstra(int fromx, int fromy, int destx, int desty) int granularity = 3, newx = -1, newy, foundAnswer = -1, numreplace; int changeiter, numfound, adjcount; - int destxlow = destx - MAX_GRANULARITY; - int destylow = desty - MAX_GRANULARITY; - int destxhi = destxlow + MAX_GRANULARITY * 2; - int destyhi = destylow + MAX_GRANULARITY * 2; + int destxlow = destx - pfc.MaxGranularity; + int destylow = desty - pfc.MaxGranularity; + int destxhi = destxlow + pfc.MaxGranularity * 2; + int destyhi = destylow + pfc.MaxGranularity * 2; int modifier = 0; int totalfound = 0; int DIRECTION_BONUS = 0; @@ -503,10 +515,10 @@ static int find_route_dijkstra(int fromx, int fromy, int destx, int desty) // edges of screen pose a problem, so if current and dest are within // certain distance of the edge, say we've got it - if ((newx >= wallscreen->GetWidth() - MAX_GRANULARITY) && (destx >= wallscreen->GetWidth() - MAX_GRANULARITY)) + if ((newx >= wallscreen->GetWidth() - pfc.MaxGranularity) && (destx >= wallscreen->GetWidth() - pfc.MaxGranularity)) newx = destx; - if ((newy >= wallscreen->GetHeight() - MAX_GRANULARITY) && (desty >= wallscreen->GetHeight() - MAX_GRANULARITY)) + if ((newy >= wallscreen->GetHeight() - pfc.MaxGranularity) && (desty >= wallscreen->GetHeight() - pfc.MaxGranularity)) newy = desty; // Found the desination, abort loop @@ -578,7 +590,7 @@ static int find_route_dijkstra(int fromx, int fromy, int destx, int desty) return 1; } -static int __find_route(int srcx, int srcy, short *tox, short *toy, int noredx) +static int __find_route(int srcx, int srcy, short *tox, short *toy, int noredx, const PathfinderConfig &pfc) { assert(wallscreen != nullptr); assert(beenhere != nullptr); @@ -599,7 +611,7 @@ static int __find_route(int srcx, int srcy, short *tox, short *toy, int noredx) return 1; } - if ((waspossible = is_route_possible(srcx, srcy, tox[0], toy[0], wallscreen)) == 0) { + if ((waspossible = is_route_possible(srcx, srcy, tox[0], toy[0], wallscreen, pfc)) == 0) { if (suggestx >= 0) { tox[0] = suggestx; toy[0] = suggesty; @@ -615,7 +627,7 @@ static int __find_route(int srcx, int srcy, short *tox, short *toy, int noredx) } // Try the new pathfinding algorithm - if (find_route_dijkstra(srcx, srcy, tox[0], toy[0])) { + if (find_route_dijkstra(srcx, srcy, tox[0], toy[0], pfc)) { return 1; } @@ -731,6 +743,11 @@ int find_route(short srcx, short srcy, short xx, short yy, int move_speed_x, int assert(pathbackx != nullptr); assert(pathbacky != nullptr); + // Setup pathfinder configuration, depending on the loaded game version; + // sweep granularity has changed between 3.0.0 and 3.0.1; see issue #663 + PathfinderConfig pfc; + pfc.ShortSweepGranularity = (loaded_game_file_version > kGameVersion_300) ? 3 : 1; + #ifdef DEBUG_PATHFINDER // __wnormscreen(); #endif @@ -767,9 +784,9 @@ int find_route(short srcx, short srcy, short xx, short yy, int move_speed_x, int for (aaa = 1; aaa < wallscreen->GetHeight(); aaa++) beenhere[aaa] = beenhere[0] + aaa * (wallscreen->GetWidth()); - if (__find_route(srcx, srcy, &xx, &yy, nocross) == 0) { + if (__find_route(srcx, srcy, &xx, &yy, nocross, pfc) == 0) { leftorright = 1; - if (__find_route(srcx, srcy, &xx, &yy, nocross) == 0) + if (__find_route(srcx, srcy, &xx, &yy, nocross, pfc) == 0) pathbackstage = -1; } free(beenhere[0]);