mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #12573 from NathanIkola/Fix12334
Refactor footpath_is_connected_to_map_edge
This commit is contained in:
commit
a2de9cfc70
|
@ -1296,148 +1296,210 @@ static bool get_next_direction(int32_t edges, int32_t* direction)
|
||||||
* (1 << 5): Unown
|
* (1 << 5): Unown
|
||||||
* (1 << 7): Ignore no entry signs
|
* (1 << 7): Ignore no entry signs
|
||||||
*/
|
*/
|
||||||
static int32_t footpath_is_connected_to_map_edge_recurse(
|
static int32_t footpath_is_connected_to_map_edge_helper(CoordsXYZ footpathPos, int32_t direction, int32_t flags)
|
||||||
const CoordsXYZ& footpathPos, int32_t direction, int32_t flags, int32_t level, int32_t distanceFromJunction,
|
|
||||||
int32_t junctionTolerance)
|
|
||||||
{
|
{
|
||||||
TileElement* tileElement;
|
int32_t returnVal = FOOTPATH_SEARCH_INCOMPLETE;
|
||||||
|
|
||||||
|
struct TileState
|
||||||
|
{
|
||||||
|
bool processed = false;
|
||||||
|
CoordsXYZ footpathPos;
|
||||||
|
int32_t direction;
|
||||||
|
int32_t level;
|
||||||
|
int32_t distanceFromJunction;
|
||||||
|
int32_t junctionTolerance;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Vector of all of the child tile elements for us to explore
|
||||||
|
std::vector<TileState> tiles;
|
||||||
|
TileElement* tileElement = nullptr;
|
||||||
|
int numPendingTiles = 0;
|
||||||
|
|
||||||
|
TileState currentTile = { false, footpathPos, direction, 0, 0, 16 };
|
||||||
|
|
||||||
|
// Captures the current state of the variables and stores them in tiles vector for iteration later
|
||||||
|
auto CaptureCurrentTileState = [&tiles, &numPendingTiles](TileState t_currentTile) -> void {
|
||||||
|
// Search for an entry of this tile in our list already
|
||||||
|
for (const TileState& tile : tiles)
|
||||||
|
{
|
||||||
|
if (tile.footpathPos == t_currentTile.footpathPos && tile.direction == t_currentTile.direction)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here we did not find it, so insert the tile into our list
|
||||||
|
tiles.push_back(t_currentTile);
|
||||||
|
++numPendingTiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loads the next tile to visit into our variables
|
||||||
|
auto LoadNextTileElement = [&tiles, &numPendingTiles](TileState& t_currentTile) -> void {
|
||||||
|
// Do not continue if there are no tiles in the list
|
||||||
|
if (tiles.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find the next unprocessed tile
|
||||||
|
for (size_t tileIndex = tiles.size() - 1; tileIndex > 0; --tileIndex)
|
||||||
|
{
|
||||||
|
if (tiles[tileIndex].processed)
|
||||||
|
continue;
|
||||||
|
--numPendingTiles;
|
||||||
|
t_currentTile = tiles[tileIndex];
|
||||||
|
tiles[tileIndex].processed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Default to tile 0
|
||||||
|
--numPendingTiles;
|
||||||
|
t_currentTile = tiles[0];
|
||||||
|
tiles[0].processed = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Encapsulate the tile skipping logic to make do-while more readable
|
||||||
|
auto SkipTileElement = [](int32_t ste_flags, TileElement* ste_tileElement, int32_t& ste_slopeDirection,
|
||||||
|
int32_t ste_direction, const CoordsXYZ& ste_targetPos) {
|
||||||
|
if (ste_tileElement->GetType() != TILE_ELEMENT_TYPE_PATH)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (ste_tileElement->AsPath()->IsSloped()
|
||||||
|
&& (ste_slopeDirection = ste_tileElement->AsPath()->GetSlopeDirection()) != ste_direction)
|
||||||
|
{
|
||||||
|
if (direction_reverse(ste_slopeDirection) != ste_direction)
|
||||||
|
return true;
|
||||||
|
if (ste_tileElement->GetBaseZ() + PATH_HEIGHT_STEP != ste_targetPos.z)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (ste_tileElement->GetBaseZ() != ste_targetPos.z)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!(ste_flags & FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_QUEUES))
|
||||||
|
if (ste_tileElement->AsPath()->IsQueue())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
int32_t edges, slopeDirection;
|
int32_t edges, slopeDirection;
|
||||||
|
|
||||||
auto targetPos = CoordsXYZ{ CoordsXY{ footpathPos } + CoordsDirectionDelta[direction], footpathPos.z };
|
// Capture the current tile state to begin the loop
|
||||||
if (++level > 250)
|
CaptureCurrentTileState(currentTile);
|
||||||
return FOOTPATH_SEARCH_TOO_COMPLEX;
|
|
||||||
|
|
||||||
// Check if we are at edge of map
|
// Loop on this until all tiles are processed or we return
|
||||||
if (targetPos.x < COORDS_XY_STEP || targetPos.y < COORDS_XY_STEP)
|
while (numPendingTiles > 0)
|
||||||
return FOOTPATH_SEARCH_SUCCESS;
|
|
||||||
if (targetPos.x >= gMapSizeUnits || targetPos.y >= gMapSizeUnits)
|
|
||||||
return FOOTPATH_SEARCH_SUCCESS;
|
|
||||||
|
|
||||||
tileElement = map_get_first_element_at(targetPos);
|
|
||||||
if (tileElement == nullptr)
|
|
||||||
return level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : FOOTPATH_SEARCH_INCOMPLETE;
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH)
|
LoadNextTileElement(currentTile);
|
||||||
continue;
|
|
||||||
|
|
||||||
if (tileElement->AsPath()->IsSloped() && (slopeDirection = tileElement->AsPath()->GetSlopeDirection()) != direction)
|
CoordsXYZ targetPos = CoordsXYZ{ CoordsXY{ currentTile.footpathPos } + CoordsDirectionDelta[currentTile.direction],
|
||||||
{
|
currentTile.footpathPos.z };
|
||||||
if (direction_reverse(slopeDirection) != direction)
|
|
||||||
continue;
|
|
||||||
if (tileElement->GetBaseZ() + PATH_HEIGHT_STEP != targetPos.z)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (tileElement->GetBaseZ() != targetPos.z)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(flags & FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_QUEUES))
|
if (++currentTile.level > 250)
|
||||||
{
|
|
||||||
if (tileElement->AsPath()->IsQueue())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN)
|
|
||||||
{
|
|
||||||
footpath_fix_ownership(targetPos);
|
|
||||||
}
|
|
||||||
edges = tileElement->AsPath()->GetEdges();
|
|
||||||
direction = direction_reverse(direction);
|
|
||||||
if (!(flags & FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_NO_ENTRY))
|
|
||||||
{
|
|
||||||
if (tileElement[1].GetType() == TILE_ELEMENT_TYPE_BANNER)
|
|
||||||
{
|
|
||||||
for (int32_t i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
if ((&tileElement[i - 1])->IsLastForTile())
|
|
||||||
break;
|
|
||||||
if (tileElement[i].GetType() != TILE_ELEMENT_TYPE_BANNER)
|
|
||||||
break;
|
|
||||||
edges &= tileElement[i].AsBanner()->GetAllowedEdges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tileElement[2].GetType() == TILE_ELEMENT_TYPE_BANNER && tileElement[1].GetType() != TILE_ELEMENT_TYPE_PATH)
|
|
||||||
{
|
|
||||||
for (int32_t i = 1; i < 6; i++)
|
|
||||||
{
|
|
||||||
if ((&tileElement[i - 1])->IsLastForTile())
|
|
||||||
break;
|
|
||||||
if (tileElement[i].GetType() != TILE_ELEMENT_TYPE_BANNER)
|
|
||||||
break;
|
|
||||||
edges &= tileElement[i].AsBanner()->GetAllowedEdges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
goto searchFromFootpath;
|
|
||||||
} while (!(tileElement++)->IsLastForTile());
|
|
||||||
return level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : FOOTPATH_SEARCH_INCOMPLETE;
|
|
||||||
|
|
||||||
searchFromFootpath:
|
|
||||||
// Exclude direction we came from
|
|
||||||
targetPos.z = tileElement->GetBaseZ();
|
|
||||||
edges &= ~(1 << direction);
|
|
||||||
|
|
||||||
// Find next direction to go
|
|
||||||
int32_t newDirection{};
|
|
||||||
if (!get_next_direction(edges, &newDirection))
|
|
||||||
{
|
|
||||||
return FOOTPATH_SEARCH_INCOMPLETE;
|
|
||||||
}
|
|
||||||
direction = newDirection;
|
|
||||||
|
|
||||||
edges &= ~(1 << direction);
|
|
||||||
if (edges == 0)
|
|
||||||
{
|
|
||||||
// Only possible direction to go
|
|
||||||
if (tileElement->AsPath()->IsSloped() && tileElement->AsPath()->GetSlopeDirection() == direction)
|
|
||||||
{
|
|
||||||
targetPos.z += PATH_HEIGHT_STEP;
|
|
||||||
}
|
|
||||||
return footpath_is_connected_to_map_edge_recurse(
|
|
||||||
targetPos, direction, flags, level, distanceFromJunction + 1, junctionTolerance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We have reached a junction
|
|
||||||
if (distanceFromJunction != 0)
|
|
||||||
{
|
|
||||||
junctionTolerance--;
|
|
||||||
}
|
|
||||||
junctionTolerance--;
|
|
||||||
if (junctionTolerance < 0)
|
|
||||||
{
|
|
||||||
return FOOTPATH_SEARCH_TOO_COMPLEX;
|
return FOOTPATH_SEARCH_TOO_COMPLEX;
|
||||||
|
|
||||||
|
// Return immediately if we are at the edge of the map and not unowning
|
||||||
|
// Or if we are unowning and have no tiles left
|
||||||
|
if ((map_is_edge(targetPos) && !(flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN)))
|
||||||
|
{
|
||||||
|
return FOOTPATH_SEARCH_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tileElement = map_get_first_element_at(targetPos);
|
||||||
|
if (tileElement == nullptr)
|
||||||
|
return currentTile.level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : FOOTPATH_SEARCH_INCOMPLETE;
|
||||||
|
|
||||||
|
// Loop while there are unvisited TileElements at targetPos
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
direction = newDirection;
|
if (SkipTileElement(flags, tileElement, slopeDirection, currentTile.direction, targetPos))
|
||||||
edges &= ~(1 << direction);
|
continue;
|
||||||
if (tileElement->AsPath()->IsSloped() && tileElement->AsPath()->GetSlopeDirection() == direction)
|
|
||||||
{
|
|
||||||
targetPos.z += PATH_HEIGHT_STEP;
|
|
||||||
}
|
|
||||||
int32_t result = footpath_is_connected_to_map_edge_recurse(
|
|
||||||
targetPos, direction, flags, level, 0, junctionTolerance);
|
|
||||||
if (result == FOOTPATH_SEARCH_SUCCESS)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} while (get_next_direction(edges, &newDirection));
|
|
||||||
|
|
||||||
return FOOTPATH_SEARCH_INCOMPLETE;
|
// Unown the footpath if needed
|
||||||
|
if (flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN)
|
||||||
|
footpath_fix_ownership(targetPos);
|
||||||
|
|
||||||
|
edges = tileElement->AsPath()->GetEdges();
|
||||||
|
currentTile.direction = direction_reverse(currentTile.direction);
|
||||||
|
if (!(flags & FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_NO_ENTRY))
|
||||||
|
{
|
||||||
|
int elementIndex = 1;
|
||||||
|
// Loop over all elements and cull appropriate edges
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (tileElement[elementIndex].GetType() == TILE_ELEMENT_TYPE_PATH)
|
||||||
|
break;
|
||||||
|
if (tileElement[elementIndex].GetType() != TILE_ELEMENT_TYPE_BANNER)
|
||||||
|
{
|
||||||
|
++elementIndex;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
edges &= tileElement[elementIndex].AsBanner()->GetAllowedEdges();
|
||||||
|
++elementIndex;
|
||||||
|
} while (!tileElement[elementIndex].IsLastForTile());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude the direction we came from
|
||||||
|
targetPos.z = tileElement->GetBaseZ();
|
||||||
|
edges &= ~(1 << currentTile.direction);
|
||||||
|
|
||||||
|
if (!get_next_direction(edges, ¤tTile.direction))
|
||||||
|
break;
|
||||||
|
|
||||||
|
edges &= ~(1 << currentTile.direction);
|
||||||
|
if (edges == 0)
|
||||||
|
{
|
||||||
|
// Only possible direction to go
|
||||||
|
if (tileElement->AsPath()->IsSloped() && tileElement->AsPath()->GetSlopeDirection() == currentTile.direction)
|
||||||
|
targetPos.z += PATH_HEIGHT_STEP;
|
||||||
|
|
||||||
|
// Prepare the next iteration
|
||||||
|
currentTile.footpathPos = targetPos;
|
||||||
|
++currentTile.distanceFromJunction;
|
||||||
|
CaptureCurrentTileState(currentTile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We have reached a junction
|
||||||
|
--currentTile.junctionTolerance;
|
||||||
|
if (currentTile.distanceFromJunction != 0)
|
||||||
|
{
|
||||||
|
--currentTile.junctionTolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTile.junctionTolerance < 0 && !(flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN))
|
||||||
|
{
|
||||||
|
returnVal = FOOTPATH_SEARCH_TOO_COMPLEX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop until there are no more directions we can go
|
||||||
|
do
|
||||||
|
{
|
||||||
|
edges &= ~(1 << currentTile.direction);
|
||||||
|
if (tileElement->AsPath()->IsSloped()
|
||||||
|
&& tileElement->AsPath()->GetSlopeDirection() == currentTile.direction)
|
||||||
|
{
|
||||||
|
targetPos.z += PATH_HEIGHT_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add each possible path to the list of pending tiles
|
||||||
|
currentTile.footpathPos = targetPos;
|
||||||
|
currentTile.distanceFromJunction = 0;
|
||||||
|
CaptureCurrentTileState(currentTile);
|
||||||
|
} while (get_next_direction(edges, ¤tTile.direction));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} while (!(tileElement++)->IsLastForTile());
|
||||||
|
|
||||||
|
// Return success if we have unowned all tiles in our pending list
|
||||||
|
if ((flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN) && numPendingTiles <= 0)
|
||||||
|
{
|
||||||
|
return FOOTPATH_SEARCH_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return currentTile.level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : returnVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use GAME_COMMAND_FLAGS
|
// TODO: Use GAME_COMMAND_FLAGS
|
||||||
int32_t footpath_is_connected_to_map_edge(const CoordsXYZ& footpathPos, int32_t direction, int32_t flags)
|
int32_t footpath_is_connected_to_map_edge(const CoordsXYZ& footpathPos, int32_t direction, int32_t flags)
|
||||||
{
|
{
|
||||||
flags |= FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_QUEUES;
|
flags |= FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_QUEUES;
|
||||||
return footpath_is_connected_to_map_edge_recurse(footpathPos, direction, flags, 0, 0, 16);
|
return footpath_is_connected_to_map_edge_helper(footpathPos, direction, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PathElement::IsSloped() const
|
bool PathElement::IsSloped() const
|
||||||
|
|
Loading…
Reference in New Issue