|
|
|
|
@@ -28,27 +28,37 @@
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
|
|
bool gPeepPathFindIgnoreForeignQueues;
|
|
|
|
|
RideId gPeepPathFindQueueRideIndex;
|
|
|
|
|
|
|
|
|
|
namespace OpenRCT2::PathFinding
|
|
|
|
|
{
|
|
|
|
|
static int8_t _peepPathFindNumJunctions;
|
|
|
|
|
static int8_t _peepPathFindMaxJunctions;
|
|
|
|
|
static int32_t _peepPathFindTilesChecked;
|
|
|
|
|
// The search limits the maximum junctions by certain conditions.
|
|
|
|
|
static constexpr uint8_t kMaxJunctionsStaff = 8;
|
|
|
|
|
static constexpr uint8_t kMaxJunctionsGuest = 5;
|
|
|
|
|
static constexpr uint8_t kMaxJunctionsGuestWithMap = 7;
|
|
|
|
|
static constexpr uint8_t kMaxJunctionsGuestLeavingPark = 7;
|
|
|
|
|
static constexpr uint8_t kMaxJunctionsGuestLeavingParkLost = 8;
|
|
|
|
|
|
|
|
|
|
// Maximum amount of junctions.
|
|
|
|
|
static constexpr uint8_t kMaxJunctions = std::max({ kMaxJunctionsStaff, kMaxJunctionsGuest, kMaxJunctionsGuestWithMap,
|
|
|
|
|
kMaxJunctionsGuestLeavingPark, kMaxJunctionsGuestLeavingParkLost });
|
|
|
|
|
|
|
|
|
|
struct PathFindingState
|
|
|
|
|
{
|
|
|
|
|
int8_t junctionCount;
|
|
|
|
|
int8_t maxJunctions;
|
|
|
|
|
int32_t countTilesChecked;
|
|
|
|
|
// TODO: Move them, those are query parameters not really state, but for now its easier to pass it down.
|
|
|
|
|
bool ignoreForeignQueues;
|
|
|
|
|
RideId queueRideIndex;
|
|
|
|
|
// A junction history for the peep path finding heuristic search.
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
TileCoordsXYZ location;
|
|
|
|
|
Direction direction;
|
|
|
|
|
} history[kMaxJunctions + 1];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int32_t GuestSurfacePathFinding(Peep& peep);
|
|
|
|
|
|
|
|
|
|
/* A junction history for the peep pathfinding heuristic search
|
|
|
|
|
* The magic number 16 is the largest value returned by
|
|
|
|
|
* PeepPathfindGetMaxNumberJunctions() which should eventually
|
|
|
|
|
* be declared properly. */
|
|
|
|
|
static struct
|
|
|
|
|
{
|
|
|
|
|
TileCoordsXYZ location;
|
|
|
|
|
Direction direction;
|
|
|
|
|
} _peepPathFindHistory[16];
|
|
|
|
|
|
|
|
|
|
enum class PathSearchResult
|
|
|
|
|
{
|
|
|
|
|
DeadEnd, // Path is a dead end, i.e. < 2 edges.
|
|
|
|
|
@@ -130,14 +140,14 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
}
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
|
|
static TileElement* GetBannerOnPath(TileElement* pathElement)
|
|
|
|
|
static const TileElement* GetBannerOnPath(const TileElement* pathElement)
|
|
|
|
|
{
|
|
|
|
|
// This is an improved version of original.
|
|
|
|
|
// That only checked for one fence in the way.
|
|
|
|
|
if (pathElement->IsLastForTile())
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
TileElement* bannerElement = pathElement + 1;
|
|
|
|
|
const TileElement* bannerElement = pathElement + 1;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
// Path on top, so no banners
|
|
|
|
|
@@ -155,11 +165,11 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32_t BannerClearPathEdges(bool ignoreBanners, PathElement* pathElement, int32_t edges)
|
|
|
|
|
static int32_t BannerClearPathEdges(bool ignoreBanners, const PathElement* pathElement, int32_t edges)
|
|
|
|
|
{
|
|
|
|
|
if (ignoreBanners)
|
|
|
|
|
return edges;
|
|
|
|
|
TileElement* bannerElement = GetBannerOnPath(reinterpret_cast<TileElement*>(pathElement));
|
|
|
|
|
const TileElement* bannerElement = GetBannerOnPath(reinterpret_cast<const TileElement*>(pathElement));
|
|
|
|
|
if (bannerElement != nullptr)
|
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
@@ -173,7 +183,7 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
/**
|
|
|
|
|
* Gets the connected edges of a path that are permitted (i.e. no 'no entry' signs)
|
|
|
|
|
*/
|
|
|
|
|
static int32_t PathGetPermittedEdges(bool ignoreBanners, PathElement* pathElement)
|
|
|
|
|
static int32_t PathGetPermittedEdges(bool ignoreBanners, const PathElement* pathElement)
|
|
|
|
|
{
|
|
|
|
|
return BannerClearPathEdges(ignoreBanners, pathElement, pathElement->GetEdgesAndCorners()) & 0x0F;
|
|
|
|
|
}
|
|
|
|
|
@@ -348,12 +358,13 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
continue;
|
|
|
|
|
if (nextTileElement->GetType() != TileElementType::Path)
|
|
|
|
|
continue;
|
|
|
|
|
if (!IsValidPathZAndDirection(nextTileElement, loc.z, chosenDirection))
|
|
|
|
|
const auto* nextPathElement = nextTileElement->AsPath();
|
|
|
|
|
if (!FootpathIsZAndDirectionValid(*nextPathElement, loc.z, chosenDirection))
|
|
|
|
|
continue;
|
|
|
|
|
if (nextTileElement->AsPath()->IsWide())
|
|
|
|
|
if (nextPathElement->IsWide())
|
|
|
|
|
return PathSearchResult::Wide;
|
|
|
|
|
// Only queue tiles that are connected to a ride are returned as ride queues.
|
|
|
|
|
if (nextTileElement->AsPath()->IsQueue() && !nextTileElement->AsPath()->GetRideIndex().IsNull())
|
|
|
|
|
if (nextPathElement->IsQueue() && !nextPathElement->GetRideIndex().IsNull())
|
|
|
|
|
return PathSearchResult::RideQueue;
|
|
|
|
|
|
|
|
|
|
return PathSearchResult::Other;
|
|
|
|
|
@@ -442,12 +453,13 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
break;
|
|
|
|
|
case TileElementType::Path:
|
|
|
|
|
{
|
|
|
|
|
if (!IsValidPathZAndDirection(tileElement, loc.z, chosenDirection))
|
|
|
|
|
const auto* pathElement = tileElement->AsPath();
|
|
|
|
|
if (!FootpathIsZAndDirectionValid(*pathElement, loc.z, chosenDirection))
|
|
|
|
|
continue;
|
|
|
|
|
if (tileElement->AsPath()->IsWide())
|
|
|
|
|
return PathSearchResult::Wide;
|
|
|
|
|
|
|
|
|
|
uint8_t edges = PathGetPermittedEdges(ignoreBanners, tileElement->AsPath());
|
|
|
|
|
uint8_t edges = PathGetPermittedEdges(ignoreBanners, pathElement);
|
|
|
|
|
edges &= ~(1 << DirectionReverse(chosenDirection));
|
|
|
|
|
loc.z = tileElement->BaseHeight;
|
|
|
|
|
|
|
|
|
|
@@ -550,24 +562,25 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
static uint8_t PeepPathfindGetMaxNumberJunctions(Peep& peep)
|
|
|
|
|
{
|
|
|
|
|
if (peep.Is<Staff>())
|
|
|
|
|
return 8;
|
|
|
|
|
return kMaxJunctionsStaff;
|
|
|
|
|
|
|
|
|
|
auto* guest = peep.As<Guest>();
|
|
|
|
|
if (guest == nullptr)
|
|
|
|
|
return 8;
|
|
|
|
|
return kMaxJunctionsStaff;
|
|
|
|
|
|
|
|
|
|
if (guest->PeepFlags & PEEP_FLAGS_LEAVING_PARK && guest->GuestIsLostCountdown < 90)
|
|
|
|
|
bool isLeavingPark = (guest->PeepFlags & PEEP_FLAGS_LEAVING_PARK) != 0;
|
|
|
|
|
if (isLeavingPark && guest->GuestIsLostCountdown < 90)
|
|
|
|
|
{
|
|
|
|
|
return 8;
|
|
|
|
|
return kMaxJunctionsGuestLeavingParkLost;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isLeavingPark)
|
|
|
|
|
return kMaxJunctionsGuestLeavingPark;
|
|
|
|
|
|
|
|
|
|
if (guest->HasItem(ShopItem::Map))
|
|
|
|
|
return 7;
|
|
|
|
|
return kMaxJunctionsGuestWithMap;
|
|
|
|
|
|
|
|
|
|
if (guest->PeepFlags & PEEP_FLAGS_LEAVING_PARK)
|
|
|
|
|
return 7;
|
|
|
|
|
|
|
|
|
|
return 5;
|
|
|
|
|
return kMaxJunctionsGuest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@@ -704,9 +717,10 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
* rct2: 0x0069A997
|
|
|
|
|
*/
|
|
|
|
|
static void PeepPathfindHeuristicSearch(
|
|
|
|
|
TileCoordsXYZ loc, const TileCoordsXYZ& goal, const Peep& peep, TileElement* currentTileElement,
|
|
|
|
|
const bool inPatrolArea, uint8_t numSteps, uint16_t* endScore, Direction testEdge, uint8_t* endJunctions,
|
|
|
|
|
TileCoordsXYZ junctionList[16], uint8_t directionList[16], TileCoordsXYZ* endXYZ, uint8_t* endSteps)
|
|
|
|
|
PathFindingState& state, TileCoordsXYZ loc, const TileCoordsXYZ& goal, const Peep& peep,
|
|
|
|
|
TileElement* currentTileElement, const bool inPatrolArea, uint8_t numSteps, uint16_t* endScore, Direction testEdge,
|
|
|
|
|
uint8_t* endJunctions, TileCoordsXYZ junctionList[16], uint8_t directionList[16], TileCoordsXYZ* endXYZ,
|
|
|
|
|
uint8_t* endSteps)
|
|
|
|
|
{
|
|
|
|
|
PathSearchResult searchResult = PathSearchResult::Failed;
|
|
|
|
|
|
|
|
|
|
@@ -721,12 +735,12 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
loc += TileDirectionDelta[testEdge];
|
|
|
|
|
|
|
|
|
|
++numSteps;
|
|
|
|
|
_peepPathFindTilesChecked--;
|
|
|
|
|
state.countTilesChecked--;
|
|
|
|
|
|
|
|
|
|
/* If this is where the search started this is a search loop and the
|
|
|
|
|
* current search path ends here.
|
|
|
|
|
* Return without updating the parameters (best result so far). */
|
|
|
|
|
if (_peepPathFindHistory[0].location == loc)
|
|
|
|
|
if (state.history[0].location == loc)
|
|
|
|
|
{
|
|
|
|
|
LogPathfinding(&peep, "Return from %d,%d,%d; Steps: %u; At start", loc.x >> 5, loc.y >> 5, loc.z, numSteps);
|
|
|
|
|
return;
|
|
|
|
|
@@ -828,17 +842,17 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
break;
|
|
|
|
|
case TileElementType::Path:
|
|
|
|
|
{
|
|
|
|
|
const auto* pathElement = tileElement->AsPath();
|
|
|
|
|
/* 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 (!IsValidPathZAndDirection(tileElement, loc.z, testEdge))
|
|
|
|
|
if (!FootpathIsZAndDirectionValid(*pathElement, loc.z, testEdge))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Path may be sloped, so set z to path base height.
|
|
|
|
|
loc.z = tileElement->BaseHeight;
|
|
|
|
|
|
|
|
|
|
if (tileElement->AsPath()->IsWide())
|
|
|
|
|
if (pathElement->IsWide())
|
|
|
|
|
{
|
|
|
|
|
/* Check if staff can ignore this wide flag. */
|
|
|
|
|
if (staff == nullptr || !staff->CanIgnoreWideFlag(loc.ToCoordsXYZ(), tileElement))
|
|
|
|
|
@@ -851,7 +865,7 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
|
|
|
|
|
searchResult = PathSearchResult::Thin;
|
|
|
|
|
|
|
|
|
|
uint8_t numEdges = std::popcount(tileElement->AsPath()->GetEdges());
|
|
|
|
|
uint8_t numEdges = std::popcount(pathElement->GetEdges());
|
|
|
|
|
|
|
|
|
|
if (numEdges < 2)
|
|
|
|
|
{
|
|
|
|
|
@@ -863,15 +877,14 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ // numEdges == 2
|
|
|
|
|
if (tileElement->AsPath()->IsQueue()
|
|
|
|
|
&& tileElement->AsPath()->GetRideIndex() != gPeepPathFindQueueRideIndex)
|
|
|
|
|
if (pathElement->IsQueue() && pathElement->GetRideIndex() != state.queueRideIndex)
|
|
|
|
|
{
|
|
|
|
|
if (gPeepPathFindIgnoreForeignQueues && !tileElement->AsPath()->GetRideIndex().IsNull())
|
|
|
|
|
if (state.ignoreForeignQueues && !pathElement->GetRideIndex().IsNull())
|
|
|
|
|
{
|
|
|
|
|
// Path is a queue we aren't interested in
|
|
|
|
|
/* The rideIndex will be useful for
|
|
|
|
|
* adding transport rides later. */
|
|
|
|
|
rideIndex = tileElement->AsPath()->GetRideIndex();
|
|
|
|
|
rideIndex = pathElement->GetRideIndex();
|
|
|
|
|
searchResult = PathSearchResult::RideQueue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -910,14 +923,14 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
// Update the end x,y,z
|
|
|
|
|
*endXYZ = loc;
|
|
|
|
|
// Update the telemetry
|
|
|
|
|
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
|
|
|
|
*endJunctions = state.maxJunctions - state.junctionCount;
|
|
|
|
|
for (uint8_t junctInd = 0; junctInd < *endJunctions; junctInd++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t 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;
|
|
|
|
|
uint8_t histIdx = state.maxJunctions - junctInd;
|
|
|
|
|
junctionList[junctInd].x = state.history[histIdx].location.x;
|
|
|
|
|
junctionList[junctInd].y = state.history[histIdx].location.y;
|
|
|
|
|
junctionList[junctInd].z = state.history[histIdx].location.z;
|
|
|
|
|
directionList[junctInd] = state.history[histIdx].direction;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LogPathfinding(
|
|
|
|
|
@@ -961,14 +974,14 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
// Update the end x,y,z
|
|
|
|
|
*endXYZ = loc;
|
|
|
|
|
// Update the telemetry
|
|
|
|
|
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
|
|
|
|
*endJunctions = state.maxJunctions - state.junctionCount;
|
|
|
|
|
for (uint8_t junctInd = 0; junctInd < *endJunctions; junctInd++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t 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;
|
|
|
|
|
uint8_t histIdx = state.maxJunctions - junctInd;
|
|
|
|
|
junctionList[junctInd].x = state.history[histIdx].location.x;
|
|
|
|
|
junctionList[junctInd].y = state.history[histIdx].location.y;
|
|
|
|
|
junctionList[junctInd].z = state.history[histIdx].location.z;
|
|
|
|
|
directionList[junctInd] = state.history[histIdx].direction;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LogPathfinding(
|
|
|
|
|
@@ -1004,7 +1017,7 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
|
|
|
|
|
/* Check if either of the search limits has been reached:
|
|
|
|
|
* - max number of steps or max tiles checked. */
|
|
|
|
|
if (numSteps >= 200 || _peepPathFindTilesChecked <= 0)
|
|
|
|
|
if (numSteps >= 200 || state.countTilesChecked <= 0)
|
|
|
|
|
{
|
|
|
|
|
/* The current search ends here.
|
|
|
|
|
* The path continues, so the goal could still be reachable from here.
|
|
|
|
|
@@ -1018,14 +1031,14 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
// Update the end x,y,z
|
|
|
|
|
*endXYZ = loc;
|
|
|
|
|
// Update the telemetry
|
|
|
|
|
*endJunctions = _peepPathFindMaxJunctions - _peepPathFindNumJunctions;
|
|
|
|
|
*endJunctions = state.maxJunctions - state.junctionCount;
|
|
|
|
|
for (uint8_t junctInd = 0; junctInd < *endJunctions; junctInd++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t 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;
|
|
|
|
|
uint8_t histIdx = state.maxJunctions - junctInd;
|
|
|
|
|
junctionList[junctInd].x = state.history[histIdx].location.x;
|
|
|
|
|
junctionList[junctInd].y = state.history[histIdx].location.y;
|
|
|
|
|
junctionList[junctInd].z = state.history[histIdx].location.z;
|
|
|
|
|
directionList[junctInd] = state.history[histIdx].direction;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LogPathfinding(
|
|
|
|
|
@@ -1083,10 +1096,9 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
/* Check the _peepPathFindHistory 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. */
|
|
|
|
|
for (int32_t junctionNum = _peepPathFindNumJunctions + 1; junctionNum <= _peepPathFindMaxJunctions;
|
|
|
|
|
junctionNum++)
|
|
|
|
|
for (int32_t junctionNum = state.junctionCount + 1; junctionNum <= state.maxJunctions; junctionNum++)
|
|
|
|
|
{
|
|
|
|
|
if (_peepPathFindHistory[junctionNum].location == loc)
|
|
|
|
|
if (state.history[junctionNum].location == loc)
|
|
|
|
|
{
|
|
|
|
|
pathLoop = true;
|
|
|
|
|
break;
|
|
|
|
|
@@ -1107,7 +1119,7 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
* be reachable from here.
|
|
|
|
|
* If the search result is better than the best so far (in the parameters),
|
|
|
|
|
* then update the parameters with this search before continuing to the next map element. */
|
|
|
|
|
if (_peepPathFindNumJunctions <= 0)
|
|
|
|
|
if (state.junctionCount <= 0)
|
|
|
|
|
{
|
|
|
|
|
if (newScore < *endScore || (newScore == *endScore && numSteps < *endSteps))
|
|
|
|
|
{
|
|
|
|
|
@@ -1117,12 +1129,12 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
// Update the end x,y,z
|
|
|
|
|
*endXYZ = loc;
|
|
|
|
|
// Update the telemetry
|
|
|
|
|
*endJunctions = _peepPathFindMaxJunctions; // - _peepPathFindNumJunctions;
|
|
|
|
|
*endJunctions = state.maxJunctions; // - _peepPathFindNumJunctions;
|
|
|
|
|
for (uint8_t junctInd = 0; junctInd < *endJunctions; junctInd++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t histIdx = _peepPathFindMaxJunctions - junctInd;
|
|
|
|
|
junctionList[junctInd] = _peepPathFindHistory[histIdx].location;
|
|
|
|
|
directionList[junctInd] = _peepPathFindHistory[histIdx].direction;
|
|
|
|
|
uint8_t histIdx = state.maxJunctions - junctInd;
|
|
|
|
|
junctionList[junctInd] = state.history[histIdx].location;
|
|
|
|
|
directionList[junctInd] = state.history[histIdx].direction;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LogPathfinding(
|
|
|
|
|
@@ -1133,10 +1145,10 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
|
|
|
|
|
/* This junction was NOT previously visited in the current
|
|
|
|
|
* search path, so add the junction to the history. */
|
|
|
|
|
_peepPathFindHistory[_peepPathFindNumJunctions].location = loc;
|
|
|
|
|
state.history[state.junctionCount].location = loc;
|
|
|
|
|
// .direction take is added below.
|
|
|
|
|
|
|
|
|
|
_peepPathFindNumJunctions--;
|
|
|
|
|
state.junctionCount--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1145,7 +1157,7 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
edges &= ~(1 << nextTestEdge);
|
|
|
|
|
uint8_t savedNumJunctions = _peepPathFindNumJunctions;
|
|
|
|
|
uint8_t savedNumJunctions = state.junctionCount;
|
|
|
|
|
|
|
|
|
|
uint8_t height = loc.z;
|
|
|
|
|
if (tileElement->AsPath()->IsSloped() && tileElement->AsPath()->GetSlopeDirection() == nextTestEdge)
|
|
|
|
|
@@ -1177,13 +1189,13 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
if (isThinJunction)
|
|
|
|
|
{
|
|
|
|
|
/* Add the current test_edge to the history. */
|
|
|
|
|
_peepPathFindHistory[_peepPathFindNumJunctions + 1].direction = nextTestEdge;
|
|
|
|
|
state.history[state.junctionCount + 1].direction = nextTestEdge;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PeepPathfindHeuristicSearch(
|
|
|
|
|
{ loc.x, loc.y, height }, goal, peep, tileElement, nextInPatrolArea, numSteps, endScore, nextTestEdge,
|
|
|
|
|
endJunctions, junctionList, directionList, endXYZ, endSteps);
|
|
|
|
|
_peepPathFindNumJunctions = savedNumJunctions;
|
|
|
|
|
state, { loc.x, loc.y, height }, goal, peep, tileElement, nextInPatrolArea, numSteps, endScore,
|
|
|
|
|
nextTestEdge, endJunctions, junctionList, directionList, endXYZ, endSteps);
|
|
|
|
|
state.junctionCount = savedNumJunctions;
|
|
|
|
|
|
|
|
|
|
LogPathfinding(
|
|
|
|
|
&peep, "Returned to %d,%d,%d; Steps: %u; edge: %d; Score: %d", loc.x >> 5, loc.y >> 5, loc.z, numSteps,
|
|
|
|
|
@@ -1214,12 +1226,18 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
*
|
|
|
|
|
* rct2: 0x0069A5F0
|
|
|
|
|
*/
|
|
|
|
|
Direction ChooseDirection(const TileCoordsXYZ& loc, const TileCoordsXYZ& goal, Peep& peep)
|
|
|
|
|
Direction ChooseDirection(
|
|
|
|
|
const TileCoordsXYZ& loc, const TileCoordsXYZ& goal, Peep& peep, bool ignoreForeignQueues, RideId queueRideIndex)
|
|
|
|
|
{
|
|
|
|
|
PROFILED_FUNCTION();
|
|
|
|
|
|
|
|
|
|
PathFindingState state{};
|
|
|
|
|
|
|
|
|
|
state.ignoreForeignQueues = ignoreForeignQueues;
|
|
|
|
|
state.queueRideIndex = queueRideIndex;
|
|
|
|
|
|
|
|
|
|
// The max number of thin junctions searched - a per-search-path limit.
|
|
|
|
|
_peepPathFindMaxJunctions = PeepPathfindGetMaxNumberJunctions(peep);
|
|
|
|
|
state.maxJunctions = PeepPathfindGetMaxNumberJunctions(peep);
|
|
|
|
|
|
|
|
|
|
/* The max number of tiles to check - a whole-search limit.
|
|
|
|
|
* Mainly to limit the performance impact of the path finding. */
|
|
|
|
|
@@ -1388,12 +1406,12 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
/* Divide the maxTilesChecked global search limit
|
|
|
|
|
* between the remaining edges to ensure the search
|
|
|
|
|
* covers all of the remaining edges. */
|
|
|
|
|
_peepPathFindTilesChecked = maxTilesChecked / numEdges;
|
|
|
|
|
_peepPathFindNumJunctions = _peepPathFindMaxJunctions;
|
|
|
|
|
state.countTilesChecked = maxTilesChecked / numEdges;
|
|
|
|
|
state.junctionCount = state.maxJunctions;
|
|
|
|
|
|
|
|
|
|
// Initialise _peepPathFindHistory.
|
|
|
|
|
|
|
|
|
|
for (auto& entry : _peepPathFindHistory)
|
|
|
|
|
for (auto& entry : state.history)
|
|
|
|
|
{
|
|
|
|
|
entry.location.SetNull();
|
|
|
|
|
entry.direction = INVALID_DIRECTION;
|
|
|
|
|
@@ -1402,8 +1420,8 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
/* The pathfinding will only use elements
|
|
|
|
|
* 1.._peepPathFindMaxJunctions, so the starting point
|
|
|
|
|
* is placed in element 0 */
|
|
|
|
|
_peepPathFindHistory[0].location = loc;
|
|
|
|
|
_peepPathFindHistory[0].direction = 0xF;
|
|
|
|
|
state.history[0].location = loc;
|
|
|
|
|
state.history[0].direction = 0xF;
|
|
|
|
|
|
|
|
|
|
uint16_t score = 0xFFFF;
|
|
|
|
|
/* Variable endXYZ contains the end location of the
|
|
|
|
|
@@ -1440,8 +1458,8 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
&peep, "Pathfind searching in direction: %d from %d,%d,%d", testEdge, loc.x >> 5, loc.y >> 5, loc.z);
|
|
|
|
|
|
|
|
|
|
PeepPathfindHeuristicSearch(
|
|
|
|
|
{ loc.x, loc.y, height }, goal, peep, firstTileElement, inPatrolArea, 0, &score, testEdge, &endJunctions,
|
|
|
|
|
endJunctionList, endDirectionList, &endXYZ, &endSteps);
|
|
|
|
|
state, { loc.x, loc.y, height }, goal, peep, firstTileElement, inPatrolArea, 0, &score, testEdge,
|
|
|
|
|
&endJunctions, endJunctionList, endDirectionList, &endXYZ, &endSteps);
|
|
|
|
|
|
|
|
|
|
if constexpr (kLogPathfinding)
|
|
|
|
|
{
|
|
|
|
|
@@ -1576,11 +1594,8 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
if (!chosenEntrance.has_value())
|
|
|
|
|
return GuestPathfindAimless(peep, edges);
|
|
|
|
|
|
|
|
|
|
gPeepPathFindIgnoreForeignQueues = true;
|
|
|
|
|
gPeepPathFindQueueRideIndex = RideId::GetNull();
|
|
|
|
|
|
|
|
|
|
const auto goalPos = TileCoordsXYZ(chosenEntrance.value());
|
|
|
|
|
Direction chosenDirection = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, goalPos, peep);
|
|
|
|
|
Direction chosenDirection = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, goalPos, peep, true, RideId::GetNull());
|
|
|
|
|
|
|
|
|
|
if (chosenDirection == INVALID_DIRECTION)
|
|
|
|
|
return GuestPathfindAimless(peep, edges);
|
|
|
|
|
@@ -1633,11 +1648,8 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
return PeepMoveOneTile(direction, peep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gPeepPathFindIgnoreForeignQueues = true;
|
|
|
|
|
gPeepPathFindQueueRideIndex = RideId::GetNull();
|
|
|
|
|
|
|
|
|
|
const auto goalPos = TileCoordsXYZ(peepSpawnLoc);
|
|
|
|
|
direction = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, goalPos, peep);
|
|
|
|
|
direction = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, goalPos, peep, true, RideId::GetNull());
|
|
|
|
|
if (direction == INVALID_DIRECTION)
|
|
|
|
|
return GuestPathfindAimless(peep, edges);
|
|
|
|
|
|
|
|
|
|
@@ -1673,10 +1685,7 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
entranceGoal = TileCoordsXYZ(*chosenEntrance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gPeepPathFindIgnoreForeignQueues = true;
|
|
|
|
|
gPeepPathFindQueueRideIndex = RideId::GetNull();
|
|
|
|
|
|
|
|
|
|
Direction chosenDirection = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, entranceGoal, peep);
|
|
|
|
|
Direction chosenDirection = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, entranceGoal, peep, true, RideId::GetNull());
|
|
|
|
|
if (chosenDirection == INVALID_DIRECTION)
|
|
|
|
|
return GuestPathfindAimless(peep, edges);
|
|
|
|
|
|
|
|
|
|
@@ -2035,9 +2044,6 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
return GuestPathfindAimless(peep, edges);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The ride is open.
|
|
|
|
|
gPeepPathFindQueueRideIndex = rideIndex;
|
|
|
|
|
|
|
|
|
|
/* Find the ride's closest entrance station to the peep.
|
|
|
|
|
* At the same time, count how many entrance stations there are and
|
|
|
|
|
* which stations are entrance stations. */
|
|
|
|
|
@@ -2096,9 +2102,7 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
|
|
|
|
|
GetRideQueueEnd(loc);
|
|
|
|
|
|
|
|
|
|
gPeepPathFindIgnoreForeignQueues = true;
|
|
|
|
|
|
|
|
|
|
direction = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, loc, peep);
|
|
|
|
|
direction = ChooseDirection(TileCoordsXYZ{ peep.NextLoc }, loc, peep, true, rideIndex);
|
|
|
|
|
|
|
|
|
|
if (direction == INVALID_DIRECTION)
|
|
|
|
|
{
|
|
|
|
|
@@ -2120,31 +2124,4 @@ namespace OpenRCT2::PathFinding
|
|
|
|
|
return PeepMoveOneTile(direction, peep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsValidPathZAndDirection(TileElement* tileElement, int32_t currentZ, int32_t currentDirection)
|
|
|
|
|
{
|
|
|
|
|
if (tileElement->AsPath()->IsSloped())
|
|
|
|
|
{
|
|
|
|
|
int32_t slopeDirection = tileElement->AsPath()->GetSlopeDirection();
|
|
|
|
|
if (slopeDirection == currentDirection)
|
|
|
|
|
{
|
|
|
|
|
if (currentZ != tileElement->BaseHeight)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
slopeDirection = DirectionReverse(slopeDirection);
|
|
|
|
|
if (slopeDirection != currentDirection)
|
|
|
|
|
return false;
|
|
|
|
|
if (currentZ != tileElement->BaseHeight + 2)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (currentZ != tileElement->BaseHeight)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace OpenRCT2::PathFinding
|
|
|
|
|
|