mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #4576 from zaxcav/fixPathFind
Pathfinding checks all map elements on each tile.
This commit is contained in:
commit
a572ecf477
674
src/peep/peep.c
674
src/peep/peep.c
|
@ -102,6 +102,11 @@ enum {
|
|||
PATH_SEARCH_FAILED
|
||||
};
|
||||
|
||||
// Some text descriptions corresponding to the above enum for understandable debug messages
|
||||
const char *gPathFindSearchText[] = {"DeadEnd", "Wide", "Thin", "Junction", "RideQueue", "RideEntrance", "RideExit", "ParkEntryExit", "ShopEntrance", "LimitReached", "PathLoop", "Other", "Failed"};
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
F1EE18_DESTINATION_REACHED = 1 << 0,
|
||||
F1EE18_OUTSIDE_PARK = 1 << 1,
|
||||
|
@ -8754,10 +8759,15 @@ static bool path_is_thin_junction(rct_map_element *path, sint16 x, sint16 y, uin
|
|||
* no matter how close to the goal they get, but will follow possible "through
|
||||
* paths".
|
||||
*
|
||||
* The implementation is essentially an A* path finding algorithm over the
|
||||
* path layout in xyz; however a best score is tracked via the score parameter
|
||||
* rather than storing scores for each xyz, which means explicit loop detection
|
||||
* is necessary to limit the search space.
|
||||
* The implementation is a depth first search of the path layout in xyz
|
||||
* according to the search limits.
|
||||
* Unlike an A* search, which tracks for each tile a heuristic score (a
|
||||
* function of the xyz distances to the goal) and cost of reaching that tile
|
||||
* (steps to the tile), a single best result "so far" (best heuristic score
|
||||
* with least cost) is tracked via the score parameter.
|
||||
* With this approach, explicit loop detection is necessary to limit the
|
||||
* search space, and each alternate route through the same tile can be
|
||||
* returned as the best result, rather than only the shortest route with A*.
|
||||
*
|
||||
* The parameters that hold the best search result so far are:
|
||||
* - score - the least heuristic distance from the goal
|
||||
|
@ -8828,58 +8838,75 @@ static void peep_pathfind_heuristic_search(sint16 x, sint16 y, uint8 z, rct_map_
|
|||
return;
|
||||
}
|
||||
|
||||
/* Get the next map element in the direction of test_edge, ignoring
|
||||
* map elements that are not of interest, which includes wide paths
|
||||
* and foreign ride queues (depending on gPeepPathFindIgnoreForeignQueues) */
|
||||
/* Get the next map element of interest in the direction of test_edge. */
|
||||
bool found = false;
|
||||
rct_map_element *mapElement = map_get_first_element_at(x / 32, y / 32);
|
||||
do {
|
||||
/* Look for all map elements that the peep could walk onto while
|
||||
* navigating to the goal, including the goal tile. */
|
||||
|
||||
if (mapElement->flags & MAP_ELEMENT_FLAG_GHOST) continue;
|
||||
|
||||
uint8 rideIndex = 0xFF;
|
||||
switch (map_element_get_type(mapElement)) {
|
||||
case MAP_ELEMENT_TYPE_TRACK:
|
||||
if (z != mapElement->base_height) continue;
|
||||
/* More details about the mapElement are not needed.
|
||||
* If in the future it would be useful to know
|
||||
* whether the map element is a shop, the following
|
||||
* commented out code will do it. */
|
||||
//rideIndex = mapElement->properties.track.ride_index;
|
||||
//rct_ride *ride = get_ride(rideIndex);
|
||||
//if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) {
|
||||
// searchResult = PATH_SEARCH_SHOP_ENTRANCE;
|
||||
//} else {
|
||||
searchResult = PATH_SEARCH_OTHER;
|
||||
//}
|
||||
found = true;
|
||||
break;
|
||||
/* For peeps heading for a shop, the goal is the shop
|
||||
* tile. */
|
||||
rideIndex = mapElement->properties.track.ride_index;
|
||||
rct_ride *ride = get_ride(rideIndex);
|
||||
if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_IS_SHOP)) {
|
||||
found = true;
|
||||
searchResult = PATH_SEARCH_SHOP_ENTRANCE;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
case MAP_ELEMENT_TYPE_ENTRANCE:
|
||||
if (z != mapElement->base_height) continue;
|
||||
int direction;
|
||||
searchResult = PATH_SEARCH_OTHER;
|
||||
switch (mapElement->properties.entrance.type) {
|
||||
case ENTRANCE_TYPE_RIDE_ENTRANCE:
|
||||
/* For peeps heading for a ride without a queue, the
|
||||
* goal is the ride entrance tile.
|
||||
* For mechanics heading for the ride entrance
|
||||
* (in the case when the station has no exit),
|
||||
* the goal is the ride entrance tile. */
|
||||
direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
|
||||
if (direction == test_edge) {
|
||||
/* The rideIndex will be useful for
|
||||
* adding transport rides later. */
|
||||
rideIndex = mapElement->properties.entrance.ride_index;
|
||||
searchResult = PATH_SEARCH_RIDE_ENTRANCE;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
continue; // Ride entrance is not facing the right direction.
|
||||
case ENTRANCE_TYPE_PARK_ENTRANCE:
|
||||
/* For peeps leaving the park, the goal is the park
|
||||
* entrance/exit tile. */
|
||||
searchResult = PATH_SEARCH_PARK_EXIT;
|
||||
found = true;
|
||||
break;
|
||||
/* More details for other entrance types are not needed.
|
||||
* If in the future it would be useful to know
|
||||
* whether the map element is a ride exit or a park
|
||||
* entrance/exit, the following commented out code
|
||||
* will do it. */
|
||||
//case ENTRANCE_TYPE_RIDE_EXIT:
|
||||
// direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
|
||||
// if (direction == test_edge) {
|
||||
// searchResult = PATH_SEARCH_RIDE_EXIT;
|
||||
// }
|
||||
// break;
|
||||
//case ENTRANCE_TYPE_PARK_ENTRANCE:
|
||||
// searchResult = PATH_SEARCH_PARK_EXIT;
|
||||
case ENTRANCE_TYPE_RIDE_EXIT:
|
||||
/* For mechanics heading for the ride exit, the
|
||||
* goal is the ride exit tile. */
|
||||
direction = mapElement->type & MAP_ELEMENT_DIRECTION_MASK;
|
||||
if (direction == test_edge) {
|
||||
searchResult = PATH_SEARCH_RIDE_EXIT;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
continue; // Ride exit is not facing the right direction.
|
||||
default: continue;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
case MAP_ELEMENT_TYPE_PATH:
|
||||
/* For peeps heading for a ride with a queue, the goal is the last
|
||||
* queue path.
|
||||
* Otherwise, peeps walk on path tiles to get to the goal. */
|
||||
|
||||
if (!is_valid_path_z_and_direction(mapElement, z, test_edge)) continue;
|
||||
|
||||
// Path may be sloped, so set z to path base height.
|
||||
|
@ -8903,307 +8930,321 @@ static void peep_pathfind_heuristic_search(sint16 x, sint16 y, uint8 z, rct_map_
|
|||
if (footpath_element_is_queue(mapElement) && mapElement->properties.path.ride_index != gPeepPathFindQueueRideIndex) {
|
||||
if (gPeepPathFindIgnoreForeignQueues && (mapElement->properties.path.ride_index != 0xFF)) {
|
||||
// Path is a queue we aren't interested in
|
||||
/* The rideIndex will be useful for
|
||||
* adding transport rides later. */
|
||||
rideIndex = mapElement->properties.path.ride_index;
|
||||
searchResult = PATH_SEARCH_RIDE_QUEUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
} while (!map_element_is_last_for_tile(mapElement++));
|
||||
|
||||
/* No map element could be found.
|
||||
* Return without updating the parameters (best result so far). */
|
||||
if (!found) {
|
||||
#
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; No map element found", counter, x >> 5, y >> 5, z);
|
||||
log_info("[%03d] Checking map element at %d,%d,%d; Type: %s", counter, x >> 5, y >> 5, z, gPathFindSearchText[searchResult]);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
return;
|
||||
}
|
||||
|
||||
/* At this point the map element is found. */
|
||||
/* At this point mapElement is of interest to the pathfinding. */
|
||||
|
||||
uint8 height = z;
|
||||
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_PATH) {
|
||||
// Adjust height for goal comparison according to the path slope.
|
||||
if (footpath_element_is_sloped(mapElement)) {
|
||||
if (footpath_element_get_slope_direction(mapElement) == test_edge) {
|
||||
height += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Should we check that this mapElement is connected in the
|
||||
* reverse direction? For some mapElement types this was
|
||||
* already done above (e.g. ride entrances), but for others not.
|
||||
* Ignore for now. */
|
||||
|
||||
// Calculate the heuristic score of this map element.
|
||||
uint16 x_delta = abs(gPeepPathFindGoalPosition.x - x);
|
||||
uint16 y_delta = abs(gPeepPathFindGoalPosition.y - y);
|
||||
if (x_delta < y_delta) x_delta >>= 4;
|
||||
else y_delta >>= 4;
|
||||
uint16 new_score = x_delta + y_delta;
|
||||
uint16 z_delta = abs(gPeepPathFindGoalPosition.z - height);
|
||||
z_delta <<= 1;
|
||||
new_score += z_delta;
|
||||
// Calculate the heuristic score of this map element.
|
||||
uint16 x_delta = abs(gPeepPathFindGoalPosition.x - x);
|
||||
uint16 y_delta = abs(gPeepPathFindGoalPosition.y - y);
|
||||
if (x_delta < y_delta) x_delta >>= 4;
|
||||
else y_delta >>= 4;
|
||||
uint16 new_score = x_delta + y_delta;
|
||||
uint16 z_delta = abs(gPeepPathFindGoalPosition.z - z);
|
||||
z_delta <<= 1;
|
||||
new_score += z_delta;
|
||||
|
||||
/* If this map element is the search goal the current search path ends here. */
|
||||
if (new_score == 0) {
|
||||
/* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search. */
|
||||
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; At goal; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* At this point the map element tile is not the goal. */
|
||||
|
||||
/* If this map element is not a path, the search cannot be continued.
|
||||
* Return without updating the parameters (best result so far). */
|
||||
if (searchResult != PATH_SEARCH_DEAD_END &&
|
||||
searchResult != PATH_SEARCH_THIN &&
|
||||
searchResult != PATH_SEARCH_JUNCTION &&
|
||||
searchResult != PATH_SEARCH_WIDE) {
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; Not a path", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
return;
|
||||
}
|
||||
|
||||
/* At this point the map element is a path. */
|
||||
|
||||
/* If this is a wide path the search ends here. */
|
||||
if (searchResult == PATH_SEARCH_WIDE) {
|
||||
/* Ignore Wide paths as continuing paths UNLESS the current path is also Wide.
|
||||
* i.e. search across wide paths from a wide path to get onto a thin path,
|
||||
* thereafter stay on thin paths. */
|
||||
/* So, if the current path is also wide the goal could still
|
||||
* be reachable from here.
|
||||
* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search. */
|
||||
if (footpath_element_is_wide(currentMapElement) &&
|
||||
(new_score < *endScore || (new_score == *endScore && counter < *endSteps ))) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; Wide path; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
return;
|
||||
}
|
||||
|
||||
/* At this point the map element is a non-wide path.*/
|
||||
|
||||
/* Get all the permitted_edges of the map element. */
|
||||
uint8 edges = path_get_permitted_edges(mapElement);
|
||||
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Path %d,%d,%d; Edges (0123):%d%d%d%d; Reverse: %d", counter, x >> 5, y >> 5, z, edges & 1, (edges & 2) >> 1, (edges & 4) >> 2, (edges & 8) >> 3, test_edge ^ 2);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
|
||||
/* Remove the reverse edge (i.e. the edge back to the previous map element.) */
|
||||
edges &= ~(1 << (test_edge ^ 2));
|
||||
test_edge = bitscanforward(edges);
|
||||
|
||||
/* If there are no other edges the current search ends here.
|
||||
* Return without updating the parameters (best result so far). */
|
||||
if (test_edge == -1) {
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; No more edges/dead end", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if either of the search limits has been reached:
|
||||
* - max number of steps or max tiles checked. */
|
||||
if (counter >= 200 || _peepPathFindTilesChecked <= 0) {
|
||||
/* The current search ends here.
|
||||
* The path continues, so the goal could still be reachable from here.
|
||||
* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search. */
|
||||
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; Search limit reached", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
return;
|
||||
}
|
||||
|
||||
bool thin_junction = false;
|
||||
if (searchResult == PATH_SEARCH_JUNCTION) {
|
||||
/* Check if this is a thin junction. And perform additional
|
||||
* necessary checks. */
|
||||
thin_junction = path_is_thin_junction(mapElement, x, y, z);
|
||||
|
||||
if (thin_junction) {
|
||||
/* The current search path is passing through a thin
|
||||
* junction on this map element. Only 'thin' junctions
|
||||
* are counted towards the junction search limit. */
|
||||
|
||||
/* Check the pathfind_history to see if this junction has been
|
||||
* previously passed through in the current search path.
|
||||
* i.e. this is a loop in the current search path.
|
||||
* If so, the current search path ends here.
|
||||
* Return without updating the parameters (best result so far). */
|
||||
for (int junctionNum = _peepPathFindNumJunctions + 1; junctionNum <= _peepPathFindMaxJunctions; junctionNum++) {
|
||||
if ((_peepPathFindHistory[junctionNum].location.x == (uint8)(x >> 5)) &&
|
||||
(_peepPathFindHistory[junctionNum].location.y == (uint8)(y >> 5)) &&
|
||||
(_peepPathFindHistory[junctionNum].location.z == (uint8)z)) {
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; Loop", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the junction search limit is reached, the
|
||||
* current search path ends here. The goal may still
|
||||
* be reachable from here.
|
||||
* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search. */
|
||||
if (_peepPathFindNumJunctions <= 0) {
|
||||
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions; // - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
/* If this map element is the search goal the current search path ends here. */
|
||||
if (new_score == 0) {
|
||||
/* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search before continuing to the next map element. */
|
||||
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return from %d,%d,%d; NumJunctions < 0; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
|
||||
log_info("[%03d] Search path ends at %d,%d,%d; At goal; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this point the map element tile is not the goal. */
|
||||
|
||||
/* If this map element is not a path, the search cannot be continued.
|
||||
* Continue to the next map element without updating the parameters (best result so far). */
|
||||
if (searchResult != PATH_SEARCH_DEAD_END &&
|
||||
searchResult != PATH_SEARCH_THIN &&
|
||||
searchResult != PATH_SEARCH_JUNCTION &&
|
||||
searchResult != PATH_SEARCH_WIDE) {
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Search path ends at %d,%d,%d; Not a path", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this point the map element is a path. */
|
||||
|
||||
/* If this is a wide path the search ends here. */
|
||||
if (searchResult == PATH_SEARCH_WIDE) {
|
||||
/* Ignore Wide paths as continuing paths UNLESS the current path is also Wide.
|
||||
* i.e. search across wide paths from a wide path to get onto a thin path,
|
||||
* thereafter stay on thin paths. */
|
||||
/* So, if the current path is also wide the goal could still
|
||||
* be reachable from here.
|
||||
* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search before continuing to the next map element. */
|
||||
if (footpath_element_is_wide(currentMapElement) &&
|
||||
(new_score < *endScore || (new_score == *endScore && counter < *endSteps ))) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Search path ends at %d,%d,%d; Wide path; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this point the map element is a non-wide path.*/
|
||||
|
||||
/* Get all the permitted_edges of the map element. */
|
||||
uint8 edges = path_get_permitted_edges(mapElement);
|
||||
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Path element at %d,%d,%d; Edges (0123):%d%d%d%d; Reverse: %d", counter, x >> 5, y >> 5, z, edges & 1, (edges & 2) >> 1, (edges & 4) >> 2, (edges & 8) >> 3, test_edge ^ 2);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
|
||||
/* Remove the reverse edge (i.e. the edge back to the previous map element.) */
|
||||
edges &= ~(1 << (test_edge ^ 2));
|
||||
test_edge = bitscanforward(edges);
|
||||
|
||||
/* If there are no other edges the current search ends here.
|
||||
* Continue to the next map element without updating the parameters (best result so far). */
|
||||
if (test_edge == -1) {
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Search path ends at %d,%d,%d; No more edges/dead end", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if either of the search limits has been reached:
|
||||
* - max number of steps or max tiles checked. */
|
||||
if (counter >= 200 || _peepPathFindTilesChecked <= 0) {
|
||||
/* The current search ends here.
|
||||
* The path continues, so the goal could still be reachable from here.
|
||||
* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search before continuing to the next map element. */
|
||||
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Search path ends at %d,%d,%d; Search limit reached", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
continue;
|
||||
}
|
||||
|
||||
bool thin_junction = false;
|
||||
if (searchResult == PATH_SEARCH_JUNCTION) {
|
||||
/* Check if this is a thin junction. And perform additional
|
||||
* necessary checks. */
|
||||
thin_junction = path_is_thin_junction(mapElement, x, y, z);
|
||||
|
||||
if (thin_junction) {
|
||||
/* The current search path is passing through a thin
|
||||
* junction on this map element. Only 'thin' junctions
|
||||
* are counted towards the junction search limit. */
|
||||
|
||||
/* Check the pathfind_history to see if this junction has been
|
||||
* previously passed through in the current search path.
|
||||
* i.e. this is a loop in the current search path.
|
||||
* If so, the current search path ends here.
|
||||
* Continue to the next map element without updating the parameters (best result so far). */
|
||||
bool pathLoop = false;
|
||||
for (int junctionNum = _peepPathFindNumJunctions + 1; junctionNum <= _peepPathFindMaxJunctions; junctionNum++) {
|
||||
if ((_peepPathFindHistory[junctionNum].location.x == (uint8)(x >> 5)) &&
|
||||
(_peepPathFindHistory[junctionNum].location.y == (uint8)(y >> 5)) &&
|
||||
(_peepPathFindHistory[junctionNum].location.z == (uint8)z)) {
|
||||
pathLoop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pathLoop) {
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Search path ends at %d,%d,%d; Loop", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the junction search limit is reached, the
|
||||
* current search path ends here. The goal may still
|
||||
* be reachable from here.
|
||||
* If the search result is better than the best so far (in the paramaters),
|
||||
* then update the parameters with this search before continuing to the next map element. */
|
||||
if (_peepPathFindNumJunctions <= 0) {
|
||||
if (new_score < *endScore || (new_score == *endScore && counter < *endSteps )) {
|
||||
// Update the search results
|
||||
*endScore = new_score;
|
||||
*endSteps = counter;
|
||||
// Update the end x,y,z
|
||||
endXYZ->x = x >> 5;
|
||||
endXYZ->y = y >> 5;
|
||||
endXYZ->z = z;
|
||||
// Update the telemetry
|
||||
*endJunctions = _peepPathFindMaxJunctions; // - _peepPathFindNumJunctions;
|
||||
for (uint8 junctInd = 0; junctInd < *endJunctions; junctInd++) {
|
||||
uint8 histIdx = _peepPathFindMaxJunctions - junctInd;
|
||||
junctionList[junctInd].x = _peepPathFindHistory[histIdx].location.x;
|
||||
junctionList[junctInd].y = _peepPathFindHistory[histIdx].location.y;
|
||||
junctionList[junctInd].z = _peepPathFindHistory[histIdx].location.z;
|
||||
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Search path ends at %d,%d,%d; NumJunctions < 0; Score: %d", counter, x >> 5, y >> 5, z, *endScore);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* This junction was NOT previously visited in the current
|
||||
* search path, so add the junction to the history. */
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions].location.x = (uint8)(x >> 5);
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions].location.y = (uint8)(y >> 5);
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions].location.z = (uint8)z;
|
||||
// .direction take is added below.
|
||||
|
||||
_peepPathFindNumJunctions--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Continue searching down each remaining edge of the path
|
||||
* (recursive call). */
|
||||
do {
|
||||
edges &= ~(1 << test_edge);
|
||||
uint8 savedNumJunctions = _peepPathFindNumJunctions;
|
||||
|
||||
uint8 height = z;
|
||||
if (footpath_element_is_sloped(mapElement) &&
|
||||
footpath_element_get_slope_direction(mapElement) == test_edge) {
|
||||
height += 2;
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
if (searchResult == PATH_SEARCH_JUNCTION) {
|
||||
if (thin_junction)
|
||||
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Thin-Junction", counter, x >> 5, y >> 5, z, test_edge);
|
||||
else
|
||||
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Wide-Junction", counter, x >> 5, y >> 5, z, test_edge);
|
||||
} else {
|
||||
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Segment", counter, x >> 5, y >> 5, z, test_edge);
|
||||
}
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
|
||||
if (thin_junction) {
|
||||
/* Add the current test_edge to the history. */
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions + 1].direction = test_edge;
|
||||
}
|
||||
|
||||
peep_pathfind_heuristic_search(x, y, height, mapElement, counter, endScore, test_edge, endJunctions, junctionList, directionList, endXYZ, endSteps);
|
||||
_peepPathFindNumJunctions = savedNumJunctions;
|
||||
|
||||
/* This junction was NOT previously visited in the current
|
||||
* search path, so add the junction to the history. */
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions].location.x = (uint8)(x >> 5);
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions].location.y = (uint8)(y >> 5);
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions].location.z = (uint8)z;
|
||||
// .direction take is added below.
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Returned to %d,%d,%d edge: %d; Score: %d", counter, x >> 5, y >> 5, z, test_edge, *endScore);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
} while ((test_edge = bitscanforward(edges)) != -1);
|
||||
|
||||
_peepPathFindNumJunctions--;
|
||||
} while (!map_element_is_last_for_tile(mapElement++));
|
||||
|
||||
if (!found) {
|
||||
/* No map element could be found.
|
||||
* Return without updating the parameters (best result so far). */
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Returning from %d,%d,%d; No relevant map element found", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
} else {
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Returning from %d,%d,%d; All map elements checked", counter, x >> 5, y >> 5, z);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
}
|
||||
|
||||
/* Continue searching down each remaining edge of the path
|
||||
* (recursive call). */
|
||||
do {
|
||||
edges &= ~(1 << test_edge);
|
||||
uint8 savedNumJunctions = _peepPathFindNumJunctions;
|
||||
|
||||
uint8 height = z;
|
||||
if (footpath_element_is_sloped(mapElement) &&
|
||||
footpath_element_get_slope_direction(mapElement) == test_edge) {
|
||||
height += 2;
|
||||
}
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
if (searchResult == PATH_SEARCH_JUNCTION) {
|
||||
if (thin_junction)
|
||||
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Thin-Junction", counter, x >> 5, y >> 5, z, test_edge);
|
||||
else
|
||||
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Wide-Junction", counter, x >> 5, y >> 5, z, test_edge);
|
||||
} else {
|
||||
log_info("[%03d] Recurse from %d,%d,%d edge: %d; Segment", counter, x >> 5, y >> 5, z, test_edge);
|
||||
}
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
|
||||
if (thin_junction) {
|
||||
/* Add the current test_edge to the history. */
|
||||
_peepPathFindHistory[_peepPathFindNumJunctions + 1].direction = test_edge;
|
||||
}
|
||||
|
||||
peep_pathfind_heuristic_search(x, y, height, mapElement, counter, endScore, test_edge, endJunctions, junctionList, directionList, endXYZ, endSteps);
|
||||
_peepPathFindNumJunctions = savedNumJunctions;
|
||||
|
||||
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
if (gPathFindDebug) {
|
||||
log_info("[%03d] Return to %d,%d,%d edge: %d; Score: %d", counter, x >> 5, y >> 5, z, test_edge, *endScore);
|
||||
}
|
||||
#endif // defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
|
||||
} while ((test_edge = bitscanforward(edges)) != -1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -9355,9 +9396,9 @@ int peep_pathfind_choose_direction(sint16 x, sint16 y, uint8 z, rct_peep *peep)
|
|||
|
||||
uint8 endSteps = 255;
|
||||
|
||||
/* Variable bestJunctions is the number of junctions
|
||||
* pass through in the search path.
|
||||
* Variables bestJunctionList and bestDirectionList
|
||||
/* Variable endJunctions is the number of junctions
|
||||
* passed through in the search path.
|
||||
* Variables endJunctionList and endDirectionList
|
||||
* contain the junctions and corresponding directions
|
||||
* of the search path.
|
||||
* In the future these could be used to visualise the
|
||||
|
@ -9593,8 +9634,21 @@ static int guest_path_find_park_entrance(rct_peep* peep, rct_map_element *map_el
|
|||
gPeepPathFindIgnoreForeignQueues = true;
|
||||
gPeepPathFindQueueRideIndex = 255;
|
||||
|
||||
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
|
||||
/* Determine if the pathfinding debugging is wanted for this peep. */
|
||||
/* For guests, use the existing PEEP_FLAGS_TRACKING flag to
|
||||
* determine for which guest(s) the pathfinding debugging will
|
||||
* be output for. */
|
||||
format_string(gPathFindDebugPeepName, sizeof(gPathFindDebugPeepName), peep->name_string_idx, &(peep->id));
|
||||
gPathFindDebug = peep->peep_flags & PEEP_FLAGS_TRACKING;
|
||||
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
|
||||
|
||||
int chosenDirection = peep_pathfind_choose_direction(peep->next_x, peep->next_y, peep->next_z, peep);
|
||||
|
||||
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
|
||||
gPathFindDebug = false;
|
||||
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
|
||||
|
||||
if (chosenDirection == -1)
|
||||
return guest_path_find_aimless(peep, edges);
|
||||
else
|
||||
|
|
|
@ -1093,21 +1093,6 @@ static uint8 staff_mechanic_direction_path(rct_peep* peep, uint8 validDirections
|
|||
return staff_mechanic_direction_path_rand(peep, pathDirections);
|
||||
}
|
||||
|
||||
/* Adjust the peep goal according to the direction of the
|
||||
* exit/entrance. */
|
||||
uint8 entranceDirection = map_element_get_direction(mapElement);
|
||||
chosenTile.x -= TileDirectionDelta[entranceDirection].x;
|
||||
chosenTile.y -= TileDirectionDelta[entranceDirection].y;
|
||||
gPeepPathFindGoalPosition.x = chosenTile.x;
|
||||
gPeepPathFindGoalPosition.y = chosenTile.y;
|
||||
|
||||
// Peep is about to walk into the target exit/entrance.
|
||||
if (chosenTile.x == peep->next_x &&
|
||||
chosenTile.y == peep->next_y &&
|
||||
z == peep->next_z) {
|
||||
return entranceDirection;
|
||||
}
|
||||
|
||||
gPeepPathFindIgnoreForeignQueues = false;
|
||||
gPeepPathFindQueueRideIndex = 255;
|
||||
|
||||
|
|
Loading…
Reference in New Issue