Merge pull request #17204 from LordOfLunacy/develop

I have picked up the task of preparing a rework of the pathfinding to allow for the addition of a new pathfinding implementation to the game. This initial pull request makes GuestPathfinding more modular by creating a GuestPathfinding base class and implementing the current pathfinding system into an OriginalPathfinding subclass.
This commit is contained in:
Ted John 2022-05-16 09:32:52 +01:00 committed by GitHub
commit 4adca3ba15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 169 additions and 125 deletions

View File

@ -9,6 +9,7 @@
#include "GameState.h"
#include "./peep/GuestPathfinding.h"
#include "Context.h"
#include "Editor.h"
#include "Game.h"

View File

@ -73,6 +73,8 @@ uint32_t gNextGuestNumber;
uint8_t gPeepWarningThrottle[16];
std::unique_ptr<GuestPathfinding> gGuestPathfinder = std::make_unique<OriginalPathfinding>();
static uint8_t _unk_F1AEF0;
static TileElement* _peepRideEntranceExitElement;
@ -2357,7 +2359,7 @@ void Peep::PerformNextAction(uint8_t& pathing_result, TileElement*& tile_result)
if (guest != nullptr)
{
result = guest_path_finding(guest);
result = gGuestPathfinder->CalculateNextDestination(*guest);
}
else
{

View File

@ -399,7 +399,7 @@ public: // Peep
bool IsActionInterruptable() const;
// Reset the peep's stored goal, which means they will forget any stored pathfinding history
// on the next peep_pathfind_choose_direction call.
// on the next GuestPathfinding::ChooseDirection call.
void ResetPathfindGoal();
void SetDestination(const CoordsXY& coords);

View File

@ -181,7 +181,7 @@ bool Staff::CanIgnoreWideFlag(const CoordsXYZ& staffPos, TileElement* path) cons
}
/* test_element is a path */
if (!IsValidPathZAndDirection(test_element, adjacPos.z / COORDS_Z_STEP, adjac_dir))
if (!GuestPathfinding::IsValidPathZAndDirection(test_element, adjacPos.z / COORDS_Z_STEP, adjac_dir))
continue;
/* test_element is a connected path */
@ -719,10 +719,10 @@ Direction Staff::MechanicDirectionPath(uint8_t validDirections, PathElement* pat
gPeepPathFindQueueRideIndex = RideId::GetNull();
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
PathfindLoggingEnable(this);
PathfindLoggingEnable(*this);
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
Direction pathfindDirection = peep_pathfind_choose_direction(TileCoordsXYZ{ NextLoc }, this);
Direction pathfindDirection = gGuestPathfinder->ChooseDirection(TileCoordsXYZ{ NextLoc }, *this);
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
PathfindLoggingDisable();
@ -732,7 +732,7 @@ Direction Staff::MechanicDirectionPath(uint8_t validDirections, PathElement* pat
{
/* Heuristic search failed for all directions.
* Reset the PathfindGoal - this means that the PathfindHistory
* will be reset in the next call to peep_pathfind_choose_direction().
* will be reset in the next call to GuestPathfinding::ChooseDirection().
* This lets the heuristic search "try again" in case the player has
* edited the path layout or the mechanic was already stuck in the
* save game (e.g. with a worse version of the pathfinding). */

View File

@ -43,7 +43,7 @@ static bool _pathFindDebug = false;
static utf8 _pathFindDebugPeepName[256];
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
static int32_t guest_surface_path_finding(Peep* peep);
static int32_t guest_surface_path_finding(Peep& peep);
/* A junction history for the peep pathfinding heuristic search
* The magic number 16 is the largest value returned by
@ -124,10 +124,10 @@ static int32_t path_get_permitted_edges(PathElement* pathElement)
*
* rct2: 0x0069524E
*/
static int32_t peep_move_one_tile(Direction direction, Peep* peep)
static int32_t peep_move_one_tile(Direction direction, Peep& peep)
{
assert(direction_valid(direction));
auto newTile = CoordsXY{ CoordsXY{ peep->NextLoc } + CoordsDirectionDelta[direction] }.ToTileCentre();
auto newTile = CoordsXY{ CoordsXY{ peep.NextLoc } + CoordsDirectionDelta[direction] }.ToTileCentre();
if (newTile.x >= MAXIMUM_MAP_SIZE_BIG || newTile.y >= MAXIMUM_MAP_SIZE_BIG)
{
@ -135,8 +135,8 @@ static int32_t peep_move_one_tile(Direction direction, Peep* peep)
return guest_surface_path_finding(peep);
}
peep->PeepDirection = direction;
if (peep->State != PeepState::Queuing)
peep.PeepDirection = direction;
if (peep.State != PeepState::Queuing)
{
// When peeps are walking along a path, we would like them to be spread out across the width of the path,
// instead of all walking along the exact centre line of the path.
@ -159,19 +159,19 @@ static int32_t peep_move_one_tile(Direction direction, Peep* peep)
if (direction == 0 || direction == 2)
{
// Peep is moving along X, so apply the offset to the X position of the destination and clamp their current Y
const int32_t centreLine = (peep->y & 0xFFE0) + COORDS_XY_HALF_TILE;
const int32_t centreLine = (peep.y & 0xFFE0) + COORDS_XY_HALF_TILE;
newTile.x += offset;
newTile.y = std::clamp<int32_t>(peep->y, centreLine - 3, centreLine + 3);
newTile.y = std::clamp<int32_t>(peep.y, centreLine - 3, centreLine + 3);
}
else
{
// Peep is moving along Y, so apply the offset to the Y position of the destination and clamp their current X
const int32_t centreLine = (peep->x & 0xFFE0) + COORDS_XY_HALF_TILE;
newTile.x = std::clamp<int32_t>(peep->x, centreLine - 3, centreLine + 3);
const int32_t centreLine = (peep.x & 0xFFE0) + COORDS_XY_HALF_TILE;
newTile.x = std::clamp<int32_t>(peep.x, centreLine - 3, centreLine + 3);
newTile.y += offset;
}
}
peep->SetDestination(newTile, 2);
peep.SetDestination(newTile, 2);
return 0;
}
@ -180,9 +180,9 @@ static int32_t peep_move_one_tile(Direction direction, Peep* peep)
*
* rct2: 0x00694C41
*/
static int32_t guest_surface_path_finding(Peep* peep)
static int32_t guest_surface_path_finding(Peep& peep)
{
auto pathPos = CoordsXYRangedZ{ peep->NextLoc, peep->NextLoc.z, peep->NextLoc.z + PATH_CLEARANCE };
auto pathPos = CoordsXYRangedZ{ peep.NextLoc, peep.NextLoc.z, peep.NextLoc.z + PATH_CLEARANCE };
Direction randDirection = scenario_rand() & 3;
if (!fence_in_the_way(pathPos, randDirection))
@ -208,8 +208,8 @@ static int32_t guest_surface_path_finding(Peep* peep)
}
randDirection &= 3;
pathPos.x = peep->NextLoc.x;
pathPos.y = peep->NextLoc.y;
pathPos.x = peep.NextLoc.x;
pathPos.y = peep.NextLoc.y;
if (!fence_in_the_way(pathPos, randDirection))
{
pathPos.x += CoordsDirectionDelta[randDirection].x;
@ -228,8 +228,8 @@ static int32_t guest_surface_path_finding(Peep* peep)
randDirection -= 2;
randDirection &= 3;
pathPos.x = peep->NextLoc.x;
pathPos.y = peep->NextLoc.y;
pathPos.x = peep.NextLoc.x;
pathPos.y = peep.NextLoc.y;
if (!fence_in_the_way(pathPos, randDirection))
{
pathPos.x += CoordsDirectionDelta[randDirection].x;
@ -289,7 +289,7 @@ static uint8_t footpath_element_next_in_direction(TileCoordsXYZ loc, PathElement
continue;
if (nextTileElement->GetType() != TileElementType::Path)
continue;
if (!IsValidPathZAndDirection(nextTileElement, loc.z, chosenDirection))
if (!GuestPathfinding::IsValidPathZAndDirection(nextTileElement, loc.z, chosenDirection))
continue;
if (nextTileElement->AsPath()->IsWide())
return PATH_SEARCH_WIDE;
@ -382,7 +382,7 @@ static uint8_t footpath_element_dest_in_dir(TileCoordsXYZ loc, Direction chosenD
break;
case TileElementType::Path:
{
if (!IsValidPathZAndDirection(tileElement, loc.z, chosenDirection))
if (!GuestPathfinding::IsValidPathZAndDirection(tileElement, loc.z, chosenDirection))
continue;
if (tileElement->AsPath()->IsWide())
return PATH_SEARCH_WIDE;
@ -460,14 +460,14 @@ static uint8_t footpath_element_destination_in_direction(
*
* rct2: 0x00695225
*/
static int32_t guest_path_find_aimless(Peep* peep, uint8_t edges)
static int32_t guest_path_find_aimless(Peep& peep, uint8_t edges)
{
if (scenario_rand() & 1)
{
// If possible go straight
if (edges & (1 << peep->PeepDirection))
if (edges & (1 << peep.PeepDirection))
{
return peep_move_one_tile(peep->PeepDirection, peep);
return peep_move_one_tile(peep.PeepDirection, peep);
}
}
@ -486,21 +486,21 @@ static int32_t guest_path_find_aimless(Peep* peep, uint8_t edges)
*
* rct2: 0x0069A60A
*/
static uint8_t peep_pathfind_get_max_number_junctions(Peep* peep)
static uint8_t peep_pathfind_get_max_number_junctions(Peep& peep)
{
if (peep->Is<Staff>())
if (peep.Is<Staff>())
return 8;
// PEEP_FLAGS_2? It's cleared here but not set anywhere!
if ((peep->PeepFlags & PEEP_FLAGS_2))
if ((peep.PeepFlags & PEEP_FLAGS_2))
{
if ((scenario_rand() & 0xFFFF) <= 7281)
peep->PeepFlags &= ~PEEP_FLAGS_2;
peep.PeepFlags &= ~PEEP_FLAGS_2;
return 8;
}
auto* guest = peep->As<Guest>();
auto* guest = peep.As<Guest>();
if (guest == nullptr)
return 8;
@ -687,7 +687,7 @@ static constexpr const char* pathSearchToString(uint8_t pathFindSearchResult)
* rct2: 0x0069A997
*/
static void peep_pathfind_heuristic_search(
TileCoordsXYZ loc, Peep* peep, TileElement* currentTileElement, bool inPatrolArea, uint8_t counter, uint16_t* endScore,
TileCoordsXYZ loc, Peep& peep, TileElement* currentTileElement, bool inPatrolArea, uint8_t counter, uint16_t* endScore,
Direction test_edge, uint8_t* endJunctions, TileCoordsXYZ junctionList[16], uint8_t directionList[16],
TileCoordsXYZ* endXYZ, uint8_t* endSteps)
{
@ -696,7 +696,7 @@ static void peep_pathfind_heuristic_search(
bool currentElementIsWide = currentTileElement->AsPath()->IsWide();
if (currentElementIsWide)
{
const Staff* staff = peep->As<Staff>();
const Staff* staff = peep.As<Staff>();
if (staff != nullptr && staff->CanIgnoreWideFlag(loc.ToCoordsXYZ(), currentTileElement))
currentElementIsWide = false;
}
@ -721,7 +721,7 @@ static void peep_pathfind_heuristic_search(
}
bool nextInPatrolArea = inPatrolArea;
auto* staff = peep->As<Staff>();
auto* staff = peep.As<Staff>();
if (staff != nullptr && staff->IsMechanic())
{
nextInPatrolArea = staff->IsLocationInPatrol(loc.ToCoordsXY());
@ -824,7 +824,7 @@ static void peep_pathfind_heuristic_search(
* queue path.
* Otherwise, peeps walk on path tiles to get to the goal. */
if (!IsValidPathZAndDirection(tileElement, loc.z, test_edge))
if (!GuestPathfinding::IsValidPathZAndDirection(tileElement, loc.z, test_edge))
continue;
// Path may be sloped, so set z to path base height.
@ -1075,14 +1075,14 @@ static void peep_pathfind_heuristic_search(
/* First check if going through the junction would be
* a loop. If so, the current search path ends here.
* Path finding loop detection can take advantage of both the
* peep->PathfindHistory - loops through remembered junctions
* peep.PathfindHistory - loops through remembered junctions
* the peep has already passed through getting to its
* current position while on the way to its current goal;
* _peepPathFindHistory - loops in the current search path. */
bool pathLoop = false;
/* Check the peep->PathfindHistory to see if this junction has
/* Check the peep.PathfindHistory to see if this junction has
* already been visited by the peep while heading for this goal. */
for (auto& pathfindHistory : peep->PathfindHistory)
for (auto& pathfindHistory : peep.PathfindHistory)
{
if (pathfindHistory == loc)
{
@ -1261,7 +1261,7 @@ static void peep_pathfind_heuristic_search(
*
* rct2: 0x0069A5F0
*/
Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
Direction OriginalPathfinding::ChooseDirection(const TileCoordsXYZ& loc, Peep& peep)
{
PROFILED_FUNCTION();
@ -1270,9 +1270,9 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
/* The max number of tiles to check - a whole-search limit.
* Mainly to limit the performance impact of the path finding. */
int32_t maxTilesChecked = (peep->Is<Staff>()) ? 50000 : 15000;
int32_t maxTilesChecked = (peep.Is<Staff>()) ? 50000 : 15000;
// Used to allow walking through no entry banners
_peepPathFindIsStaff = peep->Is<Staff>();
_peepPathFindIsStaff = peep.Is<Staff>();
TileCoordsXYZ goal = gPeepPathFindGoalPosition;
@ -1322,7 +1322,7 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
}
/* Check if this path element is a thin junction.
* Only 'thin' junctions are remembered in peep->PathfindHistory.
* Only 'thin' junctions are remembered in peep.PathfindHistory.
* NO attempt is made to merge the overlaid path elements and
* check if the combination is 'thin'!
* The junction is considered 'thin' simply if any of the
@ -1338,9 +1338,9 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
permitted_edges &= 0xF;
uint8_t edges = permitted_edges;
if (isThin && peep->PathfindGoal == goal)
if (isThin && peep.PathfindGoal == goal)
{
/* Use of peep->PathfindHistory[]:
/* Use of peep.PathfindHistory[]:
* When walking to a goal, the peep PathfindHistory stores
* the last 4 thin junctions that the peep walked through.
* For each of these 4 thin junctions the peep remembers
@ -1355,7 +1355,7 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
/* If the peep remembers walking through this junction
* previously while heading for its goal, retrieve the
* directions it has not yet tried. */
for (auto& pathfindHistory : peep->PathfindHistory)
for (auto& pathfindHistory : peep.PathfindHistory)
{
if (pathfindHistory == loc)
{
@ -1405,15 +1405,15 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
/* If this is a new goal for the peep. Store it and reset the peep's
* PathfindHistory. */
if (!direction_valid(peep->PathfindGoal.direction) || peep->PathfindGoal != goal)
if (!direction_valid(peep.PathfindGoal.direction) || peep.PathfindGoal != goal)
{
peep->PathfindGoal = { goal, 0 };
peep.PathfindGoal = { goal, 0 };
// Clear pathfinding history
TileCoordsXYZD nullPos;
nullPos.SetNull();
std::fill(std::begin(peep->PathfindHistory), std::end(peep->PathfindHistory), nullPos);
std::fill(std::begin(peep.PathfindHistory), std::end(peep.PathfindHistory), nullPos);
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
@ -1504,13 +1504,13 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
uint8_t endDirectionList[16] = { 0 };
bool inPatrolArea = false;
auto* staff = peep->As<Staff>();
auto* staff = peep.As<Staff>();
if (staff != nullptr && staff->IsMechanic())
{
/* Mechanics are the only staff type that
* pathfind to a destination. Determine if the
* mechanic is in their patrol area. */
inPatrolArea = staff->IsLocationInPatrol(peep->NextLoc);
inPatrolArea = staff->IsLocationInPatrol(peep.NextLoc);
}
#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2
@ -1592,20 +1592,20 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
{
for (int32_t i = 0; i < 4; ++i)
{
if (peep->PathfindHistory[i] == loc)
if (peep.PathfindHistory[i] == loc)
{
/* Peep remembers this junction, so remove the
* chosen_edge from those left to try. */
peep->PathfindHistory[i].direction &= ~(1 << chosen_edge);
peep.PathfindHistory[i].direction &= ~(1 << chosen_edge);
/* Also remove the edge through which the peep
* entered the junction from those left to try. */
peep->PathfindHistory[i].direction &= ~(1 << direction_reverse(peep->PeepDirection));
peep.PathfindHistory[i].direction &= ~(1 << direction_reverse(peep.PeepDirection));
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
log_verbose(
"Updating existing pf_history (in index: %d) for %d,%d,%d without entry edge %d & exit edge %d.", i,
loc.x, loc.y, loc.z, direction_reverse(peep->PeepDirection), chosen_edge);
loc.x, loc.y, loc.z, direction_reverse(peep.PeepDirection), chosen_edge);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
return chosen_edge;
@ -1614,20 +1614,20 @@ Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep)
/* Peep does not remember this junction, so forget a junction
* and remember this junction. */
int32_t i = peep->PathfindGoal.direction++;
peep->PathfindGoal.direction &= 3;
peep->PathfindHistory[i] = { loc, permitted_edges };
int32_t i = peep.PathfindGoal.direction++;
peep.PathfindGoal.direction &= 3;
peep.PathfindHistory[i] = { loc, permitted_edges };
/* Remove the chosen_edge from those left to try. */
peep->PathfindHistory[i].direction &= ~(1 << chosen_edge);
peep.PathfindHistory[i].direction &= ~(1 << chosen_edge);
/* Also remove the edge through which the peep
* entered the junction from those left to try. */
peep->PathfindHistory[i].direction &= ~(1 << direction_reverse(peep->PeepDirection));
peep.PathfindHistory[i].direction &= ~(1 << direction_reverse(peep.PeepDirection));
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
log_verbose(
"Storing new pf_history (in index: %d) for %d,%d,%d without entry edge %d & exit edge %d.", i, loc.x, loc.y,
loc.z, direction_reverse(peep->PeepDirection), chosen_edge);
loc.z, direction_reverse(peep.PeepDirection), chosen_edge);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
}
@ -1661,10 +1661,10 @@ static std::optional<CoordsXYZ> GetNearestParkEntrance(const CoordsXY& loc)
*
* rct2: 0x006952C0
*/
static int32_t GuestPathFindParkEntranceEntering(Peep* peep, uint8_t edges)
int32_t OriginalPathfinding::GuestPathFindParkEntranceEntering(Peep& peep, uint8_t edges)
{
// Send peeps to the nearest park entrance.
auto chosenEntrance = GetNearestParkEntrance(peep->NextLoc);
auto chosenEntrance = GetNearestParkEntrance(peep.NextLoc);
// If no defined park entrances are found, walk aimlessly.
if (!chosenEntrance.has_value())
@ -1674,7 +1674,7 @@ static int32_t GuestPathFindParkEntranceEntering(Peep* peep, uint8_t edges)
gPeepPathFindIgnoreForeignQueues = true;
gPeepPathFindQueueRideIndex = RideId::GetNull();
Direction chosenDirection = peep_pathfind_choose_direction(TileCoordsXYZ{ peep->NextLoc }, peep);
Direction chosenDirection = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, peep);
if (chosenDirection == INVALID_DIRECTION)
return guest_path_find_aimless(peep, edges);
@ -1710,10 +1710,10 @@ static uint8_t get_nearest_peep_spawn_index(uint16_t x, uint16_t y)
*
* rct2: 0x0069536C
*/
static int32_t GuestPathFindPeepSpawn(Peep* peep, uint8_t edges)
int32_t OriginalPathfinding::GuestPathFindPeepSpawn(Peep& peep, uint8_t edges)
{
// Send peeps to the nearest spawn point.
uint8_t chosenSpawn = get_nearest_peep_spawn_index(peep->NextLoc.x, peep->NextLoc.y);
uint8_t chosenSpawn = get_nearest_peep_spawn_index(peep.NextLoc.x, peep.NextLoc.y);
// If no defined spawns were found, walk aimlessly.
if (chosenSpawn == 0xFF)
@ -1723,14 +1723,14 @@ static int32_t GuestPathFindPeepSpawn(Peep* peep, uint8_t edges)
Direction direction = peepSpawnLoc.direction;
gPeepPathFindGoalPosition = TileCoordsXYZ(peepSpawnLoc);
if (peepSpawnLoc.x == peep->NextLoc.x && peepSpawnLoc.y == peep->NextLoc.y)
if (peepSpawnLoc.x == peep.NextLoc.x && peepSpawnLoc.y == peep.NextLoc.y)
{
return peep_move_one_tile(direction, peep);
}
gPeepPathFindIgnoreForeignQueues = true;
gPeepPathFindQueueRideIndex = RideId::GetNull();
direction = peep_pathfind_choose_direction(TileCoordsXYZ{ peep->NextLoc }, peep);
direction = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, peep);
if (direction == INVALID_DIRECTION)
return guest_path_find_aimless(peep, edges);
@ -1741,28 +1741,28 @@ static int32_t GuestPathFindPeepSpawn(Peep* peep, uint8_t edges)
*
* rct2: 0x00695161
*/
static int32_t GuestPathFindParkEntranceLeaving(Peep* peep, uint8_t edges)
int32_t OriginalPathfinding::GuestPathFindParkEntranceLeaving(Peep& peep, uint8_t edges)
{
TileCoordsXYZ entranceGoal{};
if (peep->PeepFlags & PEEP_FLAGS_PARK_ENTRANCE_CHOSEN)
if (peep.PeepFlags & PEEP_FLAGS_PARK_ENTRANCE_CHOSEN)
{
entranceGoal = peep->PathfindGoal;
entranceGoal = peep.PathfindGoal;
auto* entranceElement = map_get_park_entrance_element_at(entranceGoal.ToCoordsXYZ(), false);
// If entrance no longer exists, choose a new one
if (entranceElement == nullptr)
{
peep->PeepFlags &= ~(PEEP_FLAGS_PARK_ENTRANCE_CHOSEN);
peep.PeepFlags &= ~(PEEP_FLAGS_PARK_ENTRANCE_CHOSEN);
}
}
if (!(peep->PeepFlags & PEEP_FLAGS_PARK_ENTRANCE_CHOSEN))
if (!(peep.PeepFlags & PEEP_FLAGS_PARK_ENTRANCE_CHOSEN))
{
auto chosenEntrance = GetNearestParkEntrance(peep->NextLoc);
auto chosenEntrance = GetNearestParkEntrance(peep.NextLoc);
if (!chosenEntrance.has_value())
return guest_path_find_aimless(peep, edges);
peep->PeepFlags |= PEEP_FLAGS_PARK_ENTRANCE_CHOSEN;
peep.PeepFlags |= PEEP_FLAGS_PARK_ENTRANCE_CHOSEN;
entranceGoal = TileCoordsXYZ(*chosenEntrance);
}
@ -1774,7 +1774,7 @@ static int32_t GuestPathFindParkEntranceLeaving(Peep* peep, uint8_t edges)
PathfindLoggingEnable(peep);
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
Direction chosenDirection = peep_pathfind_choose_direction(TileCoordsXYZ{ peep->NextLoc }, peep);
Direction chosenDirection = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, peep);
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
PathfindLoggingDisable();
@ -1942,9 +1942,9 @@ static void get_ride_queue_end(TileCoordsXYZ& loc)
* appropriate.
*/
static StationIndex guest_pathfinding_select_random_station(
const Guest* guest, int32_t numEntranceStations, BitSet<OpenRCT2::Limits::MaxStationsPerRide>& entranceStations)
const Guest& guest, int32_t numEntranceStations, BitSet<OpenRCT2::Limits::MaxStationsPerRide>& entranceStations)
{
int32_t select = guest->GuestNumRides % numEntranceStations;
int32_t select = guest.GuestNumRides % numEntranceStations;
while (select > 0)
{
for (StationIndex::UnderlyingType i = 0; i < OpenRCT2::Limits::MaxStationsPerRide; i++)
@ -1971,22 +1971,22 @@ static StationIndex guest_pathfinding_select_random_station(
*
* rct2: 0x00694C35
*/
int32_t guest_path_finding(Guest* peep)
int32_t OriginalPathfinding::CalculateNextDestination(Guest& peep)
{
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
PathfindLoggingEnable(peep);
if (_pathFindDebug)
{
log_info("Starting guest_path_finding for %s", _pathFindDebugPeepName);
log_info("Starting CalculateNextDestination for %s", _pathFindDebugPeepName);
}
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (peep->GetNextIsSurface())
if (peep.GetNextIsSurface())
{
return guest_surface_path_finding(peep);
}
TileCoordsXYZ loc{ peep->NextLoc };
TileCoordsXYZ loc{ peep.NextLoc };
auto* pathElement = map_get_path_element_at(loc);
if (pathElement == nullptr)
@ -2002,7 +2002,7 @@ int32_t guest_path_finding(Guest* peep)
return guest_surface_path_finding(peep);
}
if (!peep->OutsideOfPark && peep->HeadingForRideOrParkExit())
if (!peep.OutsideOfPark && peep.HeadingForRideOrParkExit())
{
/* If this tileElement is adjacent to any non-wide paths,
* remove all of the edges to wide paths. */
@ -2024,14 +2024,14 @@ int32_t guest_path_finding(Guest* peep)
edges = adjustedEdges;
}
int32_t direction = direction_reverse(peep->PeepDirection);
int32_t direction = direction_reverse(peep.PeepDirection);
// Check if in a dead end (i.e. only edge is where the peep came from)
if (!(edges & ~(1 << direction)))
{
// In a dead end. Check if peep is lost, etc.
peep->CheckIfLost();
peep->CheckCantFindRide();
peep->CheckCantFindExit();
peep.CheckIfLost();
peep.CheckCantFindRide();
peep.CheckCantFindExit();
}
else
{
@ -2048,7 +2048,7 @@ int32_t guest_path_finding(Guest* peep)
if (_pathFindDebug)
{
log_info(
"Completed guest_path_finding for %s - taking only direction available: %d.", _pathFindDebugPeepName,
"Completed CalculateNextDestination for %s - taking only direction available: %d.", _pathFindDebugPeepName,
direction);
}
PathfindLoggingDisable();
@ -2060,16 +2060,16 @@ int32_t guest_path_finding(Guest* peep)
// Peep is outside the park.
// loc_694F19:
if (peep->OutsideOfPark)
if (peep.OutsideOfPark)
{
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
log_info("Completed guest_path_finding for %s - peep is outside the park.", _pathFindDebugPeepName);
log_info("Completed CalculateNextDestination for %s - peep is outside the park.", _pathFindDebugPeepName);
}
PathfindLoggingDisable();
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
switch (peep->State)
switch (peep.State)
{
case PeepState::EnteringPark:
return GuestPathFindParkEntranceEntering(peep, edges);
@ -2086,7 +2086,7 @@ int32_t guest_path_finding(Guest* peep)
* In principle, peeps with food are not paying as much attention to
* where they are going and are consequently more like to walk up
* dead end paths, paths to ride exits, etc. */
if (!peep->HasFoodOrDrink() && (scenario_rand() & 0xFFFF) >= 2184)
if (!peep.HasFoodOrDrink() && (scenario_rand() & 0xFFFF) >= 2184)
{
uint8_t adjustedEdges = edges;
for (Direction chosenDirection : ALL_DIRECTIONS)
@ -2113,41 +2113,41 @@ int32_t guest_path_finding(Guest* peep)
/* If there are still multiple directions to choose from,
* peeps with maps will randomly read the map: probability of doing so
* is much higher when heading for a ride or the park exit. */
if (peep->HasItem(ShopItem::Map))
if (peep.HasItem(ShopItem::Map))
{
// If at least 2 directions consult map
if (bitcount(edges) >= 2)
{
uint16_t probability = 1638;
if (peep->HeadingForRideOrParkExit())
if (peep.HeadingForRideOrParkExit())
{
probability = 9362;
}
if ((scenario_rand() & 0xFFFF) < probability)
{
peep->ReadMap();
peep.ReadMap();
}
}
}
if (peep->PeepFlags & PEEP_FLAGS_LEAVING_PARK)
if (peep.PeepFlags & PEEP_FLAGS_LEAVING_PARK)
{
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
log_info("Completed guest_path_finding for %s - peep is leaving the park.", _pathFindDebugPeepName);
log_info("Completed CalculateNextDestination for %s - peep is leaving the park.", _pathFindDebugPeepName);
}
PathfindLoggingDisable();
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
return GuestPathFindParkEntranceLeaving(peep, edges);
}
if (peep->GuestHeadingToRideId.IsNull())
if (peep.GuestHeadingToRideId.IsNull())
{
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
log_info("Completed guest_path_finding for %s - peep is aimless.", _pathFindDebugPeepName);
log_info("Completed CalculateNextDestination for %s - peep is aimless.", _pathFindDebugPeepName);
}
PathfindLoggingDisable();
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
@ -2155,7 +2155,7 @@ int32_t guest_path_finding(Guest* peep)
}
// Peep is heading for a ride.
RideId rideIndex = peep->GuestHeadingToRideId;
RideId rideIndex = peep.GuestHeadingToRideId;
auto ride = get_ride(rideIndex);
if (ride == nullptr || ride->status != RideStatus::Open)
{
@ -2163,7 +2163,8 @@ int32_t guest_path_finding(Guest* peep)
if (_pathFindDebug)
{
log_info(
"Completed guest_path_finding for %s - peep is heading to closed ride == aimless.", _pathFindDebugPeepName);
"Completed CalculateNextDestination for %s - peep is heading to closed ride == aimless.",
_pathFindDebugPeepName);
}
PathfindLoggingDisable();
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
@ -2194,7 +2195,7 @@ int32_t guest_path_finding(Guest* peep)
entranceStations[stationIndex.ToUnderlying()] = true;
TileCoordsXYZD entranceLocation = station.Entrance;
auto score = CalculateHeuristicPathingScore(entranceLocation, TileCoordsXYZ{ peep->NextLoc });
auto score = CalculateHeuristicPathingScore(entranceLocation, TileCoordsXYZ{ peep.NextLoc });
if (score < bestScore)
{
bestScore = score;
@ -2234,22 +2235,23 @@ int32_t guest_path_finding(Guest* peep)
gPeepPathFindGoalPosition = loc;
gPeepPathFindIgnoreForeignQueues = true;
direction = peep_pathfind_choose_direction(TileCoordsXYZ{ peep->NextLoc }, peep);
direction = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, peep);
if (direction == INVALID_DIRECTION)
{
/* Heuristic search failed for all directions.
* Reset the PathfindGoal - this means that the PathfindHistory
* will be reset in the next call to peep_pathfind_choose_direction().
* will be reset in the next call to ChooseDirection().
* This lets the heuristic search "try again" in case the player has
* edited the path layout or the mechanic was already stuck in the
* save game (e.g. with a worse version of the pathfinding). */
peep->ResetPathfindGoal();
peep.ResetPathfindGoal();
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
log_info("Completed guest_path_finding for %s - failed to choose a direction == aimless.", _pathFindDebugPeepName);
log_info(
"Completed CalculateNextDestination for %s - failed to choose a direction == aimless.", _pathFindDebugPeepName);
}
PathfindLoggingDisable();
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
@ -2259,14 +2261,14 @@ int32_t guest_path_finding(Guest* peep)
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
if (_pathFindDebug)
{
log_info("Completed guest_path_finding for %s - direction chosen: %d.", _pathFindDebugPeepName, direction);
log_info("Completed CalculateNextDestination for %s - direction chosen: %d.", _pathFindDebugPeepName, direction);
}
PathfindLoggingDisable();
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
return peep_move_one_tile(direction, peep);
}
bool IsValidPathZAndDirection(TileElement* tileElement, int32_t currentZ, int32_t currentDirection)
bool GuestPathfinding::IsValidPathZAndDirection(TileElement* tileElement, int32_t currentZ, int32_t currentDirection)
{
if (tileElement->AsPath()->IsSloped())
{
@ -2311,18 +2313,18 @@ void Peep::ResetPathfindGoal()
}
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
void PathfindLoggingEnable([[maybe_unused]] Peep* peep)
void PathfindLoggingEnable([[maybe_unused]] Peep& peep)
{
# if defined(PATHFIND_DEBUG) && PATHFIND_DEBUG
/* Determine if the pathfinding debugging is wanted for this peep. */
format_string(gPathFindDebugPeepName, sizeof(gPathFindDebugPeepName), peep->name_string_idx, &(peep->Id));
format_string(gPathFindDebugPeepName, sizeof(gPathFindDebugPeepName), peep.name_string_idx, &(peep.Id));
/* For guests, use the existing PEEP_FLAGS_TRACKING flag to
* determine for which guest(s) the pathfinding debugging will
* be output for. */
if (peep->type == PEEP_TYPE_GUEST)
if (peep.type == PEEP_TYPE_GUEST)
{
gPathFindDebug = peep->PeepFlags & PEEP_FLAGS_TRACKING;
gPathFindDebug = peep.PeepFlags & PEEP_FLAGS_TRACKING;
}
/* For staff, there is no tracking button (any other similar
* suitable existing mechanism?), so fall back to a crude

View File

@ -37,19 +37,58 @@ extern RideId gPeepPathFindQueueRideIndex;
// In practice, if this is false, gPeepPathFindQueueRideIndex is always RIDE_ID_NULL.
extern bool gPeepPathFindIgnoreForeignQueues;
// Given a peep 'peep' at tile 'loc', who is trying to get to 'gPeepPathFindGoalPosition', decide
// the direction the peep should walk in from the current tile.
Direction peep_pathfind_choose_direction(const TileCoordsXYZ& loc, Peep* peep);
class GuestPathfinding
{
public:
virtual ~GuestPathfinding() = default;
// Test whether the given tile can be walked onto, if the peep is currently at height currentZ and
// moving in direction currentDirection.
bool IsValidPathZAndDirection(TileElement* tileElement, int32_t currentZ, int32_t currentDirection);
/**
* Given a peep 'peep' at tile 'loc', who is trying to get to 'gPeepPathFindGoalPosition', decide the direction the peep
* should walk in from the current tile.
*
* @param loc Reference to the peep's current tile location
* @param peep Reference to the current peep struct
* @return The direction the peep should walk in
*/
virtual Direction ChooseDirection(const TileCoordsXYZ& loc, Peep& peep) = 0;
// Overall guest pathfinding AI. Sets up Peep::DestinationX/DestinationY (which they move to in a
// straight line, no pathfinding). Called whenever the guest has arrived at their previously set destination.
//
// Returns 0 if the guest has successfully had a new destination set up, nonzero otherwise.
int32_t guest_path_finding(Guest* peep);
/**
* Test whether the given tile can be walked onto, if the peep is currently at height currentZ and
* moving in direction currentDirection
*
* @param tileElement A pointer to the tile that is being tested
* @param currentZ The height coord the peep is at
* @param currentDirection The direction the peep is facing in
* @return True if the given tile can be walked onto, false otherwise
*/
static bool IsValidPathZAndDirection(TileElement* tileElement, int32_t currentZ, int32_t currentDirection);
/**
* Overall guest pathfinding AI. Sets up Peep::DestinationX/DestinationY (which they move to in a
* straight line, no pathfinding). Called whenever the guest has arrived at their previously set destination.
*
* @param peep A reference to a guest struct
* @returns 0 if the guest has successfully had a new destination set up, nonzero otherwise.
*/
virtual int32_t CalculateNextDestination(Guest& peep) = 0;
};
class OriginalPathfinding : public GuestPathfinding
{
public:
Direction ChooseDirection(const TileCoordsXYZ& loc, Peep& peep) final override;
int32_t CalculateNextDestination(Guest& peep) final override;
private:
int32_t GuestPathFindParkEntranceEntering(Peep& peep, uint8_t edges);
int32_t GuestPathFindPeepSpawn(Peep& peep, uint8_t edges);
int32_t GuestPathFindParkEntranceLeaving(Peep& peep, uint8_t edges);
};
extern std::unique_ptr<GuestPathfinding> gGuestPathfinder;
#if defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1
# define PATHFIND_DEBUG \
@ -60,6 +99,6 @@ int32_t guest_path_finding(Guest* peep);
// The following calls configure debug logging for the given peep
// If they're a guest, pathfinding will be logged if they have PEEP_FLAGS_TRACKING set
// If they're staff, pathfinding will be logged if their name is "Mechanic Debug"
void PathfindLoggingEnable(Peep* peep);
void PathfindLoggingEnable(Peep& peep);
void PathfindLoggingDisable();
#endif // defined(DEBUG_LEVEL_1) && DEBUG_LEVEL_1

View File

@ -84,7 +84,7 @@ protected:
// Pick the direction the peep should initially move in, given the goal position.
// This will also store the goal position and initialize pathfinding data for the peep.
gPeepPathFindGoalPosition = goal;
const Direction moveDir = peep_pathfind_choose_direction(*pos, peep);
const Direction moveDir = gGuestPathfinder->ChooseDirection(*pos, *peep);
if (moveDir == INVALID_DIRECTION)
{
// Couldn't determine a direction to move off in