2014-11-15 19:22:06 +01:00
|
|
|
/*****************************************************************************
|
2023-01-01 11:58:01 +01:00
|
|
|
* Copyright (c) 2014-2023 OpenRCT2 developers
|
2014-11-15 19:22:06 +01:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
2014-11-15 19:22:06 +01:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2014-11-15 19:22:06 +01:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "../Cheats.h"
|
2018-04-10 14:19:23 +02:00
|
|
|
#include "../Context.h"
|
2018-06-22 23:17:03 +02:00
|
|
|
#include "../Game.h"
|
2022-05-01 18:21:53 +02:00
|
|
|
#include "../Identifiers.h"
|
2018-06-22 23:17:03 +02:00
|
|
|
#include "../OpenRCT2.h"
|
Split actions hpp files into separate h and cpp files (#13548)
* Split up SmallSceneryPlace/Remove
Added undo function for Remove Scenery
* Refactor: Balloon and Banner actions hpp=>h/cpp
* Refactor: rename all action *.hpp files to *.cpp
This is preparation for separation in later commits. Note that without
the complete set of commits in this branch, the code will not build.
* Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp
* VSCode: add src subdirectories to includePath
* Refactor Guest actions hpp=>h/cpp
* Refactor Land actions hpp=>h/cpp
* Refactor LargeScenery actions hpp=>h/cpp
* Refactor Load, Maze, Network actions hpp=>h/cpp
* Refactor Park actions hpp=>h/cpp
* Refactor/style: move private function declarations in actions *.h
Previous action .h files included private function declarations with
private member variables, before public function declarations. This
commit re-orders the header files to the following order:
- public member variables
- private member variables
- public functions
- private functions
* Refactor Pause action hpp=>h/cpp
* Refactor Peep, Place, Player actions hpp=>h/cpp
* Refactor Ride actions hpp=>h/cpp
* Refactor Scenario, Set*, Sign* actions hpp=>h/cpp
* Refactor SmallScenerySetColourAction hpp=>h/cpp
* Refactor Staff actions hpp=>h/cpp
* Refactor Surface, Tile, Track* actions hpp=>h/cpp
* Refactor Wall and Water actions hpp=>h/cpp
* Fix various includes and other compile errors
Update includes for tests.
Move static function declarations to .h files
Add explicit includes to various files that were previously implicit
(the required header was a nested include in an action hpp file, and the
action .h file does not include that header)
Move RideSetStatus string enum to the cpp file to avoid unused imports
* Xcode: modify project file for actions refactor
* Cleanup whitespace and end-of-file newlines
Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
|
|
|
#include "../actions/FootpathPlaceAction.h"
|
|
|
|
#include "../actions/FootpathRemoveAction.h"
|
|
|
|
#include "../actions/LandSetRightsAction.h"
|
2018-03-16 20:51:37 +01:00
|
|
|
#include "../core/Guard.hpp"
|
2021-11-24 15:48:33 +01:00
|
|
|
#include "../entity/EntityList.h"
|
2021-11-24 15:58:01 +01:00
|
|
|
#include "../entity/EntityRegistry.h"
|
2020-08-02 09:32:59 +02:00
|
|
|
#include "../interface/Window_internal.h"
|
2018-01-06 18:32:25 +01:00
|
|
|
#include "../localisation/Localisation.h"
|
2018-03-19 23:28:40 +01:00
|
|
|
#include "../management/Finance.h"
|
2016-01-25 05:00:31 +01:00
|
|
|
#include "../network/network.h"
|
2023-02-15 22:35:16 +01:00
|
|
|
#include "../object/FootpathItemEntry.h"
|
2019-01-20 21:38:38 +01:00
|
|
|
#include "../object/FootpathObject.h"
|
2021-09-06 21:59:17 +02:00
|
|
|
#include "../object/FootpathRailingsObject.h"
|
|
|
|
#include "../object/FootpathSurfaceObject.h"
|
2023-02-15 22:35:16 +01:00
|
|
|
#include "../object/ObjectEntryManager.h"
|
2018-01-02 20:36:42 +01:00
|
|
|
#include "../object/ObjectList.h"
|
2018-02-11 23:09:38 +01:00
|
|
|
#include "../object/ObjectManager.h"
|
2018-03-04 17:44:42 +01:00
|
|
|
#include "../paint/VirtualFloor.h"
|
Split actions hpp files into separate h and cpp files (#13548)
* Split up SmallSceneryPlace/Remove
Added undo function for Remove Scenery
* Refactor: Balloon and Banner actions hpp=>h/cpp
* Refactor: rename all action *.hpp files to *.cpp
This is preparation for separation in later commits. Note that without
the complete set of commits in this branch, the code will not build.
* Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp
* VSCode: add src subdirectories to includePath
* Refactor Guest actions hpp=>h/cpp
* Refactor Land actions hpp=>h/cpp
* Refactor LargeScenery actions hpp=>h/cpp
* Refactor Load, Maze, Network actions hpp=>h/cpp
* Refactor Park actions hpp=>h/cpp
* Refactor/style: move private function declarations in actions *.h
Previous action .h files included private function declarations with
private member variables, before public function declarations. This
commit re-orders the header files to the following order:
- public member variables
- private member variables
- public functions
- private functions
* Refactor Pause action hpp=>h/cpp
* Refactor Peep, Place, Player actions hpp=>h/cpp
* Refactor Ride actions hpp=>h/cpp
* Refactor Scenario, Set*, Sign* actions hpp=>h/cpp
* Refactor SmallScenerySetColourAction hpp=>h/cpp
* Refactor Staff actions hpp=>h/cpp
* Refactor Surface, Tile, Track* actions hpp=>h/cpp
* Refactor Wall and Water actions hpp=>h/cpp
* Fix various includes and other compile errors
Update includes for tests.
Move static function declarations to .h files
Add explicit includes to various files that were previously implicit
(the required header was a nested include in an action hpp file, and the
action .h file does not include that header)
Move RideSetStatus string enum to the cpp file to avoid unused imports
* Xcode: modify project file for actions refactor
* Cleanup whitespace and end-of-file newlines
Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
|
|
|
#include "../ride/RideData.h"
|
2017-10-23 14:47:12 +02:00
|
|
|
#include "../ride/Station.h"
|
2017-10-17 13:51:47 +02:00
|
|
|
#include "../ride/Track.h"
|
2017-10-16 12:02:23 +02:00
|
|
|
#include "../ride/TrackData.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "../util/Util.h"
|
2022-10-17 18:08:55 +02:00
|
|
|
#include "Location.hpp"
|
2018-01-03 14:56:08 +01:00
|
|
|
#include "Map.h"
|
2018-06-22 23:17:03 +02:00
|
|
|
#include "MapAnimation.h"
|
2018-03-19 23:28:40 +01:00
|
|
|
#include "Park.h"
|
2021-11-26 16:03:14 +01:00
|
|
|
#include "Scenery.h"
|
2018-05-01 16:33:16 +02:00
|
|
|
#include "Surface.h"
|
2022-04-07 21:42:08 +02:00
|
|
|
#include "TileElement.h"
|
2014-11-15 19:22:06 +01:00
|
|
|
|
2018-08-12 13:50:40 +02:00
|
|
|
#include <algorithm>
|
2018-11-21 23:16:04 +01:00
|
|
|
#include <iterator>
|
2018-08-12 13:50:40 +02:00
|
|
|
|
2021-08-22 17:18:38 +02:00
|
|
|
using namespace OpenRCT2::TrackMetaData;
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathUpdateQueueEntranceBanner(const CoordsXY& footpathPos, TileElement* tileElement);
|
2015-02-13 21:55:49 +01:00
|
|
|
|
2021-09-03 19:19:42 +02:00
|
|
|
FootpathSelection gFootpathSelection;
|
2021-04-14 14:56:28 +02:00
|
|
|
ProvisionalFootpath gProvisionalFootpath;
|
2018-06-20 17:28:51 +02:00
|
|
|
uint16_t gFootpathSelectedId;
|
2019-12-12 11:21:05 +01:00
|
|
|
CoordsXYZ gFootpathConstructFromPosition;
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t gFootpathConstructSlope;
|
|
|
|
uint8_t gFootpathGroundFlags;
|
2016-04-15 20:16:48 +02:00
|
|
|
|
2022-01-19 14:17:11 +01:00
|
|
|
static RideId* _footpathQueueChainNext;
|
|
|
|
static RideId _footpathQueueChain[64];
|
2016-05-17 22:29:05 +02:00
|
|
|
|
2018-04-02 19:00:36 +02:00
|
|
|
// This is the coordinates that a user of the bin should move to
|
|
|
|
// rct2: 0x00992A4C
|
2023-03-28 00:16:55 +02:00
|
|
|
const std::array<CoordsXY, NumOrthogonalDirections> BinUseOffsets = {
|
|
|
|
CoordsXY{ 11, 16 },
|
2018-04-02 19:00:36 +02:00
|
|
|
{ 16, 21 },
|
|
|
|
{ 21, 16 },
|
|
|
|
{ 16, 11 },
|
|
|
|
};
|
|
|
|
|
2018-04-04 21:22:23 +02:00
|
|
|
// These are the offsets for bench positions on footpaths, 2 for each edge
|
|
|
|
// rct2: 0x00981F2C, 0x00981F2E
|
2023-03-28 00:16:55 +02:00
|
|
|
const std::array<CoordsXY, NumOrthogonalDirections* 2> BenchUseOffsets = {
|
|
|
|
CoordsXY{ 7, 12 }, { 12, 25 }, { 25, 20 }, { 20, 7 }, { 7, 20 }, { 20, 25 }, { 25, 12 }, { 12, 7 },
|
2018-04-04 21:22:23 +02:00
|
|
|
};
|
2018-04-02 19:00:36 +02:00
|
|
|
|
2016-08-14 14:12:07 +02:00
|
|
|
/** rct2: 0x00981D6C, 0x00981D6E */
|
2023-03-28 00:16:55 +02:00
|
|
|
const std::array<CoordsXY, NumOrthogonalDirections> DirectionOffsets = {
|
|
|
|
CoordsXY{ -1, 0 },
|
2021-10-17 15:31:56 +02:00
|
|
|
{ 0, 1 },
|
|
|
|
{ 1, 0 },
|
|
|
|
{ 0, -1 },
|
|
|
|
};
|
2015-02-13 21:55:49 +01:00
|
|
|
|
2015-07-10 21:58:09 +02:00
|
|
|
// rct2: 0x0097B974
|
2018-06-20 17:28:51 +02:00
|
|
|
static constexpr const uint16_t EntranceDirections[] = {
|
2018-06-22 23:17:03 +02:00
|
|
|
(4), 0, 0, 0, 0, 0, 0, 0, // ENTRANCE_TYPE_RIDE_ENTRANCE,
|
|
|
|
(4), 0, 0, 0, 0, 0, 0, 0, // ENTRANCE_TYPE_RIDE_EXIT,
|
|
|
|
(4 | 1), 0, 0, 0, 0, 0, 0, 0, // ENTRANCE_TYPE_PARK_ENTRANCE
|
2015-07-10 21:58:09 +02:00
|
|
|
};
|
|
|
|
|
2016-08-11 21:38:27 +02:00
|
|
|
/** rct2: 0x0098D7F0 */
|
2018-06-20 17:28:51 +02:00
|
|
|
static constexpr const uint8_t connected_path_count[] = {
|
2017-06-06 23:24:18 +02:00
|
|
|
0, // 0b0000
|
|
|
|
1, // 0b0001
|
|
|
|
1, // 0b0010
|
|
|
|
2, // 0b0011
|
|
|
|
1, // 0b0100
|
|
|
|
2, // 0b0101
|
|
|
|
2, // 0b0110
|
|
|
|
3, // 0b0111
|
|
|
|
1, // 0b1000
|
|
|
|
2, // 0b1001
|
|
|
|
2, // 0b1010
|
|
|
|
3, // 0b1011
|
|
|
|
2, // 0b1100
|
|
|
|
3, // 0b1101
|
|
|
|
3, // 0b1110
|
|
|
|
4, // 0b1111
|
2016-08-11 21:38:27 +02:00
|
|
|
};
|
|
|
|
|
2021-08-11 23:26:46 +02:00
|
|
|
int32_t EntranceElement::GetDirections() const
|
2015-07-10 21:58:09 +02:00
|
|
|
{
|
2021-08-11 23:26:46 +02:00
|
|
|
return EntranceDirections[(GetEntranceType() * 8) + GetSequenceIndex()];
|
2015-07-10 21:58:09 +02:00
|
|
|
}
|
|
|
|
|
2021-08-11 23:26:46 +02:00
|
|
|
static bool entrance_has_direction(const EntranceElement& entranceElement, int32_t direction)
|
2015-07-10 21:58:09 +02:00
|
|
|
{
|
2021-08-11 23:26:46 +02:00
|
|
|
return entranceElement.GetDirections() & (1 << (direction & 3));
|
2015-07-10 21:58:09 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
TileElement* MapGetFootpathElement(const CoordsXYZ& coords)
|
2014-12-18 23:31:05 +01:00
|
|
|
{
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(coords);
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2019-03-15 03:12:20 +01:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
break;
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() == TileElementType::Path && tileElement->GetBaseZ() == coords.z)
|
2017-10-31 14:03:45 +01:00
|
|
|
return tileElement;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2014-12-18 23:31:05 +01:00
|
|
|
|
2018-01-29 17:06:01 +01:00
|
|
|
return nullptr;
|
2014-12-18 23:31:05 +01:00
|
|
|
}
|
|
|
|
|
2014-11-15 19:22:06 +01:00
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2014-12-16 23:03:56 +01:00
|
|
|
* rct2: 0x006A76FF
|
|
|
|
*/
|
2023-03-02 22:55:42 +01:00
|
|
|
money64 FootpathProvisionalSet(
|
2021-09-03 19:19:42 +02:00
|
|
|
ObjectEntryIndex type, ObjectEntryIndex railingsType, const CoordsXYZ& footpathLoc, int32_t slope,
|
|
|
|
PathConstructFlags constructFlags)
|
2014-12-16 23:03:56 +01:00
|
|
|
{
|
2023-03-02 22:55:42 +01:00
|
|
|
money64 cost;
|
2014-12-16 23:03:56 +01:00
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathProvisionalRemove();
|
2014-12-16 23:03:56 +01:00
|
|
|
|
2021-09-03 19:19:42 +02:00
|
|
|
auto footpathPlaceAction = FootpathPlaceAction(footpathLoc, slope, type, railingsType, INVALID_DIRECTION, constructFlags);
|
2019-03-01 18:17:46 +01:00
|
|
|
footpathPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
|
|
|
|
auto res = GameActions::Execute(&footpathPlaceAction);
|
2023-03-02 22:55:42 +01:00
|
|
|
cost = res.Error == GameActions::Status::Ok ? res.Cost : MONEY64_UNDEFINED;
|
2021-11-24 08:35:08 +01:00
|
|
|
if (res.Error == GameActions::Status::Ok)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-09-03 19:19:42 +02:00
|
|
|
gProvisionalFootpath.SurfaceIndex = type;
|
|
|
|
gProvisionalFootpath.RailingsIndex = railingsType;
|
2021-04-14 14:56:28 +02:00
|
|
|
gProvisionalFootpath.Position = footpathLoc;
|
|
|
|
gProvisionalFootpath.Slope = slope;
|
2021-09-03 19:19:42 +02:00
|
|
|
gProvisionalFootpath.ConstructFlags = constructFlags;
|
2021-04-14 14:56:28 +02:00
|
|
|
gProvisionalFootpath.Flags |= PROVISIONAL_PATH_FLAG_1;
|
2014-12-16 23:03:56 +01:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
if (gFootpathGroundFlags & ELEMENT_IS_UNDERGROUND)
|
|
|
|
{
|
2023-01-16 21:14:50 +01:00
|
|
|
ViewportSetVisibility(1);
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-16 21:14:50 +01:00
|
|
|
ViewportSetVisibility(3);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2014-12-16 23:03:56 +01:00
|
|
|
|
2018-03-03 12:34:27 +01:00
|
|
|
// Invalidate previous footpath piece.
|
2022-10-08 20:27:21 +02:00
|
|
|
VirtualFloorInvalidate();
|
2018-03-03 12:34:27 +01:00
|
|
|
|
2023-01-16 21:13:42 +01:00
|
|
|
if (!SceneryToolIsActive())
|
2018-01-07 00:09:26 +01:00
|
|
|
{
|
2021-11-24 08:35:08 +01:00
|
|
|
if (res.Error != GameActions::Status::Ok)
|
2018-01-07 01:47:14 +01:00
|
|
|
{
|
|
|
|
// If we can't build this, don't show a virtual floor.
|
2022-10-08 20:27:21 +02:00
|
|
|
VirtualFloorSetHeight(0);
|
2018-01-07 01:47:14 +01:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
else if (
|
|
|
|
gFootpathConstructSlope == TILE_ELEMENT_SLOPE_FLAT
|
2021-04-14 14:56:28 +02:00
|
|
|
|| gProvisionalFootpath.Position.z < gFootpathConstructFromPosition.z)
|
2018-01-07 01:47:14 +01:00
|
|
|
{
|
|
|
|
// Going either straight on, or down.
|
2022-10-08 20:27:21 +02:00
|
|
|
VirtualFloorSetHeight(gProvisionalFootpath.Position.z);
|
2018-01-07 01:47:14 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Going up in the world!
|
2022-10-08 20:27:21 +02:00
|
|
|
VirtualFloorSetHeight(gProvisionalFootpath.Position.z + LAND_HEIGHT_STEP);
|
2018-01-07 01:47:14 +01:00
|
|
|
}
|
2018-01-07 00:09:26 +01:00
|
|
|
}
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
return cost;
|
2014-12-16 23:03:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2014-12-16 23:03:56 +01:00
|
|
|
* rct2: 0x006A77FF
|
2014-11-15 19:22:06 +01:00
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathProvisionalRemove()
|
2014-11-15 19:22:06 +01:00
|
|
|
{
|
2021-04-14 14:56:28 +02:00
|
|
|
if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_1)
|
2018-01-11 16:22:37 +01:00
|
|
|
{
|
2021-04-14 14:56:28 +02:00
|
|
|
gProvisionalFootpath.Flags &= ~PROVISIONAL_PATH_FLAG_1;
|
2014-11-15 19:22:06 +01:00
|
|
|
|
2022-08-11 00:00:58 +02:00
|
|
|
auto action = FootpathRemoveAction(gProvisionalFootpath.Position);
|
|
|
|
action.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
|
|
|
|
GameActions::Execute(&action);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2014-11-15 19:22:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2014-11-15 19:22:06 +01:00
|
|
|
* rct2: 0x006A7831
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathProvisionalUpdate()
|
2014-11-15 19:22:06 +01:00
|
|
|
{
|
2021-04-14 14:56:28 +02:00
|
|
|
if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_SHOW_ARROW)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-04-14 14:56:28 +02:00
|
|
|
gProvisionalFootpath.Flags &= ~PROVISIONAL_PATH_FLAG_SHOW_ARROW;
|
2014-11-15 19:22:06 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateTileFull(gFootpathConstructFromPosition);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathProvisionalRemove();
|
2014-12-17 21:45:27 +01:00
|
|
|
}
|
|
|
|
|
2015-04-01 01:46:20 +02:00
|
|
|
/**
|
2015-12-11 16:38:37 +01:00
|
|
|
* Determines the location of the footpath at which we point with the cursor. If no footpath is underneath the cursor,
|
|
|
|
* then return the location of the ground tile. Besides the location it also computes the direction of the yellow arrow
|
|
|
|
* when we are going to build a footpath bridge/tunnel.
|
2015-04-01 01:46:20 +02:00
|
|
|
* rct2: 0x00689726
|
|
|
|
* In:
|
2017-06-06 23:24:18 +02:00
|
|
|
* screenX: eax
|
|
|
|
* screenY: ebx
|
2015-04-01 01:46:20 +02:00
|
|
|
* Out:
|
2017-06-06 23:24:18 +02:00
|
|
|
* x: ax
|
|
|
|
* y: bx
|
|
|
|
* direction: ecx
|
2017-10-31 14:03:45 +01:00
|
|
|
* tileElement: edx
|
2015-04-01 01:46:20 +02:00
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
CoordsXY FootpathGetCoordinatesFromPos(const ScreenCoordsXY& screenCoords, int32_t* direction, TileElement** tileElement)
|
2015-04-01 01:46:20 +02:00
|
|
|
{
|
2023-01-21 20:57:17 +01:00
|
|
|
WindowBase* window = WindowFindFromPoint(screenCoords);
|
2020-08-02 21:13:09 +02:00
|
|
|
if (window == nullptr || window->viewport == nullptr)
|
|
|
|
{
|
|
|
|
CoordsXY position{};
|
2021-09-09 01:48:53 +02:00
|
|
|
position.SetNull();
|
2020-08-02 21:13:09 +02:00
|
|
|
return position;
|
|
|
|
}
|
2020-08-02 09:32:59 +02:00
|
|
|
auto viewport = window->viewport;
|
2023-01-16 21:14:50 +01:00
|
|
|
auto info = GetMapCoordinatesFromPosWindow(window, screenCoords, EnumsToFlags(ViewportInteractionItem::Footpath));
|
2020-12-29 19:09:15 +01:00
|
|
|
if (info.SpriteType != ViewportInteractionItem::Footpath
|
2018-06-22 23:17:03 +02:00
|
|
|
|| !(viewport->flags & (VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL)))
|
|
|
|
{
|
2023-01-16 21:14:50 +01:00
|
|
|
info = GetMapCoordinatesFromPosWindow(
|
2021-02-10 02:06:18 +01:00
|
|
|
window, screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Footpath));
|
2020-12-29 19:09:15 +01:00
|
|
|
if (info.SpriteType == ViewportInteractionItem::None)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-08-02 09:32:59 +02:00
|
|
|
auto position = info.Loc;
|
2021-09-09 01:48:53 +02:00
|
|
|
position.SetNull();
|
2020-01-13 02:20:29 +01:00
|
|
|
return position;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-02 09:32:59 +02:00
|
|
|
auto minPosition = info.Loc;
|
|
|
|
auto maxPosition = info.Loc + CoordsXY{ 31, 31 };
|
|
|
|
auto myTileElement = info.Element;
|
|
|
|
auto position = info.Loc.ToTileCentre();
|
|
|
|
auto z = 0;
|
2020-12-29 19:09:15 +01:00
|
|
|
if (info.SpriteType == ViewportInteractionItem::Footpath)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-12-22 22:14:02 +01:00
|
|
|
z = myTileElement->GetBaseZ();
|
2018-09-16 16:17:35 +02:00
|
|
|
if (myTileElement->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
z += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 03:25:11 +02:00
|
|
|
auto start_vp_pos = viewport->ScreenToViewportCoord(screenCoords);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
for (int32_t i = 0; i < 5; i++)
|
|
|
|
{
|
2020-12-29 19:09:15 +01:00
|
|
|
if (info.SpriteType != ViewportInteractionItem::Footpath)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-10-12 07:35:20 +02:00
|
|
|
z = TileElementHeight(position);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2023-01-16 21:14:50 +01:00
|
|
|
position = ViewportPosToMapPos(start_vp_pos, z);
|
2018-08-12 13:50:40 +02:00
|
|
|
position.x = std::clamp(position.x, minPosition.x, maxPosition.x);
|
|
|
|
position.y = std::clamp(position.y, minPosition.y, maxPosition.y);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Determine to which edge the cursor is closest
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t myDirection;
|
|
|
|
int32_t mod_x = position.x & 0x1F, mod_y = position.y & 0x1F;
|
2018-06-22 23:17:03 +02:00
|
|
|
if (mod_x < mod_y)
|
|
|
|
{
|
|
|
|
if (mod_x + mod_y < 32)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
myDirection = 0;
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
myDirection = 1;
|
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (mod_x + mod_y < 32)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
myDirection = 3;
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
myDirection = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 16:47:23 +01:00
|
|
|
position = position.ToTileStart();
|
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
if (direction != nullptr)
|
|
|
|
*direction = myDirection;
|
|
|
|
if (tileElement != nullptr)
|
|
|
|
*tileElement = myTileElement;
|
2020-01-13 02:20:29 +01:00
|
|
|
|
|
|
|
return position;
|
2015-04-01 01:46:20 +02:00
|
|
|
}
|
|
|
|
|
2014-12-17 21:45:27 +01:00
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2014-12-17 21:45:27 +01:00
|
|
|
* rct2: 0x0068A0C9
|
2015-03-30 17:28:36 +02:00
|
|
|
* screenX: eax
|
|
|
|
* screenY: ebx
|
|
|
|
* x: ax
|
|
|
|
* y: bx
|
|
|
|
* direction: cl
|
2017-10-31 14:03:45 +01:00
|
|
|
* tileElement: edx
|
2014-12-17 21:45:27 +01:00
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
CoordsXY FootpathBridgeGetInfoFromPos(const ScreenCoordsXY& screenCoords, int32_t* direction, TileElement** tileElement)
|
2014-12-17 21:45:27 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// First check if we point at an entrance or exit. In that case, we would want the path coming from the entrance/exit.
|
2023-01-21 20:57:17 +01:00
|
|
|
WindowBase* window = WindowFindFromPoint(screenCoords);
|
2020-08-02 21:13:09 +02:00
|
|
|
if (window == nullptr || window->viewport == nullptr)
|
|
|
|
{
|
|
|
|
CoordsXY ret{};
|
2021-09-09 01:48:53 +02:00
|
|
|
ret.SetNull();
|
2020-08-02 21:13:09 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2020-08-02 09:32:59 +02:00
|
|
|
auto viewport = window->viewport;
|
2023-01-16 21:14:50 +01:00
|
|
|
auto info = GetMapCoordinatesFromPosWindow(window, screenCoords, EnumsToFlags(ViewportInteractionItem::Ride));
|
2020-08-02 09:32:59 +02:00
|
|
|
*tileElement = info.Element;
|
2020-12-29 19:09:15 +01:00
|
|
|
if (info.SpriteType == ViewportInteractionItem::Ride
|
2017-06-06 23:24:18 +02:00
|
|
|
&& viewport->flags & (VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_HIDE_VERTICAL)
|
2021-12-11 00:39:39 +01:00
|
|
|
&& (*tileElement)->GetType() == TileElementType::Entrance)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-08-11 23:26:46 +02:00
|
|
|
int32_t directions = (*tileElement)->AsEntrance()->GetDirections();
|
2018-06-22 23:17:03 +02:00
|
|
|
if (directions & 0x0F)
|
|
|
|
{
|
2023-01-18 22:42:16 +01:00
|
|
|
int32_t bx = UtilBitScanForward(directions);
|
2018-10-05 14:02:55 +02:00
|
|
|
bx += (*tileElement)->AsEntrance()->GetDirection();
|
2017-06-06 23:24:18 +02:00
|
|
|
bx &= 3;
|
2018-06-22 23:17:03 +02:00
|
|
|
if (direction != nullptr)
|
|
|
|
*direction = bx;
|
2020-08-02 09:32:59 +02:00
|
|
|
return info.Loc;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-16 21:14:50 +01:00
|
|
|
info = GetMapCoordinatesFromPosWindow(
|
2020-08-02 09:32:59 +02:00
|
|
|
window, screenCoords,
|
2021-02-10 02:06:18 +01:00
|
|
|
EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Footpath, ViewportInteractionItem::Ride));
|
2021-12-11 00:39:39 +01:00
|
|
|
if (info.SpriteType == ViewportInteractionItem::Ride && (*tileElement)->GetType() == TileElementType::Entrance)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-08-11 23:26:46 +02:00
|
|
|
int32_t directions = (*tileElement)->AsEntrance()->GetDirections();
|
2018-06-22 23:17:03 +02:00
|
|
|
if (directions & 0x0F)
|
|
|
|
{
|
2023-01-18 22:42:16 +01:00
|
|
|
int32_t bx = (*tileElement)->GetDirectionWithOffset(UtilBitScanForward(directions));
|
2018-06-22 23:17:03 +02:00
|
|
|
if (direction != nullptr)
|
|
|
|
*direction = bx;
|
2020-08-02 09:32:59 +02:00
|
|
|
return info.Loc;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We point at something else
|
2022-10-04 08:51:27 +02:00
|
|
|
return FootpathGetCoordinatesFromPos(screenCoords, direction, tileElement);
|
2015-02-13 21:55:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2015-02-13 21:55:49 +01:00
|
|
|
* rct2: 0x00673883
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathRemoveLitter(const CoordsXYZ& footpathPos)
|
2015-02-13 21:55:49 +01:00
|
|
|
{
|
2021-01-23 08:36:46 +01:00
|
|
|
std::vector<Litter*> removals;
|
|
|
|
for (auto litter : EntityTileList<Litter>(footpathPos))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-06-08 21:13:50 +02:00
|
|
|
int32_t distanceZ = abs(litter->z - footpathPos.z);
|
|
|
|
if (distanceZ <= 32)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-01-23 08:36:46 +01:00
|
|
|
removals.push_back(litter);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2021-01-23 08:36:46 +01:00
|
|
|
for (auto* litter : removals)
|
|
|
|
{
|
|
|
|
litter->Invalidate();
|
2021-11-24 14:37:47 +01:00
|
|
|
EntityRemove(litter);
|
2021-01-23 08:36:46 +01:00
|
|
|
}
|
2015-02-13 21:55:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2015-02-13 21:55:49 +01:00
|
|
|
* rct2: 0x0069A48B
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathInterruptPeeps(const CoordsXYZ& footpathPos)
|
2015-02-13 21:55:49 +01:00
|
|
|
{
|
2020-06-08 21:13:50 +02:00
|
|
|
auto quad = EntityTileList<Peep>(footpathPos);
|
|
|
|
for (auto peep : quad)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-09-28 20:51:49 +02:00
|
|
|
if (peep->State == PeepState::Sitting || peep->State == PeepState::Watching)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-02-10 19:47:11 +01:00
|
|
|
auto location = peep->GetLocation();
|
|
|
|
if (location.z == footpathPos.z)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-02-10 19:47:11 +01:00
|
|
|
auto destination = location.ToTileCentre();
|
2020-09-28 20:51:49 +02:00
|
|
|
peep->SetState(PeepState::Walking);
|
2021-02-10 20:18:27 +01:00
|
|
|
peep->SetDestination(destination, 5);
|
2020-06-08 21:13:50 +02:00
|
|
|
peep->UpdateCurrentActionSpriteType();
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-13 21:55:49 +01:00
|
|
|
}
|
|
|
|
|
2015-12-11 16:38:37 +01:00
|
|
|
/**
|
2019-09-18 23:31:30 +02:00
|
|
|
* Returns true if the edge of tile x, y specified by direction is occupied by a fence
|
|
|
|
* between heights z0 and z1.
|
|
|
|
*
|
|
|
|
* Note that there may still be a fence on the opposing tile.
|
2015-12-11 16:38:37 +01:00
|
|
|
*
|
|
|
|
* rct2: 0x006E59DC
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
bool WallInTheWay(const CoordsXYRangedZ& fencePos, int32_t direction)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement* tileElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
tileElement = MapGetFirstElementAt(fencePos);
|
2018-01-29 17:06:01 +01:00
|
|
|
if (tileElement == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Wall)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2019-02-25 16:08:49 +01:00
|
|
|
if (tileElement->IsGhost())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2020-01-04 12:01:35 +01:00
|
|
|
if (fencePos.baseZ >= tileElement->GetClearanceZ())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2020-01-04 12:01:35 +01:00
|
|
|
if (fencePos.clearanceZ <= tileElement->GetBaseZ())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2018-09-14 11:14:19 +02:00
|
|
|
if ((tileElement->GetDirection()) != direction)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
return true;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
static PathElement* FootpathConnectCornersGetNeighbour(const CoordsXYZ& footpathPos, int32_t requireEdges)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2022-10-12 07:35:20 +02:00
|
|
|
if (!MapIsLocationValid(footpathPos))
|
2018-06-01 11:30:41 +02:00
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(footpathPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return nullptr;
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2020-01-04 11:51:20 +01:00
|
|
|
auto pathElement = tileElement->AsPath();
|
|
|
|
if (pathElement->IsQueue())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2020-01-04 12:36:16 +01:00
|
|
|
if (tileElement->GetBaseZ() != footpathPos.z)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2020-01-04 11:51:20 +01:00
|
|
|
if (!(pathElement->GetEdgesAndCorners() & requireEdges))
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2020-01-04 11:51:20 +01:00
|
|
|
return pathElement;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2018-06-01 11:30:41 +02:00
|
|
|
|
2018-01-29 17:06:01 +01:00
|
|
|
return nullptr;
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the corner edges of four path tiles.
|
|
|
|
* The function will search for a path in the direction given, then check clockwise to see if it there is a path and again until
|
|
|
|
* it reaches the initial path. In other words, checks if there are four paths together so that it can set the inner corners of
|
|
|
|
* each one.
|
|
|
|
*
|
|
|
|
* rct2: 0x006A70EB
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static void FootpathConnectCorners(const CoordsXY& footpathPos, PathElement* initialTileElement)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2020-01-04 11:51:20 +01:00
|
|
|
using PathElementCoordsPair = std::pair<PathElement*, CoordsXY>;
|
|
|
|
std::array<PathElementCoordsPair, 4> tileElements;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-01-04 11:51:20 +01:00
|
|
|
if (initialTileElement->IsQueue())
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
2020-01-04 11:51:20 +01:00
|
|
|
if (initialTileElement->IsSloped())
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
|
2021-11-25 10:13:20 +01:00
|
|
|
std::get<0>(tileElements) = { initialTileElement, footpathPos };
|
2020-01-04 11:51:20 +01:00
|
|
|
int32_t z = initialTileElement->GetBaseZ();
|
2022-10-17 18:08:55 +02:00
|
|
|
for (int32_t initialDirection = 0; initialDirection < NumOrthogonalDirections; initialDirection++)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t direction = initialDirection;
|
2020-01-04 11:51:20 +01:00
|
|
|
auto currentPos = footpathPos + CoordsDirectionDelta[direction];
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
std::get<1>(tileElements) = { FootpathConnectCornersGetNeighbour({ currentPos, z }, (1 << DirectionReverse(direction))),
|
|
|
|
currentPos };
|
2021-11-25 10:13:20 +01:00
|
|
|
if (std::get<1>(tileElements).first == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
direction = DirectionNext(direction);
|
2020-01-04 11:51:20 +01:00
|
|
|
currentPos += CoordsDirectionDelta[direction];
|
2022-10-04 21:12:38 +02:00
|
|
|
std::get<2>(tileElements) = { FootpathConnectCornersGetNeighbour({ currentPos, z }, (1 << DirectionReverse(direction))),
|
|
|
|
currentPos };
|
2021-11-25 10:13:20 +01:00
|
|
|
if (std::get<2>(tileElements).first == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
direction = DirectionNext(direction);
|
2020-01-04 11:51:20 +01:00
|
|
|
currentPos += CoordsDirectionDelta[direction];
|
2017-06-06 23:24:18 +02:00
|
|
|
// First check link to previous tile
|
2022-10-04 21:12:38 +02:00
|
|
|
std::get<3>(tileElements) = { FootpathConnectCornersGetNeighbour({ currentPos, z }, (1 << DirectionReverse(direction))),
|
|
|
|
currentPos };
|
2021-11-25 10:13:20 +01:00
|
|
|
if (std::get<3>(tileElements).first == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
// Second check link to initial tile
|
2022-10-04 08:51:27 +02:00
|
|
|
std::get<3>(tileElements) = { FootpathConnectCornersGetNeighbour({ currentPos, z }, (1 << ((direction + 1) & 3))),
|
2021-11-25 10:13:20 +01:00
|
|
|
currentPos };
|
|
|
|
if (std::get<3>(tileElements).first == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
direction = DirectionNext(direction);
|
2021-11-25 10:13:20 +01:00
|
|
|
std::get<3>(tileElements).first->SetCorners(std::get<3>(tileElements).first->GetCorners() | (1 << (direction)));
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateElement(std::get<3>(tileElements).second, reinterpret_cast<TileElement*>(std::get<3>(tileElements).first));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
direction = DirectionPrev(direction);
|
2021-11-25 10:13:20 +01:00
|
|
|
std::get<2>(tileElements).first->SetCorners(std::get<2>(tileElements).first->GetCorners() | (1 << (direction)));
|
2018-10-05 13:33:46 +02:00
|
|
|
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateElement(std::get<2>(tileElements).second, reinterpret_cast<TileElement*>(std::get<2>(tileElements).first));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
direction = DirectionPrev(direction);
|
2021-11-25 10:13:20 +01:00
|
|
|
std::get<1>(tileElements).first->SetCorners(std::get<1>(tileElements).first->GetCorners() | (1 << (direction)));
|
2018-10-05 13:33:46 +02:00
|
|
|
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateElement(std::get<1>(tileElements).second, reinterpret_cast<TileElement*>(std::get<1>(tileElements).first));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
direction = initialDirection;
|
2021-11-25 10:13:20 +01:00
|
|
|
std::get<0>(tileElements).first->SetCorners(std::get<0>(tileElements).first->GetCorners() | (1 << (direction)));
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateElement(std::get<0>(tileElements).second, reinterpret_cast<TileElement*>(std::get<0>(tileElements).first));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
struct FootpathNeighbour
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint8_t order;
|
|
|
|
uint8_t direction;
|
2022-01-19 14:17:11 +01:00
|
|
|
RideId ride_index;
|
2022-01-26 13:28:19 +01:00
|
|
|
StationIndex entrance_index;
|
2018-02-14 09:42:26 +01:00
|
|
|
};
|
2015-07-07 20:09:12 +02:00
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
struct FootpathNeighbourList
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-19 09:16:44 +01:00
|
|
|
FootpathNeighbour items[8];
|
2018-03-12 00:28:33 +01:00
|
|
|
size_t count;
|
2018-02-14 09:42:26 +01:00
|
|
|
};
|
2015-07-07 20:09:12 +02:00
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
static int32_t FootpathNeighbourCompare(const void* a, const void* b)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2023-01-19 09:16:44 +01:00
|
|
|
uint8_t va = (static_cast<const FootpathNeighbour*>(a))->order;
|
|
|
|
uint8_t vb = (static_cast<const FootpathNeighbour*>(b))->order;
|
2018-06-22 23:17:03 +02:00
|
|
|
if (va < vb)
|
|
|
|
return 1;
|
2021-09-15 22:22:15 +02:00
|
|
|
if (va > vb)
|
2018-06-22 23:17:03 +02:00
|
|
|
return -1;
|
2021-09-15 22:22:15 +02:00
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
uint8_t da = (static_cast<const FootpathNeighbour*>(a))->direction;
|
|
|
|
uint8_t db = (static_cast<const FootpathNeighbour*>(b))->direction;
|
2021-09-15 22:22:15 +02:00
|
|
|
if (da < db)
|
|
|
|
return -1;
|
|
|
|
if (da > db)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
static void FootpathNeighbourListInit(FootpathNeighbourList* neighbourList)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
neighbourList->count = 0;
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
static void FootpathNeighbourListPush(
|
2023-01-19 09:16:44 +01:00
|
|
|
FootpathNeighbourList* neighbourList, int32_t order, int32_t direction, RideId rideIndex, ::StationIndex entrance_index)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2018-11-21 23:16:04 +01:00
|
|
|
Guard::Assert(neighbourList->count < std::size(neighbourList->items));
|
2017-06-06 23:24:18 +02:00
|
|
|
neighbourList->items[neighbourList->count].order = order;
|
|
|
|
neighbourList->items[neighbourList->count].direction = direction;
|
|
|
|
neighbourList->items[neighbourList->count].ride_index = rideIndex;
|
|
|
|
neighbourList->items[neighbourList->count].entrance_index = entrance_index;
|
|
|
|
neighbourList->count++;
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
static bool FootpathNeighbourListPop(FootpathNeighbourList* neighbourList, FootpathNeighbour* outNeighbour)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
if (neighbourList->count == 0)
|
|
|
|
return false;
|
2015-07-07 20:09:12 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
*outNeighbour = neighbourList->items[0];
|
2018-03-16 20:57:22 +01:00
|
|
|
const size_t bytesToMove = (neighbourList->count - 1) * sizeof(neighbourList->items[0]);
|
|
|
|
memmove(&neighbourList->items[0], &neighbourList->items[1], bytesToMove);
|
2017-06-06 23:24:18 +02:00
|
|
|
neighbourList->count--;
|
|
|
|
return true;
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
static void FootpathNeighbourListRemove(FootpathNeighbourList* neighbourList, size_t index)
|
2015-08-04 18:40:02 +02:00
|
|
|
{
|
2018-03-12 00:28:33 +01:00
|
|
|
Guard::ArgumentInRange<size_t>(index, 0, neighbourList->count - 1);
|
2020-04-17 21:08:22 +02:00
|
|
|
int32_t itemsRemaining = static_cast<int32_t>(neighbourList->count - index) - 1;
|
2018-06-22 23:17:03 +02:00
|
|
|
if (itemsRemaining > 0)
|
|
|
|
{
|
2023-01-19 09:16:44 +01:00
|
|
|
memmove(&neighbourList->items[index], &neighbourList->items[index + 1], sizeof(FootpathNeighbour) * itemsRemaining);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
neighbourList->count--;
|
2015-08-04 18:40:02 +02:00
|
|
|
}
|
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
static void FoopathNeighbourListSort(FootpathNeighbourList* neighbourList)
|
2015-07-07 20:09:12 +02:00
|
|
|
{
|
2023-01-19 09:16:44 +01:00
|
|
|
qsort(neighbourList->items, neighbourList->count, sizeof(FootpathNeighbour), FootpathNeighbourCompare);
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
static TileElement* FootpathGetElement(const CoordsXYRangedZ& footpathPos, int32_t direction)
|
2015-07-08 07:58:48 +02:00
|
|
|
{
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(footpathPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return nullptr;
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2020-01-04 12:55:21 +01:00
|
|
|
if (footpathPos.clearanceZ == tileElement->GetBaseZ())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-01-06 22:29:40 +01:00
|
|
|
auto slope = tileElement->AsPath()->GetSlopeDirection();
|
2017-06-06 23:24:18 +02:00
|
|
|
if (slope != direction)
|
|
|
|
break;
|
|
|
|
}
|
2017-10-31 14:03:45 +01:00
|
|
|
return tileElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-01-04 12:55:21 +01:00
|
|
|
if (footpathPos.baseZ == tileElement->GetBaseZ())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-09-16 16:17:35 +02:00
|
|
|
if (!tileElement->AsPath()->IsSloped())
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
auto slope = DirectionReverse(tileElement->AsPath()->GetSlopeDirection());
|
2017-06-06 23:24:18 +02:00
|
|
|
if (slope != direction)
|
|
|
|
break;
|
|
|
|
|
2017-10-31 14:03:45 +01:00
|
|
|
return tileElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2018-01-29 17:06:01 +01:00
|
|
|
return nullptr;
|
2015-07-08 07:58:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-18 23:31:30 +02:00
|
|
|
/**
|
|
|
|
* Attempt to connect a newly disconnected queue tile to the specified path tile
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static bool FootpathReconnectQueueToPath(
|
2020-01-04 12:39:19 +01:00
|
|
|
const CoordsXY& footpathPos, TileElement* tileElement, int32_t action, int32_t direction)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-05 13:11:44 +02:00
|
|
|
if (((tileElement->AsPath()->GetEdges() & (1 << direction)) == 0) ^ (action < 0))
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
|
2020-01-04 12:39:19 +01:00
|
|
|
auto targetQueuePos = footpathPos + CoordsDirectionDelta[direction];
|
2019-09-18 23:31:30 +02:00
|
|
|
|
|
|
|
if (action < 0)
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
if (WallInTheWay({ footpathPos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() }, direction))
|
2019-09-18 23:31:30 +02:00
|
|
|
return false;
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
if (WallInTheWay(
|
2022-10-04 21:12:38 +02:00
|
|
|
{ targetQueuePos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() }, DirectionReverse(direction)))
|
2019-09-18 23:31:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-04 12:55:21 +01:00
|
|
|
int32_t z = tileElement->GetBaseZ();
|
2022-10-04 08:51:27 +02:00
|
|
|
TileElement* targetFootpathElement = FootpathGetElement({ targetQueuePos, z - LAND_HEIGHT_STEP, z }, direction);
|
2020-01-04 12:55:21 +01:00
|
|
|
if (targetFootpathElement != nullptr && !targetFootpathElement->AsPath()->IsQueue())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-01-04 12:55:21 +01:00
|
|
|
auto targetQueueElement = targetFootpathElement->AsPath();
|
2018-10-04 15:40:55 +02:00
|
|
|
tileElement->AsPath()->SetSlopeDirection(0);
|
2018-06-22 23:17:03 +02:00
|
|
|
if (action > 0)
|
|
|
|
{
|
2018-10-05 13:33:46 +02:00
|
|
|
tileElement->AsPath()->SetEdges(tileElement->AsPath()->GetEdges() & ~(1 << direction));
|
2022-10-04 21:12:38 +02:00
|
|
|
targetQueueElement->SetEdges(targetQueueElement->GetEdges() & ~(1 << (DirectionReverse(direction) & 3)));
|
2018-06-22 23:17:03 +02:00
|
|
|
if (action >= 2)
|
2018-10-04 15:40:55 +02:00
|
|
|
tileElement->AsPath()->SetSlopeDirection(direction);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
else if (action < 0)
|
|
|
|
{
|
2018-10-05 13:33:46 +02:00
|
|
|
tileElement->AsPath()->SetEdges(tileElement->AsPath()->GetEdges() | (1 << direction));
|
2022-10-04 21:12:38 +02:00
|
|
|
targetQueueElement->SetEdges(targetQueueElement->GetEdges() | (1 << (DirectionReverse(direction) & 3)));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
if (action != 0)
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateTileFull(targetQueuePos);
|
2017-06-06 23:24:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2015-07-08 07:58:48 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
static bool FootpathDisconnectQueueFromPath(const CoordsXY& footpathPos, TileElement* tileElement, int32_t action)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-03 10:57:29 +02:00
|
|
|
if (!tileElement->AsPath()->IsQueue())
|
2018-06-22 23:17:03 +02:00
|
|
|
return false;
|
2015-07-08 07:58:48 +02:00
|
|
|
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
return false;
|
2015-07-08 07:58:48 +02:00
|
|
|
|
2018-10-05 13:11:44 +02:00
|
|
|
uint8_t c = connected_path_count[tileElement->AsPath()->GetEdges()];
|
2018-06-22 23:17:03 +02:00
|
|
|
if ((action < 0) ? (c >= 2) : (c < 2))
|
|
|
|
return false;
|
2015-07-08 07:58:48 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
if (action < 0)
|
|
|
|
{
|
2018-10-04 15:07:48 +02:00
|
|
|
uint8_t direction = tileElement->AsPath()->GetSlopeDirection();
|
2022-10-04 08:51:27 +02:00
|
|
|
if (FootpathReconnectQueueToPath(footpathPos, tileElement, action, direction))
|
2017-06-06 23:24:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
2015-07-08 07:58:48 +02:00
|
|
|
|
2019-09-01 17:25:50 +02:00
|
|
|
for (Direction direction : ALL_DIRECTIONS)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-04 15:07:48 +02:00
|
|
|
if ((action < 0) && (direction == tileElement->AsPath()->GetSlopeDirection()))
|
2018-01-03 10:57:37 +01:00
|
|
|
continue;
|
2022-10-04 08:51:27 +02:00
|
|
|
if (FootpathReconnectQueueToPath(footpathPos, tileElement, action, direction))
|
2017-06-06 23:24:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
2015-07-08 07:58:48 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
2015-07-08 07:58:48 +02:00
|
|
|
}
|
|
|
|
|
2015-07-07 20:09:12 +02:00
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2015-07-07 20:09:12 +02:00
|
|
|
* rct2: 0x006A6D7E
|
|
|
|
*/
|
2020-08-18 04:28:26 +02:00
|
|
|
|
2023-01-17 19:05:14 +01:00
|
|
|
static void Loc6A6FD2(const CoordsXYZ& initialTileElementPos, int32_t direction, TileElement* initialTileElement, bool query)
|
2020-08-18 04:28:26 +02:00
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if ((initialTileElement)->GetType() == TileElementType::Path)
|
2020-08-18 04:28:26 +02:00
|
|
|
{
|
|
|
|
if (!query)
|
|
|
|
{
|
|
|
|
initialTileElement->AsPath()->SetEdges(initialTileElement->AsPath()->GetEdges() | (1 << direction));
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateElement(initialTileElementPos, initialTileElement);
|
2020-08-18 04:28:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-17 19:05:14 +01:00
|
|
|
static void Loc6A6F1F(
|
2020-08-18 04:28:26 +02:00
|
|
|
const CoordsXYZ& initialTileElementPos, int32_t direction, TileElement* tileElement, TileElement* initialTileElement,
|
2023-01-19 09:16:44 +01:00
|
|
|
const CoordsXY& targetPos, int32_t flags, bool query, FootpathNeighbourList* neighbourList)
|
2020-08-18 04:28:26 +02:00
|
|
|
{
|
|
|
|
if (query)
|
|
|
|
{
|
2022-10-04 21:12:38 +02:00
|
|
|
if (WallInTheWay({ targetPos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() }, DirectionReverse(direction)))
|
2020-08-18 04:28:26 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (tileElement->AsPath()->IsQueue())
|
|
|
|
{
|
|
|
|
if (connected_path_count[tileElement->AsPath()->GetEdges()] < 2)
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListPush(
|
2020-08-18 04:28:26 +02:00
|
|
|
neighbourList, 4, direction, tileElement->AsPath()->GetRideIndex(),
|
|
|
|
tileElement->AsPath()->GetStationIndex());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if ((initialTileElement)->GetType() == TileElementType::Path && initialTileElement->AsPath()->IsQueue())
|
2020-08-18 04:28:26 +02:00
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
if (FootpathDisconnectQueueFromPath(targetPos, tileElement, 0))
|
2020-08-18 04:28:26 +02:00
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListPush(
|
2020-08-18 04:28:26 +02:00
|
|
|
neighbourList, 3, direction, tileElement->AsPath()->GetRideIndex(),
|
|
|
|
tileElement->AsPath()->GetStationIndex());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListPush(neighbourList, 2, direction, RideId::GetNull(), StationIndex::GetNull());
|
2020-08-18 04:28:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathDisconnectQueueFromPath(targetPos, tileElement, 1 + ((flags >> 6) & 1));
|
2022-10-04 21:12:38 +02:00
|
|
|
tileElement->AsPath()->SetEdges(tileElement->AsPath()->GetEdges() | (1 << DirectionReverse(direction)));
|
2020-08-18 04:28:26 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathQueueChainPush(tileElement->AsPath()->GetRideIndex());
|
2020-08-18 04:28:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!(flags & (GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED)))
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathInterruptPeeps({ targetPos, tileElement->GetBaseZ() });
|
2020-08-18 04:28:26 +02:00
|
|
|
}
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateElement(targetPos, tileElement);
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6FD2(initialTileElementPos, direction, initialTileElement, query);
|
2020-08-18 04:28:26 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 19:05:14 +01:00
|
|
|
static void Loc6A6D7E(
|
2020-01-04 11:44:36 +01:00
|
|
|
const CoordsXYZ& initialTileElementPos, int32_t direction, TileElement* initialTileElement, int32_t flags, bool query,
|
2023-01-19 09:16:44 +01:00
|
|
|
FootpathNeighbourList* neighbourList)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-01-04 11:44:36 +01:00
|
|
|
auto targetPos = CoordsXY{ initialTileElementPos } + CoordsDirectionDelta[direction];
|
2022-10-12 07:35:20 +02:00
|
|
|
if (((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && MapIsEdge(targetPos))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
if (query)
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListPush(neighbourList, 7, direction, RideId::GetNull(), StationIndex::GetNull());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6FD2(initialTileElementPos, direction, initialTileElement, query);
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(targetPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return;
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
switch (tileElement->GetType())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
case TileElementType::Path:
|
2020-08-18 04:28:26 +02:00
|
|
|
if (tileElement->GetBaseZ() == initialTileElementPos.z)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-08-18 04:28:26 +02:00
|
|
|
if (!tileElement->AsPath()->IsSloped() || tileElement->AsPath()->GetSlopeDirection() == direction)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6F1F(
|
2020-08-18 04:28:26 +02:00
|
|
|
initialTileElementPos, direction, tileElement, initialTileElement, targetPos, flags, query,
|
|
|
|
neighbourList);
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
2020-08-18 04:28:26 +02:00
|
|
|
return;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2021-09-15 22:22:15 +02:00
|
|
|
if (tileElement->GetBaseZ() == initialTileElementPos.z - LAND_HEIGHT_STEP)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-08-18 04:28:26 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped()
|
2022-10-04 21:12:38 +02:00
|
|
|
&& tileElement->AsPath()->GetSlopeDirection() == DirectionReverse(direction))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6F1F(
|
2020-08-18 04:28:26 +02:00
|
|
|
initialTileElementPos, direction, tileElement, initialTileElement, targetPos, flags, query,
|
|
|
|
neighbourList);
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
2020-08-18 04:28:26 +02:00
|
|
|
return;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2021-12-11 00:39:39 +01:00
|
|
|
case TileElementType::Track:
|
2020-01-04 11:44:36 +01:00
|
|
|
if (initialTileElementPos.z == tileElement->GetBaseZ())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-17 01:32:54 +01:00
|
|
|
auto ride = GetRide(tileElement->AsTrack()->GetRideIndex());
|
2020-12-12 22:53:50 +01:00
|
|
|
if (ride == nullptr)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-01-17 12:27:14 +01:00
|
|
|
const auto trackType = tileElement->AsTrack()->GetTrackType();
|
2018-09-17 22:10:15 +02:00
|
|
|
const uint8_t trackSequence = tileElement->AsTrack()->GetSequenceIndex();
|
2021-08-27 23:49:32 +02:00
|
|
|
const auto& ted = GetTrackElementDescriptor(trackType);
|
2021-08-29 16:08:54 +02:00
|
|
|
if (!(ted.SequenceProperties[trackSequence] & TRACK_SEQUENCE_FLAG_CONNECTS_TO_PATH))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2022-10-04 21:12:38 +02:00
|
|
|
uint16_t dx = DirectionReverse((direction - tileElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK);
|
2021-08-22 17:18:38 +02:00
|
|
|
|
2021-08-29 16:08:54 +02:00
|
|
|
if (!(ted.SequenceProperties[trackSequence] & (1 << dx)))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (query)
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListPush(
|
2022-01-26 13:28:19 +01:00
|
|
|
neighbourList, 1, direction, tileElement->AsTrack()->GetRideIndex(), StationIndex::GetNull());
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6FD2(initialTileElementPos, direction, initialTileElement, query);
|
2020-08-18 04:28:26 +02:00
|
|
|
return;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2021-12-11 00:39:39 +01:00
|
|
|
case TileElementType::Entrance:
|
2020-01-04 11:44:36 +01:00
|
|
|
if (initialTileElementPos.z == tileElement->GetBaseZ())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-08-11 23:26:46 +02:00
|
|
|
if (entrance_has_direction(
|
2022-10-04 21:12:38 +02:00
|
|
|
*(tileElement->AsEntrance()), DirectionReverse(direction - tileElement->GetDirection())))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
if (query)
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListPush(
|
2018-09-26 12:30:27 +02:00
|
|
|
neighbourList, 8, direction, tileElement->AsEntrance()->GetRideIndex(),
|
2018-09-26 12:54:44 +02:00
|
|
|
tileElement->AsEntrance()->GetStationIndex());
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-09-26 12:13:44 +02:00
|
|
|
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathQueueChainPush(tileElement->AsEntrance()->GetRideIndex());
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6FD2(initialTileElementPos, direction, initialTileElement, query);
|
2020-08-18 04:28:26 +02:00
|
|
|
return;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2021-12-11 00:30:59 +01:00
|
|
|
default:
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-07 20:09:12 +02:00
|
|
|
|
2020-08-18 04:28:26 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 19:05:14 +01:00
|
|
|
// TODO: Change this into a simple check that validates if the direction should be fully checked with Loc6A6D7E and move the
|
|
|
|
// calling of Loc6A6D7E into the parent function.
|
|
|
|
static void Loc6A6C85(
|
2023-01-19 09:16:44 +01:00
|
|
|
const CoordsXYE& tileElementPos, int32_t direction, int32_t flags, bool query, FootpathNeighbourList* neighbourList)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-01-04 11:44:36 +01:00
|
|
|
if (query
|
2022-10-04 08:51:27 +02:00
|
|
|
&& WallInTheWay(
|
2020-01-04 12:01:35 +01:00
|
|
|
{ tileElementPos, tileElementPos.element->GetBaseZ(), tileElementPos.element->GetClearanceZ() }, direction))
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElementPos.element->GetType() == TileElementType::Entrance)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-08-11 23:26:46 +02:00
|
|
|
if (!entrance_has_direction(
|
|
|
|
*(tileElementPos.element->AsEntrance()), direction - tileElementPos.element->GetDirection()))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElementPos.element->GetType() == TileElementType::Track)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-17 01:32:54 +01:00
|
|
|
auto ride = GetRide(tileElementPos.element->AsTrack()->GetRideIndex());
|
2020-12-12 22:53:50 +01:00
|
|
|
if (ride == nullptr)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-12-12 22:53:50 +01:00
|
|
|
|
|
|
|
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-17 12:27:14 +01:00
|
|
|
const auto trackType = tileElementPos.element->AsTrack()->GetTrackType();
|
2020-01-04 11:44:36 +01:00
|
|
|
const uint8_t trackSequence = tileElementPos.element->AsTrack()->GetSequenceIndex();
|
2021-08-27 23:49:32 +02:00
|
|
|
const auto& ted = GetTrackElementDescriptor(trackType);
|
2021-08-29 16:08:54 +02:00
|
|
|
if (!(ted.SequenceProperties[trackSequence] & TRACK_SEQUENCE_FLAG_CONNECTS_TO_PATH))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-01-04 11:44:36 +01:00
|
|
|
uint16_t dx = (direction - tileElementPos.element->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK;
|
2021-08-29 16:08:54 +02:00
|
|
|
if (!(ted.SequenceProperties[trackSequence] & (1 << dx)))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 11:44:36 +01:00
|
|
|
auto pos = CoordsXYZ{ tileElementPos, tileElementPos.element->GetBaseZ() };
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElementPos.element->GetType() == TileElementType::Path)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-01-04 11:44:36 +01:00
|
|
|
if (tileElementPos.element->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-01-04 11:44:36 +01:00
|
|
|
if ((tileElementPos.element->AsPath()->GetSlopeDirection() - direction) & 1)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-01-04 11:44:36 +01:00
|
|
|
if (tileElementPos.element->AsPath()->GetSlopeDirection() == direction)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-01-04 11:44:36 +01:00
|
|
|
pos.z += LAND_HEIGHT_STEP;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6D7E(pos, direction, tileElementPos.element, flags, query, neighbourList);
|
2015-07-07 20:09:12 +02:00
|
|
|
}
|
|
|
|
|
2015-02-13 21:55:49 +01:00
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2015-02-13 21:55:49 +01:00
|
|
|
* rct2: 0x006A6C66
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathConnectEdges(const CoordsXY& footpathPos, TileElement* tileElement, int32_t flags)
|
2015-02-13 21:55:49 +01:00
|
|
|
{
|
2023-01-19 09:16:44 +01:00
|
|
|
FootpathNeighbourList neighbourList;
|
|
|
|
FootpathNeighbour neighbour;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathUpdateQueueChains();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListInit(&neighbourList);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathUpdateQueueEntranceBanner(footpathPos, tileElement);
|
2019-09-01 17:25:50 +02:00
|
|
|
for (Direction direction : ALL_DIRECTIONS)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6C85({ footpathPos, tileElement }, direction, flags, true, &neighbourList);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FoopathNeighbourListSort(&neighbourList);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() == TileElementType::Path && tileElement->AsPath()->IsQueue())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-01-19 17:24:23 +01:00
|
|
|
RideId rideIndex = RideId::GetNull();
|
2022-01-26 13:28:19 +01:00
|
|
|
StationIndex entranceIndex = StationIndex::GetNull();
|
2018-06-22 23:17:03 +02:00
|
|
|
for (size_t i = 0; i < neighbourList.count; i++)
|
|
|
|
{
|
2022-01-19 17:24:23 +01:00
|
|
|
if (!neighbourList.items[i].ride_index.IsNull())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-01-19 17:24:23 +01:00
|
|
|
if (rideIndex.IsNull())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
rideIndex = neighbourList.items[i].ride_index;
|
|
|
|
entranceIndex = neighbourList.items[i].entrance_index;
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else if (rideIndex != neighbourList.items[i].ride_index)
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListRemove(&neighbourList, i);
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
|
|
|
else if (
|
|
|
|
rideIndex == neighbourList.items[i].ride_index && entranceIndex != neighbourList.items[i].entrance_index
|
2022-01-26 13:28:19 +01:00
|
|
|
&& !neighbourList.items[i].entrance_index.IsNull())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathNeighbourListRemove(&neighbourList, i);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 00:28:33 +01:00
|
|
|
neighbourList.count = std::min<size_t>(neighbourList.count, 2);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
while (FootpathNeighbourListPop(&neighbourList, &neighbour))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-17 19:05:14 +01:00
|
|
|
Loc6A6C85({ footpathPos, tileElement }, neighbour.direction, flags, false, nullptr);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() == TileElementType::Path)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathConnectCorners(footpathPos, tileElement->AsPath());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-02-13 21:55:49 +01:00
|
|
|
}
|
|
|
|
|
2015-06-09 03:09:18 +02:00
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2015-06-09 03:09:18 +02:00
|
|
|
* rct2: 0x006A742F
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathChainRideQueue(
|
2022-01-26 13:28:19 +01:00
|
|
|
RideId rideIndex, StationIndex entranceIndex, const CoordsXY& initialFootpathPos, TileElement* const initialTileElement,
|
2020-01-04 12:20:03 +01:00
|
|
|
int32_t direction)
|
2015-02-16 16:16:28 +01:00
|
|
|
{
|
2018-11-01 13:53:50 +01:00
|
|
|
TileElement *lastPathElement, *lastQueuePathElement;
|
2020-01-04 12:20:03 +01:00
|
|
|
auto tileElement = initialTileElement;
|
|
|
|
auto curQueuePos = initialFootpathPos;
|
|
|
|
auto lastPath = curQueuePos;
|
|
|
|
int32_t baseZ = tileElement->GetBaseZ();
|
|
|
|
int32_t lastPathDirection = direction;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-01-29 17:06:01 +01:00
|
|
|
lastPathElement = nullptr;
|
|
|
|
lastQueuePathElement = nullptr;
|
2018-06-22 23:17:03 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() == TileElementType::Path)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
lastPathElement = tileElement;
|
2020-01-04 12:20:03 +01:00
|
|
|
lastPath = curQueuePos;
|
2017-06-06 23:24:18 +02:00
|
|
|
lastPathDirection = direction;
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-04 15:07:48 +02:00
|
|
|
if (tileElement->AsPath()->GetSlopeDirection() == direction)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-01-04 12:20:03 +01:00
|
|
|
baseZ += LAND_HEIGHT_STEP;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 12:20:03 +01:00
|
|
|
auto targetQueuePos = curQueuePos + CoordsDirectionDelta[direction];
|
2022-10-11 20:39:24 +02:00
|
|
|
tileElement = MapGetFirstElementAt(targetQueuePos);
|
2020-09-12 13:41:10 +02:00
|
|
|
bool foundQueue = false;
|
2019-03-06 20:11:21 +01:00
|
|
|
if (tileElement != nullptr)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-03-06 20:11:21 +01:00
|
|
|
do
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-03-06 20:11:21 +01:00
|
|
|
if (lastQueuePathElement == tileElement)
|
|
|
|
continue;
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2019-03-06 20:11:21 +01:00
|
|
|
continue;
|
2020-01-04 12:20:03 +01:00
|
|
|
if (tileElement->GetBaseZ() == baseZ)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-03-06 20:11:21 +01:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
|
|
|
{
|
|
|
|
if (tileElement->AsPath()->GetSlopeDirection() != direction)
|
|
|
|
break;
|
|
|
|
}
|
2020-09-12 13:41:10 +02:00
|
|
|
foundQueue = true;
|
2020-07-21 17:52:47 +02:00
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-01-04 12:20:03 +01:00
|
|
|
if (tileElement->GetBaseZ() == baseZ - LAND_HEIGHT_STEP)
|
2019-03-06 20:11:21 +01:00
|
|
|
{
|
|
|
|
if (!tileElement->AsPath()->IsSloped())
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
if (DirectionReverse(tileElement->AsPath()->GetSlopeDirection()) != direction)
|
2019-03-06 20:11:21 +01:00
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-01-04 12:20:03 +01:00
|
|
|
baseZ -= LAND_HEIGHT_STEP;
|
2020-09-12 13:41:10 +02:00
|
|
|
foundQueue = true;
|
2020-07-21 17:52:47 +02:00
|
|
|
break;
|
2019-03-06 20:11:21 +01:00
|
|
|
}
|
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
|
|
|
}
|
2020-09-12 13:41:10 +02:00
|
|
|
if (!foundQueue)
|
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-10-03 10:57:29 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Fix #2051: Stop queue paths that are already connected to two other tiles
|
|
|
|
// from connecting to the tile we are coming from.
|
2018-10-05 13:11:44 +02:00
|
|
|
int32_t edges = tileElement->AsPath()->GetEdges();
|
2023-01-18 22:42:16 +01:00
|
|
|
int32_t numEdges = BitCount(edges);
|
2018-06-22 23:17:03 +02:00
|
|
|
if (numEdges >= 2)
|
|
|
|
{
|
2022-10-04 21:12:38 +02:00
|
|
|
int32_t requiredEdgeMask = 1 << DirectionReverse(direction);
|
2018-06-22 23:17:03 +02:00
|
|
|
if (!(edges & requiredEdgeMask))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-04 15:40:55 +02:00
|
|
|
tileElement->AsPath()->SetHasQueueBanner(false);
|
2022-10-04 21:12:38 +02:00
|
|
|
tileElement->AsPath()->SetEdges(tileElement->AsPath()->GetEdges() | (1 << DirectionReverse(direction)));
|
2018-10-05 12:39:37 +02:00
|
|
|
tileElement->AsPath()->SetRideIndex(rideIndex);
|
2018-09-16 18:21:10 +02:00
|
|
|
tileElement->AsPath()->SetStationIndex(entranceIndex);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-01-04 12:20:03 +01:00
|
|
|
curQueuePos = targetQueuePos;
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateElement(targetQueuePos, tileElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
if (lastQueuePathElement == nullptr)
|
|
|
|
{
|
2017-10-31 14:03:45 +01:00
|
|
|
lastQueuePathElement = tileElement;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() & (1 << direction))
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
direction = (direction + 1) & 3;
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() & (1 << direction))
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
direction = DirectionReverse(direction);
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() & (1 << direction))
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-01-19 17:24:23 +01:00
|
|
|
if (!rideIndex.IsNull() && lastPathElement != nullptr)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-03 10:57:29 +02:00
|
|
|
if (lastPathElement->AsPath()->IsQueue())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-04 15:40:55 +02:00
|
|
|
lastPathElement->AsPath()->SetHasQueueBanner(true);
|
2018-09-16 17:24:49 +02:00
|
|
|
lastPathElement->AsPath()->SetQueueBannerDirection(lastPathDirection); // set the ride sign direction
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
MapAnimationCreate(MAP_ANIMATION_TYPE_QUEUE_BANNER, { lastPath, lastPathElement->GetBaseZ() });
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2016-05-17 22:29:05 +02:00
|
|
|
}
|
2015-07-07 03:35:53 +02:00
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathQueueChainReset()
|
2016-05-17 22:29:05 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
_footpathQueueChainNext = _footpathQueueChain;
|
2016-05-17 22:29:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006A76E9
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathQueueChainPush(RideId rideIndex)
|
2016-05-17 22:29:05 +02:00
|
|
|
{
|
2022-01-19 17:24:23 +01:00
|
|
|
if (!rideIndex.IsNull())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-04-02 14:13:33 +02:00
|
|
|
auto* lastSlot = _footpathQueueChain + std::size(_footpathQueueChain) - 1;
|
2018-06-22 23:17:03 +02:00
|
|
|
if (_footpathQueueChainNext <= lastSlot)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
*_footpathQueueChainNext++ = rideIndex;
|
|
|
|
}
|
|
|
|
}
|
2015-02-16 16:16:28 +01:00
|
|
|
}
|
|
|
|
|
2015-02-13 21:55:49 +01:00
|
|
|
/**
|
2015-10-20 20:16:30 +02:00
|
|
|
*
|
2015-02-13 21:55:49 +01:00
|
|
|
* rct2: 0x006A759F
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathUpdateQueueChains()
|
2015-02-13 21:55:49 +01:00
|
|
|
{
|
2021-04-02 14:13:33 +02:00
|
|
|
for (auto* queueChainPtr = _footpathQueueChain; queueChainPtr < _footpathQueueChainNext; queueChainPtr++)
|
2018-01-13 14:27:24 +01:00
|
|
|
{
|
2022-01-19 14:17:11 +01:00
|
|
|
RideId rideIndex = *queueChainPtr;
|
2023-01-17 01:32:54 +01:00
|
|
|
auto ride = GetRide(rideIndex);
|
2019-08-04 15:45:17 +02:00
|
|
|
if (ride == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2022-01-28 10:04:16 +01:00
|
|
|
for (const auto& station : ride->GetStations())
|
2018-01-13 14:27:24 +01:00
|
|
|
{
|
2022-01-28 10:04:16 +01:00
|
|
|
if (station.Entrance.IsNull())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(station.Entrance);
|
2019-03-06 20:11:21 +01:00
|
|
|
if (tileElement != nullptr)
|
2018-01-13 14:27:24 +01:00
|
|
|
{
|
2019-03-06 20:11:21 +01:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Entrance)
|
2019-03-06 20:11:21 +01:00
|
|
|
continue;
|
|
|
|
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE)
|
|
|
|
continue;
|
|
|
|
if (tileElement->AsEntrance()->GetRideIndex() != rideIndex)
|
|
|
|
continue;
|
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
Direction direction = DirectionReverse(tileElement->GetDirection());
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathChainRideQueue(
|
2022-01-28 10:04:16 +01:00
|
|
|
rideIndex, ride->GetStationIndex(&station), station.Entrance.ToCoordsXY(), tileElement, direction);
|
2019-03-06 20:11:21 +01:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2015-06-09 17:19:05 +02:00
|
|
|
}
|
2015-07-05 17:47:22 +02:00
|
|
|
|
2015-07-06 15:40:37 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x0069ADBD
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static void FootpathFixOwnership(const CoordsXY& mapPos)
|
2015-07-05 17:47:22 +02:00
|
|
|
{
|
2022-10-11 20:39:24 +02:00
|
|
|
const auto* surfaceElement = MapGetSurfaceElementAt(mapPos);
|
2018-06-20 17:28:51 +02:00
|
|
|
uint16_t ownership;
|
2017-06-29 11:30:18 +02:00
|
|
|
|
|
|
|
// Unlikely to be NULL unless deliberate.
|
2018-01-29 17:06:01 +01:00
|
|
|
if (surfaceElement != nullptr)
|
2017-06-29 11:30:18 +02:00
|
|
|
{
|
|
|
|
// If the tile is not safe to own construction rights of, erase them.
|
2023-01-19 20:36:30 +01:00
|
|
|
if (CheckMaxAllowableLandRightsForTile({ mapPos, surfaceElement->BaseHeight << 3 }) == OWNERSHIP_UNOWNED)
|
2017-06-29 11:30:18 +02:00
|
|
|
{
|
|
|
|
ownership = OWNERSHIP_UNOWNED;
|
|
|
|
}
|
2021-01-10 19:23:35 +01:00
|
|
|
// If the tile is safe to own construction rights of, do not erase construction rights.
|
2017-06-29 11:30:18 +02:00
|
|
|
else
|
|
|
|
{
|
2019-08-11 10:06:04 +02:00
|
|
|
ownership = surfaceElement->GetOwnership();
|
2017-06-29 11:30:18 +02:00
|
|
|
// You can't own the entrance path.
|
|
|
|
if (ownership == OWNERSHIP_OWNED || ownership == OWNERSHIP_AVAILABLE)
|
|
|
|
{
|
|
|
|
ownership = OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ownership = OWNERSHIP_UNOWNED;
|
|
|
|
}
|
|
|
|
|
2020-01-13 02:59:59 +01:00
|
|
|
auto landSetRightsAction = LandSetRightsAction(mapPos, LandSetRightSetting::SetOwnershipWithChecks, ownership);
|
2019-03-17 09:25:51 +01:00
|
|
|
landSetRightsAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND);
|
|
|
|
GameActions::Execute(&landSetRightsAction);
|
2015-07-05 17:47:22 +02:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
static bool GetNextDirection(int32_t edges, int32_t* direction)
|
2015-07-05 17:47:22 +02:00
|
|
|
{
|
2023-01-18 22:42:16 +01:00
|
|
|
int32_t index = UtilBitScanForward(edges);
|
2017-06-06 23:24:18 +02:00
|
|
|
if (index == -1)
|
|
|
|
return false;
|
2015-07-05 17:47:22 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
*direction = index;
|
|
|
|
return true;
|
2015-07-05 17:47:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x0069AC1A
|
2016-05-17 23:03:06 +02:00
|
|
|
* @param flags (1 << 0): Ignore queues
|
|
|
|
* (1 << 5): Unown
|
|
|
|
* (1 << 7): Ignore no entry signs
|
2015-07-05 17:47:22 +02:00
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static int32_t FootpathIsConnectedToMapEdgeHelper(CoordsXYZ footpathPos, int32_t direction, int32_t flags)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-08-06 21:12:50 +02:00
|
|
|
int32_t returnVal = FOOTPATH_SEARCH_INCOMPLETE;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
struct TileState
|
|
|
|
{
|
2020-08-04 09:26:59 +02:00
|
|
|
bool processed = false;
|
2020-08-04 08:10:23 +02:00
|
|
|
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;
|
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
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)
|
2020-08-04 09:26:59 +02:00
|
|
|
return;
|
2020-10-10 00:11:08 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
// If we get here we did not find it, so insert the tile into our list
|
|
|
|
tiles.push_back(t_currentTile);
|
2020-08-04 08:10:23 +02:00
|
|
|
++numPendingTiles;
|
|
|
|
};
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
// Loads the next tile to visit into our variables
|
2020-10-10 00:11:08 +02:00
|
|
|
auto LoadNextTileElement = [&tiles, &numPendingTiles](TileState& t_currentTile) -> void {
|
|
|
|
// Do not continue if there are no tiles in the list
|
|
|
|
if (tiles.size() == 0)
|
|
|
|
return;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
// Find the next unprocessed tile
|
|
|
|
for (size_t tileIndex = tiles.size() - 1; tileIndex > 0; --tileIndex)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-10-10 00:11:08 +02:00
|
|
|
if (tiles[tileIndex].processed)
|
2018-06-22 23:17:03 +02:00
|
|
|
continue;
|
2020-10-10 00:11:08 +02:00
|
|
|
--numPendingTiles;
|
|
|
|
t_currentTile = tiles[tileIndex];
|
|
|
|
tiles[tileIndex].processed = true;
|
|
|
|
return;
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
2020-10-10 00:11:08 +02:00
|
|
|
// Default to tile 0
|
|
|
|
--numPendingTiles;
|
|
|
|
t_currentTile = tiles[0];
|
|
|
|
tiles[0].processed = true;
|
2020-08-04 08:10:23 +02:00
|
|
|
};
|
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
// Encapsulate the tile skipping logic to make do-while more readable
|
2020-08-04 08:10:23 +02:00
|
|
|
auto SkipTileElement = [](int32_t ste_flags, TileElement* ste_tileElement, int32_t& ste_slopeDirection,
|
|
|
|
int32_t ste_direction, const CoordsXYZ& ste_targetPos) {
|
2021-12-11 00:39:39 +01:00
|
|
|
if (ste_tileElement->GetType() != TileElementType::Path)
|
2020-08-04 08:10:23 +02:00
|
|
|
return true;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
if (ste_tileElement->AsPath()->IsSloped()
|
|
|
|
&& (ste_slopeDirection = ste_tileElement->AsPath()->GetSlopeDirection()) != ste_direction)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-10-04 21:12:38 +02:00
|
|
|
if (DirectionReverse(ste_slopeDirection) != ste_direction)
|
2020-08-04 08:10:23 +02:00
|
|
|
return true;
|
|
|
|
if (ste_tileElement->GetBaseZ() + PATH_HEIGHT_STEP != ste_targetPos.z)
|
|
|
|
return true;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-08-04 08:10:23 +02:00
|
|
|
else if (ste_tileElement->GetBaseZ() != ste_targetPos.z)
|
|
|
|
return true;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
if (!(ste_flags & FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_QUEUES))
|
|
|
|
if (ste_tileElement->AsPath()->IsQueue())
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
};
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
int32_t edges, slopeDirection;
|
|
|
|
|
|
|
|
// Capture the current tile state to begin the loop
|
2020-10-10 00:11:08 +02:00
|
|
|
CaptureCurrentTileState(currentTile);
|
2020-08-04 08:10:23 +02:00
|
|
|
|
|
|
|
// Loop on this until all tiles are processed or we return
|
2020-10-10 00:11:08 +02:00
|
|
|
while (numPendingTiles > 0)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-10-10 00:11:08 +02:00
|
|
|
LoadNextTileElement(currentTile);
|
|
|
|
|
|
|
|
CoordsXYZ targetPos = CoordsXYZ{ CoordsXY{ currentTile.footpathPos } + CoordsDirectionDelta[currentTile.direction],
|
|
|
|
currentTile.footpathPos.z };
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
if (++currentTile.level > 250)
|
2020-08-04 08:10:23 +02:00
|
|
|
return FOOTPATH_SEARCH_TOO_COMPLEX;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
// Return immediately if we are at the edge of the map and not unowning
|
|
|
|
// Or if we are unowning and have no tiles left
|
2022-10-12 07:35:20 +02:00
|
|
|
if ((MapIsEdge(targetPos) && !(flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN)))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-10-10 00:11:08 +02:00
|
|
|
return FOOTPATH_SEARCH_SUCCESS;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-08-04 08:10:23 +02:00
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
tileElement = MapGetFirstElementAt(targetPos);
|
2020-08-04 08:10:23 +02:00
|
|
|
if (tileElement == nullptr)
|
2020-10-10 00:11:08 +02:00
|
|
|
return currentTile.level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : FOOTPATH_SEARCH_INCOMPLETE;
|
2020-08-04 08:10:23 +02:00
|
|
|
|
|
|
|
// Loop while there are unvisited TileElements at targetPos
|
|
|
|
do
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-10-10 00:11:08 +02:00
|
|
|
if (SkipTileElement(flags, tileElement, slopeDirection, currentTile.direction, targetPos))
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
// Unown the footpath if needed
|
|
|
|
if (flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN)
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathFixOwnership(targetPos);
|
2020-08-04 08:10:23 +02:00
|
|
|
|
|
|
|
edges = tileElement->AsPath()->GetEdges();
|
2022-10-04 21:12:38 +02:00
|
|
|
currentTile.direction = DirectionReverse(currentTile.direction);
|
2021-07-27 12:02:34 +02:00
|
|
|
if (!tileElement->IsLastForTile() && !(flags & FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_NO_ENTRY))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-10-10 00:11:08 +02:00
|
|
|
int elementIndex = 1;
|
|
|
|
// Loop over all elements and cull appropriate edges
|
|
|
|
do
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement[elementIndex].GetType() == TileElementType::Path)
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement[elementIndex].GetType() != TileElementType::Banner)
|
2020-10-10 00:11:08 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
edges &= tileElement[elementIndex].AsBanner()->GetAllowedEdges();
|
2021-07-27 12:02:34 +02:00
|
|
|
} while (!tileElement[elementIndex++].IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-05 17:47:22 +02:00
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
// Exclude the direction we came from
|
|
|
|
targetPos.z = tileElement->GetBaseZ();
|
2020-10-10 00:11:08 +02:00
|
|
|
edges &= ~(1 << currentTile.direction);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
if (!GetNextDirection(edges, ¤tTile.direction))
|
2020-10-10 00:11:08 +02:00
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
edges &= ~(1 << currentTile.direction);
|
2020-08-04 08:10:23 +02:00
|
|
|
if (edges == 0)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-08-04 08:10:23 +02:00
|
|
|
// Only possible direction to go
|
2020-10-10 00:11:08 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped() && tileElement->AsPath()->GetSlopeDirection() == currentTile.direction)
|
2020-08-04 07:19:49 +02:00
|
|
|
targetPos.z += PATH_HEIGHT_STEP;
|
|
|
|
|
2020-08-04 08:10:23 +02:00
|
|
|
// Prepare the next iteration
|
2020-10-10 00:11:08 +02:00
|
|
|
currentTile.footpathPos = targetPos;
|
|
|
|
++currentTile.distanceFromJunction;
|
|
|
|
CaptureCurrentTileState(currentTile);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-08-04 08:10:23 +02:00
|
|
|
else
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2020-08-04 08:10:23 +02:00
|
|
|
// We have reached a junction
|
2020-10-10 00:11:08 +02:00
|
|
|
--currentTile.junctionTolerance;
|
|
|
|
if (currentTile.distanceFromJunction != 0)
|
|
|
|
{
|
|
|
|
--currentTile.junctionTolerance;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentTile.junctionTolerance < 0 && !(flags & FOOTPATH_CONNECTED_MAP_EDGE_UNOWN))
|
|
|
|
{
|
|
|
|
returnVal = FOOTPATH_SEARCH_TOO_COMPLEX;
|
|
|
|
break;
|
|
|
|
}
|
2020-08-04 08:10:23 +02:00
|
|
|
|
|
|
|
// Loop until there are no more directions we can go
|
|
|
|
do
|
|
|
|
{
|
2020-10-10 00:11:08 +02:00
|
|
|
edges &= ~(1 << currentTile.direction);
|
|
|
|
if (tileElement->AsPath()->IsSloped()
|
|
|
|
&& tileElement->AsPath()->GetSlopeDirection() == currentTile.direction)
|
|
|
|
{
|
2020-08-04 08:10:23 +02:00
|
|
|
targetPos.z += PATH_HEIGHT_STEP;
|
2020-10-10 00:11:08 +02:00
|
|
|
}
|
2020-08-04 08:10:23 +02:00
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
// Add each possible path to the list of pending tiles
|
|
|
|
currentTile.footpathPos = targetPos;
|
|
|
|
currentTile.distanceFromJunction = 0;
|
|
|
|
CaptureCurrentTileState(currentTile);
|
2022-10-04 08:51:27 +02:00
|
|
|
} while (GetNextDirection(edges, ¤tTile.direction));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-08-06 21:12:50 +02:00
|
|
|
break;
|
2020-08-04 08:10:23 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-10-10 00:11:08 +02:00
|
|
|
// 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;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2020-10-10 00:11:08 +02:00
|
|
|
return currentTile.level == 1 ? FOOTPATH_SEARCH_NOT_FOUND : returnVal;
|
2015-07-05 17:47:22 +02:00
|
|
|
}
|
|
|
|
|
2020-01-04 13:09:07 +01:00
|
|
|
// TODO: Use GAME_COMMAND_FLAGS
|
2022-10-04 08:51:27 +02:00
|
|
|
int32_t FootpathIsConnectedToMapEdge(const CoordsXYZ& footpathPos, int32_t direction, int32_t flags)
|
2015-07-05 17:47:22 +02:00
|
|
|
{
|
2020-04-20 10:39:26 +02:00
|
|
|
flags |= FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_QUEUES;
|
2022-10-04 08:51:27 +02:00
|
|
|
return FootpathIsConnectedToMapEdgeHelper(footpathPos, direction, flags);
|
2015-07-05 17:47:22 +02:00
|
|
|
}
|
|
|
|
|
2018-09-16 16:17:35 +02:00
|
|
|
bool PathElement::IsSloped() const
|
2017-12-08 16:47:54 +01:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return (Flags2 & FOOTPATH_ELEMENT_FLAGS2_IS_SLOPED) != 0;
|
2017-12-08 16:47:54 +01:00
|
|
|
}
|
|
|
|
|
2018-09-16 16:17:35 +02:00
|
|
|
void PathElement::SetSloped(bool isSloped)
|
2017-12-08 16:47:54 +01:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_IS_SLOPED;
|
2017-12-08 16:47:54 +01:00
|
|
|
if (isSloped)
|
2020-03-04 18:43:09 +01:00
|
|
|
Flags2 |= FOOTPATH_ELEMENT_FLAGS2_IS_SLOPED;
|
2017-12-08 16:47:54 +01:00
|
|
|
}
|
|
|
|
|
2023-03-23 09:16:32 +01:00
|
|
|
bool PathElement::HasJunctionRailings() const
|
|
|
|
{
|
|
|
|
return Flags2 & FOOTPATH_ELEMENT_FLAGS2_HAS_JUNCTION_RAILINGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetJunctionRailings(bool hasJunctionRailings)
|
|
|
|
{
|
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_HAS_JUNCTION_RAILINGS;
|
|
|
|
if (hasJunctionRailings)
|
|
|
|
Flags2 |= FOOTPATH_ELEMENT_FLAGS2_HAS_JUNCTION_RAILINGS;
|
|
|
|
}
|
|
|
|
|
2019-08-26 11:58:24 +02:00
|
|
|
Direction PathElement::GetSlopeDirection() const
|
2015-07-05 17:47:22 +02:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return SlopeDirection;
|
2018-10-04 15:07:48 +02:00
|
|
|
}
|
|
|
|
|
2019-08-26 11:58:24 +02:00
|
|
|
void PathElement::SetSlopeDirection(Direction newSlope)
|
2018-10-04 15:07:48 +02:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
SlopeDirection = newSlope;
|
2015-07-05 17:47:22 +02:00
|
|
|
}
|
|
|
|
|
2018-10-03 10:57:29 +02:00
|
|
|
bool PathElement::IsQueue() const
|
2015-07-05 17:47:22 +02:00
|
|
|
{
|
2023-01-21 16:37:11 +01:00
|
|
|
return (Type & FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE) != 0;
|
2018-01-03 10:57:37 +01:00
|
|
|
}
|
|
|
|
|
2018-10-03 10:57:29 +02:00
|
|
|
void PathElement::SetIsQueue(bool isQueue)
|
2018-03-08 14:14:44 +01:00
|
|
|
{
|
2023-01-21 16:37:11 +01:00
|
|
|
Type &= ~FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE;
|
2018-10-03 10:57:29 +02:00
|
|
|
if (isQueue)
|
2023-01-21 16:37:11 +01:00
|
|
|
Type |= FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE;
|
2018-03-08 14:14:44 +01:00
|
|
|
}
|
|
|
|
|
2018-09-16 16:32:06 +02:00
|
|
|
bool PathElement::HasQueueBanner() const
|
2018-01-03 10:57:37 +01:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return (Flags2 & FOOTPATH_ELEMENT_FLAGS2_HAS_QUEUE_BANNER) != 0;
|
2015-07-05 17:47:22 +02:00
|
|
|
}
|
2015-07-06 23:12:45 +02:00
|
|
|
|
2018-10-04 15:40:55 +02:00
|
|
|
void PathElement::SetHasQueueBanner(bool hasQueueBanner)
|
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_HAS_QUEUE_BANNER;
|
2018-10-04 15:40:55 +02:00
|
|
|
if (hasQueueBanner)
|
2020-03-04 18:43:09 +01:00
|
|
|
Flags2 |= FOOTPATH_ELEMENT_FLAGS2_HAS_QUEUE_BANNER;
|
2018-10-04 15:40:55 +02:00
|
|
|
}
|
|
|
|
|
2019-02-26 10:08:22 +01:00
|
|
|
bool PathElement::IsBroken() const
|
|
|
|
{
|
2020-03-04 21:47:34 +01:00
|
|
|
return (Flags2 & FOOTPATH_ELEMENT_FLAGS2_ADDITION_IS_BROKEN) != 0;
|
2019-02-26 10:08:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetIsBroken(bool isBroken)
|
|
|
|
{
|
2019-04-20 20:39:47 +02:00
|
|
|
if (isBroken)
|
2019-02-26 10:08:22 +01:00
|
|
|
{
|
2020-03-04 21:47:34 +01:00
|
|
|
Flags2 |= FOOTPATH_ELEMENT_FLAGS2_ADDITION_IS_BROKEN;
|
2019-02-26 10:08:22 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-04 21:47:34 +01:00
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_ADDITION_IS_BROKEN;
|
2019-02-26 10:08:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PathElement::IsBlockedByVehicle() const
|
|
|
|
{
|
2020-03-04 21:47:34 +01:00
|
|
|
return (Flags2 & FOOTPATH_ELEMENT_FLAGS2_BLOCKED_BY_VEHICLE) != 0;
|
2019-02-26 10:08:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetIsBlockedByVehicle(bool isBlocked)
|
|
|
|
{
|
2019-04-20 20:39:47 +02:00
|
|
|
if (isBlocked)
|
2019-02-26 10:08:22 +01:00
|
|
|
{
|
2020-03-04 21:47:34 +01:00
|
|
|
Flags2 |= FOOTPATH_ELEMENT_FLAGS2_BLOCKED_BY_VEHICLE;
|
2019-02-26 10:08:22 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-04 21:47:34 +01:00
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_BLOCKED_BY_VEHICLE;
|
2019-02-26 10:08:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 21:38:26 +01:00
|
|
|
::StationIndex PathElement::GetStationIndex() const
|
2018-09-16 18:21:10 +02:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return StationIndex;
|
2018-09-16 18:21:10 +02:00
|
|
|
}
|
|
|
|
|
2020-03-04 18:43:09 +01:00
|
|
|
void PathElement::SetStationIndex(::StationIndex newStationIndex)
|
2018-09-16 18:21:10 +02:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
StationIndex = newStationIndex;
|
2018-09-16 18:21:10 +02:00
|
|
|
}
|
|
|
|
|
2018-09-16 16:40:03 +02:00
|
|
|
bool PathElement::IsWide() const
|
2015-07-06 23:12:45 +02:00
|
|
|
{
|
2023-01-21 16:37:11 +01:00
|
|
|
return (Type & FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE) != 0;
|
2015-07-06 23:12:45 +02:00
|
|
|
}
|
|
|
|
|
2018-09-16 16:40:03 +02:00
|
|
|
void PathElement::SetWide(bool isWide)
|
2015-07-31 11:42:40 +02:00
|
|
|
{
|
2023-01-21 16:37:11 +01:00
|
|
|
Type &= ~FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE;
|
2017-12-08 16:47:54 +01:00
|
|
|
if (isWide)
|
2023-01-21 16:37:11 +01:00
|
|
|
Type |= FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE;
|
2015-07-31 11:42:40 +02:00
|
|
|
}
|
|
|
|
|
2018-09-16 17:08:15 +02:00
|
|
|
bool PathElement::HasAddition() const
|
2015-12-03 16:14:30 +01:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return Additions != 0;
|
2015-12-03 16:14:30 +01:00
|
|
|
}
|
|
|
|
|
2018-09-16 17:08:15 +02:00
|
|
|
uint8_t PathElement::GetAddition() const
|
2015-12-03 16:14:30 +01:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return Additions;
|
2015-12-03 16:14:30 +01:00
|
|
|
}
|
|
|
|
|
2020-04-27 15:01:37 +02:00
|
|
|
ObjectEntryIndex PathElement::GetAdditionEntryIndex() const
|
2015-12-05 00:08:34 +01:00
|
|
|
{
|
2018-09-16 17:08:15 +02:00
|
|
|
return GetAddition() - 1;
|
2015-12-05 00:08:34 +01:00
|
|
|
}
|
|
|
|
|
2023-02-15 22:35:16 +01:00
|
|
|
const PathBitEntry* PathElement::GetAdditionEntry() const
|
2015-12-03 16:14:30 +01:00
|
|
|
{
|
2020-07-24 15:58:17 +02:00
|
|
|
if (!HasAddition())
|
|
|
|
return nullptr;
|
2023-02-15 22:35:16 +01:00
|
|
|
return OpenRCT2::ObjectManager::GetObjectEntry<PathBitEntry>(GetAdditionEntryIndex());
|
2018-09-16 17:08:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetAddition(uint8_t newAddition)
|
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
Additions = newAddition;
|
2015-12-03 23:49:26 +01:00
|
|
|
}
|
|
|
|
|
2018-09-16 16:46:23 +02:00
|
|
|
bool PathElement::AdditionIsGhost() const
|
2015-12-04 21:09:49 +01:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return (Flags2 & FOOTPATH_ELEMENT_FLAGS2_ADDITION_IS_GHOST) != 0;
|
2015-12-04 21:09:49 +01:00
|
|
|
}
|
|
|
|
|
2018-09-16 16:46:23 +02:00
|
|
|
void PathElement::SetAdditionIsGhost(bool isGhost)
|
2015-12-04 21:09:49 +01:00
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_ADDITION_IS_GHOST;
|
2017-06-06 23:24:18 +02:00
|
|
|
if (isGhost)
|
2020-03-04 18:43:09 +01:00
|
|
|
Flags2 |= FOOTPATH_ELEMENT_FLAGS2_ADDITION_IS_GHOST;
|
2015-12-04 21:09:49 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 18:44:43 +02:00
|
|
|
ObjectEntryIndex PathElement::GetLegacyPathEntryIndex() const
|
2015-12-03 23:49:26 +01:00
|
|
|
{
|
2021-09-06 21:59:17 +02:00
|
|
|
if (Flags2 & FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY)
|
|
|
|
return SurfaceIndex;
|
2021-09-15 22:22:15 +02:00
|
|
|
|
|
|
|
return OBJECT_ENTRY_INDEX_NULL;
|
2017-12-08 16:47:54 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 18:44:43 +02:00
|
|
|
const FootpathObject* PathElement::GetLegacyPathEntry() const
|
2021-09-10 09:13:20 +02:00
|
|
|
{
|
2021-09-05 18:44:43 +02:00
|
|
|
return GetLegacyFootpathEntry(GetLegacyPathEntryIndex());
|
2021-09-10 09:13:20 +02:00
|
|
|
}
|
2018-09-16 17:19:56 +02:00
|
|
|
|
2021-09-06 21:59:17 +02:00
|
|
|
void PathElement::SetLegacyPathEntryIndex(ObjectEntryIndex newIndex)
|
|
|
|
{
|
2021-09-03 19:19:42 +02:00
|
|
|
SurfaceIndex = newIndex;
|
2021-09-08 13:39:11 +02:00
|
|
|
RailingsIndex = OBJECT_ENTRY_INDEX_NULL;
|
2021-09-06 21:59:17 +02:00
|
|
|
Flags2 |= FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PathElement::HasLegacyPathEntry() const
|
|
|
|
{
|
|
|
|
return (Flags2 & FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY) != 0;
|
|
|
|
}
|
|
|
|
|
2021-09-05 18:44:43 +02:00
|
|
|
const PathSurfaceDescriptor* PathElement::GetSurfaceDescriptor() const
|
2019-01-02 19:52:53 +01:00
|
|
|
{
|
2021-09-06 21:59:17 +02:00
|
|
|
if (HasLegacyPathEntry())
|
|
|
|
{
|
|
|
|
const auto* legacyPathEntry = GetLegacyPathEntry();
|
|
|
|
if (legacyPathEntry == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (IsQueue())
|
|
|
|
return &legacyPathEntry->GetQueueSurfaceDescriptor();
|
2019-01-02 19:52:53 +01:00
|
|
|
|
2021-09-06 21:59:17 +02:00
|
|
|
return &legacyPathEntry->GetPathSurfaceDescriptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto* surfaceEntry = GetSurfaceEntry();
|
|
|
|
if (surfaceEntry == nullptr)
|
|
|
|
return nullptr;
|
2021-09-05 18:44:43 +02:00
|
|
|
|
2021-09-06 21:59:17 +02:00
|
|
|
return &surfaceEntry->GetDescriptor();
|
2019-01-02 19:52:53 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 18:44:43 +02:00
|
|
|
const PathRailingsDescriptor* PathElement::GetRailingsDescriptor() const
|
2018-09-16 17:19:56 +02:00
|
|
|
{
|
2021-09-06 21:59:17 +02:00
|
|
|
if (HasLegacyPathEntry())
|
|
|
|
{
|
|
|
|
const auto* legacyPathEntry = GetLegacyPathEntry();
|
|
|
|
if (legacyPathEntry == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return &legacyPathEntry->GetPathRailingsDescriptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto* railingsEntry = GetRailingsEntry();
|
|
|
|
if (railingsEntry == nullptr)
|
2021-09-05 18:44:43 +02:00
|
|
|
return nullptr;
|
|
|
|
|
2021-09-06 21:59:17 +02:00
|
|
|
return &railingsEntry->GetDescriptor();
|
2017-12-08 16:47:54 +01:00
|
|
|
}
|
|
|
|
|
2021-09-06 21:59:17 +02:00
|
|
|
ObjectEntryIndex PathElement::GetSurfaceEntryIndex() const
|
2019-01-02 19:52:53 +01:00
|
|
|
{
|
2021-09-06 21:59:17 +02:00
|
|
|
if (Flags2 & FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY)
|
|
|
|
return OBJECT_ENTRY_INDEX_NULL;
|
2021-09-15 22:22:15 +02:00
|
|
|
|
|
|
|
return SurfaceIndex;
|
2021-09-06 21:59:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const FootpathSurfaceObject* PathElement::GetSurfaceEntry() const
|
|
|
|
{
|
|
|
|
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
|
|
|
|
return static_cast<FootpathSurfaceObject*>(objMgr.GetLoadedObject(ObjectType::FootpathSurface, GetSurfaceEntryIndex()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetSurfaceEntryIndex(ObjectEntryIndex newIndex)
|
|
|
|
{
|
|
|
|
SurfaceIndex = newIndex;
|
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjectEntryIndex PathElement::GetRailingsEntryIndex() const
|
|
|
|
{
|
|
|
|
if (Flags2 & FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY)
|
|
|
|
return OBJECT_ENTRY_INDEX_NULL;
|
2021-09-15 22:22:15 +02:00
|
|
|
|
|
|
|
return RailingsIndex;
|
2021-09-06 21:59:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const FootpathRailingsObject* PathElement::GetRailingsEntry() const
|
|
|
|
{
|
|
|
|
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
|
|
|
|
return static_cast<FootpathRailingsObject*>(objMgr.GetLoadedObject(ObjectType::FootpathRailings, GetRailingsEntryIndex()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetRailingsEntryIndex(ObjectEntryIndex newEntryIndex)
|
|
|
|
{
|
|
|
|
RailingsIndex = newEntryIndex;
|
|
|
|
Flags2 &= ~FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
|
2019-01-02 19:52:53 +01:00
|
|
|
}
|
|
|
|
|
2018-09-16 17:24:49 +02:00
|
|
|
uint8_t PathElement::GetQueueBannerDirection() const
|
2017-12-08 16:47:54 +01:00
|
|
|
{
|
2023-01-21 16:37:11 +01:00
|
|
|
return ((Type & FOOTPATH_ELEMENT_TYPE_DIRECTION_MASK) >> 6);
|
2017-12-08 16:47:54 +01:00
|
|
|
}
|
|
|
|
|
2018-09-16 17:24:49 +02:00
|
|
|
void PathElement::SetQueueBannerDirection(uint8_t direction)
|
2017-12-08 16:47:54 +01:00
|
|
|
{
|
2023-01-21 16:37:11 +01:00
|
|
|
Type &= ~FOOTPATH_ELEMENT_TYPE_DIRECTION_MASK;
|
|
|
|
Type |= (direction << 6);
|
2015-12-03 16:14:30 +01:00
|
|
|
}
|
|
|
|
|
2020-11-04 19:15:44 +01:00
|
|
|
bool PathElement::ShouldDrawPathOverSupports() const
|
2019-01-20 12:26:57 +01:00
|
|
|
{
|
2021-09-03 19:19:42 +02:00
|
|
|
// TODO: make this an actual decision of the tile element.
|
2021-09-05 18:44:43 +02:00
|
|
|
return (GetRailingsDescriptor()->Flags & RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS);
|
2019-01-20 12:26:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetShouldDrawPathOverSupports(bool on)
|
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_VERBOSE("Setting 'draw path over supports' to %d", static_cast<size_t>(on));
|
2019-01-20 12:26:57 +01:00
|
|
|
}
|
|
|
|
|
2015-11-14 01:08:31 +01:00
|
|
|
/**
|
2018-06-22 23:17:03 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006A8B12
|
|
|
|
* clears the wide footpath flag for all footpaths
|
|
|
|
* at location
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static void FootpathClearWide(const CoordsXY& footpathPos)
|
2015-11-14 01:08:31 +01:00
|
|
|
{
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(footpathPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return;
|
2017-12-08 16:47:54 +01:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2018-09-16 16:40:03 +02:00
|
|
|
tileElement->AsPath()->SetWide(false);
|
2018-06-22 23:17:03 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2015-11-14 01:08:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-06-22 23:17:03 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006A8ACF
|
|
|
|
* returns footpath element if it can be made wide
|
|
|
|
* returns NULL if it can not be made wide
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static TileElement* FootpathCanBeWide(const CoordsXYZ& footpathPos)
|
2015-11-14 01:08:31 +01:00
|
|
|
{
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(footpathPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return nullptr;
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2020-03-13 12:03:43 +01:00
|
|
|
if (footpathPos.z != tileElement->GetBaseZ())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2018-10-03 10:57:29 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2017-10-31 14:03:45 +01:00
|
|
|
return tileElement;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2015-11-14 01:08:31 +01:00
|
|
|
|
2018-01-29 17:06:01 +01:00
|
|
|
return nullptr;
|
2015-11-14 01:08:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-06-22 23:17:03 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006A87BB
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathUpdatePathWideFlags(const CoordsXY& footpathPos)
|
2015-11-14 01:08:31 +01:00
|
|
|
{
|
2022-10-12 14:14:45 +02:00
|
|
|
if (MapIsLocationAtEdge(footpathPos))
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathClearWide(footpathPos);
|
2017-06-06 23:24:18 +02:00
|
|
|
/* Rather than clearing the wide flag of the following tiles and
|
|
|
|
* checking the state of them later, leave them intact and assume
|
|
|
|
* they were cleared. Consequently only the wide flag for this single
|
|
|
|
* tile is modified by this update.
|
|
|
|
* This is important for avoiding glitches in pathfinding that occurs
|
|
|
|
* between the batches of updates to the path wide flags.
|
|
|
|
* Corresponding pathList[] indexes for the following tiles
|
|
|
|
* are: 2, 3, 4, 5.
|
|
|
|
* Note: indexes 3, 4, 5 are reset in the current call;
|
|
|
|
* index 2 is reset in the previous call. */
|
2018-06-22 23:17:03 +02:00
|
|
|
// x += 0x20;
|
2022-10-04 08:51:27 +02:00
|
|
|
// FootpathClearWide(x, y);
|
2018-06-22 23:17:03 +02:00
|
|
|
// y += 0x20;
|
2022-10-04 08:51:27 +02:00
|
|
|
// FootpathClearWide(x, y);
|
2018-06-22 23:17:03 +02:00
|
|
|
// x -= 0x20;
|
2022-10-04 08:51:27 +02:00
|
|
|
// FootpathClearWide(x, y);
|
2018-06-22 23:17:03 +02:00
|
|
|
// y -= 0x20;
|
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(footpathPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return;
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-10-03 10:57:29 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() == 0)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2020-03-13 12:03:43 +01:00
|
|
|
auto height = tileElement->GetBaseZ();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2023-01-17 19:05:14 +01:00
|
|
|
// pathList is a list of elements, set by Sub6A8ACF adjacent to x,y
|
2017-06-06 23:24:18 +02:00
|
|
|
// Spanned from 0x00F3EFA8 to 0x00F3EFC7 (8 elements) in the original
|
2020-01-18 15:00:01 +01:00
|
|
|
std::array<TileElement*, 8> pathList;
|
2022-10-17 18:08:55 +02:00
|
|
|
for (std::size_t direction = 0; direction < pathList.size(); ++direction)
|
2020-01-15 01:51:38 +01:00
|
|
|
{
|
2020-03-13 12:03:43 +01:00
|
|
|
auto footpathLoc = CoordsXYZ(footpathPos + CoordsDirectionDelta[direction], height);
|
2022-10-04 08:51:27 +02:00
|
|
|
pathList[direction] = FootpathCanBeWide(footpathLoc);
|
2020-01-15 01:51:38 +01:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-01-29 19:07:00 +01:00
|
|
|
uint8_t pathConnections = 0;
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() & EDGE_NW)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_NW;
|
2021-11-25 10:13:20 +01:00
|
|
|
const auto* pathElement = std::get<3>(pathList);
|
|
|
|
if (pathElement != nullptr && pathElement->AsPath()->IsWide())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections &= ~FOOTPATH_CONNECTION_NW;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() & EDGE_NE)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_NE;
|
2021-11-25 10:13:20 +01:00
|
|
|
const auto* pathElement = std::get<0>(pathList);
|
|
|
|
if (pathElement != nullptr && pathElement->AsPath()->IsWide())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections &= ~FOOTPATH_CONNECTION_NE;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() & EDGE_SE)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_SE;
|
2017-06-06 23:24:18 +02:00
|
|
|
/* In the following:
|
2020-01-18 15:00:01 +01:00
|
|
|
* footpath_element_is_wide(pathList[1])
|
2017-06-06 23:24:18 +02:00
|
|
|
* is always false due to the tile update order
|
|
|
|
* in combination with reset tiles.
|
|
|
|
* Commented out since it will never occur. */
|
2020-01-18 15:00:01 +01:00
|
|
|
// if (pathList[1] != nullptr) {
|
|
|
|
// if (footpath_element_is_wide(pathList[1])) {
|
2019-01-29 19:07:00 +01:00
|
|
|
// pathConnections &= ~FOOTPATH_CONNECTION_SE;
|
2017-06-06 23:24:18 +02:00
|
|
|
// }
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:11:44 +02:00
|
|
|
if (tileElement->AsPath()->GetEdges() & EDGE_SW)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_SW;
|
2017-06-06 23:24:18 +02:00
|
|
|
/* In the following:
|
2020-01-18 15:00:01 +01:00
|
|
|
* footpath_element_is_wide(pathList[2])
|
2017-06-06 23:24:18 +02:00
|
|
|
* is always false due to the tile update order
|
|
|
|
* in combination with reset tiles.
|
|
|
|
* Commented out since it will never occur. */
|
2020-01-18 15:00:01 +01:00
|
|
|
// if (pathList[2] != nullptr) {
|
|
|
|
// if (footpath_element_is_wide(pathList[2])) {
|
2019-01-29 19:07:00 +01:00
|
|
|
// pathConnections &= ~FOOTPATH_CONNECTION_SW;
|
2017-06-06 23:24:18 +02:00
|
|
|
// }
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
2021-11-25 10:13:20 +01:00
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_NW) && std::get<3>(pathList) != nullptr
|
|
|
|
&& !std::get<3>(pathList)->AsPath()->IsWide())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
constexpr uint8_t edgeMask1 = EDGE_SE | EDGE_SW;
|
2021-11-25 10:13:20 +01:00
|
|
|
const auto* pathElement0 = std::get<0>(pathList);
|
|
|
|
const auto* pathElement7 = std::get<7>(pathList);
|
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_NE) && pathElement7 != nullptr && !pathElement7->AsPath()->IsWide()
|
|
|
|
&& (pathElement7->AsPath()->GetEdges() & edgeMask1) == edgeMask1 && pathElement0 != nullptr
|
|
|
|
&& !pathElement0->AsPath()->IsWide())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_S;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* In the following:
|
2020-01-18 15:00:01 +01:00
|
|
|
* footpath_element_is_wide(pathList[2])
|
2017-06-06 23:24:18 +02:00
|
|
|
* is always false due to the tile update order
|
|
|
|
* in combination with reset tiles.
|
|
|
|
* Short circuit the logic appropriately. */
|
2019-01-29 19:07:00 +01:00
|
|
|
constexpr uint8_t edgeMask2 = EDGE_NE | EDGE_SE;
|
2021-11-25 10:13:20 +01:00
|
|
|
const auto* pathElement2 = std::get<2>(pathList);
|
|
|
|
const auto* pathElement6 = std::get<6>(pathList);
|
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_SW) && pathElement6 != nullptr && !(pathElement6)->AsPath()->IsWide()
|
|
|
|
&& (pathElement6->AsPath()->GetEdges() & edgeMask2) == edgeMask2 && pathElement2 != nullptr)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_E;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In the following:
|
2020-01-18 15:00:01 +01:00
|
|
|
* footpath_element_is_wide(pathList[4])
|
|
|
|
* footpath_element_is_wide(pathList[1])
|
2017-06-06 23:24:18 +02:00
|
|
|
* are always false due to the tile update order
|
|
|
|
* in combination with reset tiles.
|
|
|
|
* Short circuit the logic appropriately. */
|
2021-11-25 10:13:20 +01:00
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_SE) && std::get<1>(pathList) != nullptr)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
constexpr uint8_t edgeMask1 = EDGE_SW | EDGE_NW;
|
2021-11-25 10:13:20 +01:00
|
|
|
const auto* pathElement0 = std::get<0>(pathList);
|
|
|
|
const auto* pathElement4 = std::get<4>(pathList);
|
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_NE) && (pathElement4 != nullptr)
|
|
|
|
&& (pathElement4->AsPath()->GetEdges() & edgeMask1) == edgeMask1 && pathElement0 != nullptr
|
|
|
|
&& !pathElement0->AsPath()->IsWide())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_W;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* In the following:
|
|
|
|
* footpath_element_is_wide(pathList[5])
|
2020-01-18 15:00:01 +01:00
|
|
|
* footpath_element_is_wide(pathList[2])
|
2017-06-06 23:24:18 +02:00
|
|
|
* are always false due to the tile update order
|
|
|
|
* in combination with reset tiles.
|
|
|
|
* Short circuit the logic appropriately. */
|
2019-01-29 19:07:00 +01:00
|
|
|
constexpr uint8_t edgeMask2 = EDGE_NE | EDGE_NW;
|
2021-11-25 10:13:20 +01:00
|
|
|
const auto* pathElement2 = std::get<2>(pathList);
|
|
|
|
const auto* pathElement5 = std::get<5>(pathList);
|
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_SW) && pathElement5 != nullptr
|
|
|
|
&& (pathElement5->AsPath()->GetEdges() & edgeMask2) == edgeMask2 && pathElement2 != nullptr)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-01-29 19:07:00 +01:00
|
|
|
pathConnections |= FOOTPATH_CONNECTION_N;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 19:07:00 +01:00
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_NW) && (pathConnections & (FOOTPATH_CONNECTION_E | FOOTPATH_CONNECTION_S)))
|
|
|
|
{
|
|
|
|
pathConnections &= ~FOOTPATH_CONNECTION_NW;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-01-29 19:07:00 +01:00
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_NE) && (pathConnections & (FOOTPATH_CONNECTION_W | FOOTPATH_CONNECTION_S)))
|
|
|
|
{
|
|
|
|
pathConnections &= ~FOOTPATH_CONNECTION_NE;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-01-29 19:07:00 +01:00
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_SE) && (pathConnections & (FOOTPATH_CONNECTION_N | FOOTPATH_CONNECTION_W)))
|
|
|
|
{
|
|
|
|
pathConnections &= ~FOOTPATH_CONNECTION_SE;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-01-29 19:07:00 +01:00
|
|
|
if ((pathConnections & FOOTPATH_CONNECTION_SW) && (pathConnections & (FOOTPATH_CONNECTION_E | FOOTPATH_CONNECTION_N)))
|
|
|
|
{
|
|
|
|
pathConnections &= ~FOOTPATH_CONNECTION_SW;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-01-29 19:07:00 +01:00
|
|
|
if (!(pathConnections
|
|
|
|
& (FOOTPATH_CONNECTION_NE | FOOTPATH_CONNECTION_SE | FOOTPATH_CONNECTION_SW | FOOTPATH_CONNECTION_NW)))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-05 13:33:46 +02:00
|
|
|
uint8_t e = tileElement->AsPath()->GetEdgesAndCorners();
|
2017-12-08 16:47:54 +01:00
|
|
|
if ((e != 0b10101111) && (e != 0b01011111) && (e != 0b11101111))
|
2018-09-16 16:40:03 +02:00
|
|
|
tileElement->AsPath()->SetWide(true);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2015-11-14 01:08:31 +01:00
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
bool FootpathIsBlockedByVehicle(const TileCoordsXYZ& position)
|
2018-06-07 19:42:56 +02:00
|
|
|
{
|
2022-09-30 20:00:59 +02:00
|
|
|
auto pathElement = MapGetFirstTileElementWithBaseHeightBetween<PathElement>({ position, position.z + PATH_HEIGHT_STEP });
|
2019-08-13 20:17:15 +02:00
|
|
|
return pathElement != nullptr && pathElement->IsBlockedByVehicle();
|
2018-06-07 19:42:56 +02:00
|
|
|
}
|
|
|
|
|
2015-07-07 03:35:53 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006A7642
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathUpdateQueueEntranceBanner(const CoordsXY& footpathPos, TileElement* tileElement)
|
2015-07-07 03:35:53 +02:00
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
const auto elementType = tileElement->GetType();
|
|
|
|
if (elementType == TileElementType::Path)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-12-10 22:43:17 +01:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathQueueChainPush(tileElement->AsPath()->GetRideIndex());
|
2022-10-17 18:08:55 +02:00
|
|
|
for (int32_t direction = 0; direction < NumOrthogonalDirections; direction++)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2021-12-10 22:43:17 +01:00
|
|
|
if (tileElement->AsPath()->GetEdges() & (1 << direction))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathChainRideQueue(
|
2022-01-26 13:28:19 +01:00
|
|
|
RideId::GetNull(), StationIndex::FromUnderlying(0), footpathPos, tileElement, direction);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
}
|
2022-01-19 17:24:23 +01:00
|
|
|
tileElement->AsPath()->SetRideIndex(RideId::GetNull());
|
2021-12-10 22:43:17 +01:00
|
|
|
}
|
|
|
|
}
|
2021-12-11 00:39:39 +01:00
|
|
|
else if (elementType == TileElementType::Entrance)
|
2021-12-10 22:43:17 +01:00
|
|
|
{
|
|
|
|
if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE)
|
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathQueueChainPush(tileElement->AsEntrance()->GetRideIndex());
|
|
|
|
FootpathChainRideQueue(
|
2022-01-26 13:28:19 +01:00
|
|
|
RideId::GetNull(), StationIndex::FromUnderlying(0), footpathPos, tileElement,
|
2022-10-04 21:12:38 +02:00
|
|
|
DirectionReverse(tileElement->GetDirection()));
|
2021-12-10 22:43:17 +01:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-07 03:35:53 +02:00
|
|
|
}
|
|
|
|
|
2015-07-06 23:12:45 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006A6B7F
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static void FootpathRemoveEdgesTowardsHere(
|
2020-01-04 12:47:01 +01:00
|
|
|
const CoordsXYZ& footpathPos, int32_t direction, TileElement* tileElement, bool isQueue)
|
2015-07-06 23:12:45 +02:00
|
|
|
{
|
2018-10-03 10:57:29 +02:00
|
|
|
if (tileElement->AsPath()->IsQueue())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathQueueChainPush(tileElement->AsPath()->GetRideIndex());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-07-06 23:12:45 +02:00
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
auto d = DirectionReverse(direction);
|
2018-10-05 13:33:46 +02:00
|
|
|
tileElement->AsPath()->SetEdges(tileElement->AsPath()->GetEdges() & ~(1 << d));
|
|
|
|
int32_t cd = ((d - 1) & 3);
|
|
|
|
tileElement->AsPath()->SetCorners(tileElement->AsPath()->GetCorners() & ~(1 << cd));
|
|
|
|
cd = ((cd + 1) & 3);
|
|
|
|
tileElement->AsPath()->SetCorners(tileElement->AsPath()->GetCorners() & ~(1 << cd));
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateTile({ footpathPos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() });
|
2015-07-06 23:12:45 +02:00
|
|
|
|
2018-06-22 23:17:03 +02:00
|
|
|
if (isQueue)
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathDisconnectQueueFromPath(footpathPos, tileElement, -1);
|
2015-07-08 07:58:48 +02:00
|
|
|
|
2020-01-04 12:47:01 +01:00
|
|
|
Direction shiftedDirection = (direction + 1) & 3;
|
|
|
|
auto targetFootPathPos = CoordsXYZ{ CoordsXY{ footpathPos } + CoordsDirectionDelta[shiftedDirection], footpathPos.z };
|
2015-07-06 23:12:45 +02:00
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
tileElement = MapGetFirstElementAt(targetFootPathPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return;
|
2018-06-22 23:17:03 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2020-01-04 12:47:01 +01:00
|
|
|
if (tileElement->GetBaseZ() != targetFootPathPos.z)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
2015-07-06 23:12:45 +02:00
|
|
|
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
2015-07-06 23:12:45 +02:00
|
|
|
|
2020-01-04 12:47:01 +01:00
|
|
|
cd = ((shiftedDirection + 1) & 3);
|
2018-10-05 13:33:46 +02:00
|
|
|
tileElement->AsPath()->SetCorners(tileElement->AsPath()->GetCorners() & ~(1 << cd));
|
2022-10-12 14:14:45 +02:00
|
|
|
MapInvalidateTile({ targetFootPathPos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() });
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2015-07-06 23:12:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006A6B14
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
static void FootpathRemoveEdgesTowards(const CoordsXYRangedZ& footPathPos, int32_t direction, bool isQueue)
|
2015-07-06 23:12:45 +02:00
|
|
|
{
|
2022-10-12 07:35:20 +02:00
|
|
|
if (!MapIsLocationValid(footPathPos))
|
2018-06-01 11:30:41 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(footPathPos);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return;
|
2018-06-01 11:30:41 +02:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2017-06-06 23:24:18 +02:00
|
|
|
continue;
|
|
|
|
|
2020-01-04 12:52:18 +01:00
|
|
|
if (footPathPos.clearanceZ == tileElement->GetBaseZ())
|
2018-06-01 11:30:41 +02:00
|
|
|
{
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2018-06-01 11:30:41 +02:00
|
|
|
{
|
2018-10-04 15:07:48 +02:00
|
|
|
uint8_t slope = tileElement->AsPath()->GetSlopeDirection();
|
2017-06-06 23:24:18 +02:00
|
|
|
if (slope != direction)
|
|
|
|
break;
|
|
|
|
}
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathRemoveEdgesTowardsHere({ footPathPos, footPathPos.clearanceZ }, direction, tileElement, isQueue);
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
2018-06-01 11:30:41 +02:00
|
|
|
|
2020-01-04 12:52:18 +01:00
|
|
|
if (footPathPos.baseZ == tileElement->GetBaseZ())
|
2018-06-01 11:30:41 +02:00
|
|
|
{
|
2018-09-16 16:17:35 +02:00
|
|
|
if (!tileElement->AsPath()->IsSloped())
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
|
2022-10-04 21:12:38 +02:00
|
|
|
uint8_t slope = DirectionReverse(tileElement->AsPath()->GetSlopeDirection());
|
2017-06-06 23:24:18 +02:00
|
|
|
if (slope != direction)
|
|
|
|
break;
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathRemoveEdgesTowardsHere({ footPathPos, footPathPos.clearanceZ }, direction, tileElement, isQueue);
|
2017-06-06 23:24:18 +02:00
|
|
|
break;
|
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2015-07-06 23:12:45 +02:00
|
|
|
}
|
|
|
|
|
2018-03-12 00:28:33 +01:00
|
|
|
// Returns true when there is an element at the given coordinates that want to connect to a path with the given direction (ride
|
|
|
|
// entrances and exits, shops, paths).
|
2022-10-04 08:51:27 +02:00
|
|
|
bool TileElementWantsPathConnectionTowards(const TileCoordsXYZD& coords, const TileElement* const elementToBeRemoved)
|
2018-03-12 00:28:33 +01:00
|
|
|
{
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(coords);
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
return false;
|
2018-03-12 00:28:33 +01:00
|
|
|
do
|
|
|
|
{
|
2018-03-16 16:45:54 +01:00
|
|
|
// Don't check the element that gets removed
|
|
|
|
if (tileElement == elementToBeRemoved)
|
2018-03-12 00:28:33 +01:00
|
|
|
continue;
|
|
|
|
|
2021-12-11 00:39:39 +01:00
|
|
|
switch (tileElement->GetType())
|
2018-03-12 00:28:33 +01:00
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
case TileElementType::Path:
|
2023-01-19 20:36:30 +01:00
|
|
|
if (tileElement->BaseHeight == coords.z)
|
2018-03-12 00:28:33 +01:00
|
|
|
{
|
2018-09-16 16:17:35 +02:00
|
|
|
if (!tileElement->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
// The footpath is flat, it can be connected to from any direction
|
2018-03-12 00:28:33 +01:00
|
|
|
return true;
|
2022-10-04 21:12:38 +02:00
|
|
|
if (tileElement->AsPath()->GetSlopeDirection() == DirectionReverse(coords.direction))
|
2018-06-22 23:17:03 +02:00
|
|
|
// The footpath is sloped and its lowest point matches the edge connection
|
|
|
|
return true;
|
|
|
|
}
|
2023-01-19 20:36:30 +01:00
|
|
|
else if (tileElement->BaseHeight + 2 == coords.z)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-04 15:07:48 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped() && tileElement->AsPath()->GetSlopeDirection() == coords.direction)
|
2018-06-22 23:17:03 +02:00
|
|
|
// The footpath is sloped and its higher point matches the edge connection
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2021-12-11 00:39:39 +01:00
|
|
|
case TileElementType::Track:
|
2023-01-19 20:36:30 +01:00
|
|
|
if (tileElement->BaseHeight == coords.z)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-17 01:32:54 +01:00
|
|
|
auto ride = GetRide(tileElement->AsTrack()->GetRideIndex());
|
2019-08-08 23:58:56 +02:00
|
|
|
if (ride == nullptr)
|
|
|
|
continue;
|
|
|
|
|
2020-12-12 22:53:50 +01:00
|
|
|
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
|
2020-01-17 12:27:14 +01:00
|
|
|
const auto trackType = tileElement->AsTrack()->GetTrackType();
|
2018-09-17 22:10:15 +02:00
|
|
|
const uint8_t trackSequence = tileElement->AsTrack()->GetSequenceIndex();
|
2021-08-27 23:49:32 +02:00
|
|
|
const auto& ted = GetTrackElementDescriptor(trackType);
|
2021-08-29 16:08:54 +02:00
|
|
|
if (ted.SequenceProperties[trackSequence] & TRACK_SEQUENCE_FLAG_CONNECTS_TO_PATH)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-09-15 10:56:35 +02:00
|
|
|
uint16_t dx = ((coords.direction - tileElement->GetDirection()) & TILE_ELEMENT_DIRECTION_MASK);
|
2021-08-29 16:08:54 +02:00
|
|
|
if (ted.SequenceProperties[trackSequence] & (1 << dx))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
// Track element has the flags required for the given direction
|
|
|
|
return true;
|
|
|
|
}
|
2018-03-12 00:28:33 +01:00
|
|
|
}
|
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
2021-12-11 00:39:39 +01:00
|
|
|
case TileElementType::Entrance:
|
2023-01-19 20:36:30 +01:00
|
|
|
if (tileElement->BaseHeight == coords.z)
|
2018-04-16 23:21:45 +02:00
|
|
|
{
|
2021-08-11 23:26:46 +02:00
|
|
|
if (entrance_has_direction(*(tileElement->AsEntrance()), coords.direction - tileElement->GetDirection()))
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
// Entrance wants to be connected towards the given direction
|
|
|
|
return true;
|
|
|
|
}
|
2018-04-16 23:21:45 +02:00
|
|
|
}
|
2018-06-22 23:17:03 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2018-03-12 00:28:33 +01:00
|
|
|
}
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2018-03-12 00:28:33 +01:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-03-16 16:15:48 +01:00
|
|
|
// fix up the corners around the given path element that gets removed
|
2022-10-04 08:51:27 +02:00
|
|
|
static void FootpathFixCornersAround(const TileCoordsXY& footpathPos, TileElement* pathElement)
|
2018-03-16 16:15:48 +01:00
|
|
|
{
|
|
|
|
// A mask for the paths' corners of each possible neighbour
|
2018-06-20 17:28:51 +02:00
|
|
|
static constexpr uint8_t cornersTouchingTile[3][3] = {
|
2018-03-16 16:15:48 +01:00
|
|
|
{ 0b0010, 0b0011, 0b0001 },
|
|
|
|
{ 0b0110, 0b0000, 0b1001 },
|
|
|
|
{ 0b0100, 0b1100, 0b1000 },
|
|
|
|
};
|
|
|
|
|
|
|
|
// Sloped paths don't create filled corners, so no need to remove any
|
2021-12-11 00:39:39 +01:00
|
|
|
if (pathElement->GetType() == TileElementType::Path && pathElement->AsPath()->IsSloped())
|
2018-03-16 16:15:48 +01:00
|
|
|
return;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t xOffset = -1; xOffset <= 1; xOffset++)
|
2018-03-16 16:15:48 +01:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t yOffset = -1; yOffset <= 1; yOffset++)
|
2018-03-16 16:15:48 +01:00
|
|
|
{
|
|
|
|
// Skip self
|
|
|
|
if (xOffset == 0 && yOffset == 0)
|
|
|
|
continue;
|
|
|
|
|
2022-10-11 20:39:24 +02:00
|
|
|
TileElement* tileElement = MapGetFirstElementAt(
|
2020-01-13 03:08:12 +01:00
|
|
|
TileCoordsXY{ footpathPos.x + xOffset, footpathPos.y + yOffset }.ToCoordsXY());
|
2019-10-09 16:02:21 +02:00
|
|
|
if (tileElement == nullptr)
|
|
|
|
continue;
|
2018-03-16 16:15:48 +01:00
|
|
|
do
|
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() != TileElementType::Path)
|
2018-03-16 16:15:48 +01:00
|
|
|
continue;
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2018-03-16 16:15:48 +01:00
|
|
|
continue;
|
2023-01-19 20:36:30 +01:00
|
|
|
if (tileElement->BaseHeight != pathElement->BaseHeight)
|
2018-03-16 16:15:48 +01:00
|
|
|
continue;
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
const int32_t ix = xOffset + 1;
|
|
|
|
const int32_t iy = yOffset + 1;
|
2018-10-05 13:33:46 +02:00
|
|
|
tileElement->AsPath()->SetCorners(tileElement->AsPath()->GetCorners() & ~(cornersTouchingTile[iy][ix]));
|
2018-05-24 11:44:53 +02:00
|
|
|
} while (!(tileElement++)->IsLastForTile());
|
2018-03-16 16:15:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-06 23:12:45 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006A6AA7
|
|
|
|
* @param x x-coordinate in units (not tiles)
|
|
|
|
* @param y y-coordinate in units (not tiles)
|
|
|
|
*/
|
2022-10-04 08:51:27 +02:00
|
|
|
void FootpathRemoveEdgesAt(const CoordsXY& footpathPos, TileElement* tileElement)
|
2015-07-06 23:12:45 +02:00
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() == TileElementType::Track)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-08-08 23:58:56 +02:00
|
|
|
auto rideIndex = tileElement->AsTrack()->GetRideIndex();
|
2023-01-17 01:32:54 +01:00
|
|
|
auto ride = GetRide(rideIndex);
|
2020-12-12 22:53:50 +01:00
|
|
|
if (ride == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathUpdateQueueEntranceBanner(footpathPos, tileElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-03-16 16:15:48 +01:00
|
|
|
bool fixCorners = false;
|
2022-10-17 18:08:55 +02:00
|
|
|
for (uint8_t direction = 0; direction < NumOrthogonalDirections; direction++)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2023-01-19 20:36:30 +01:00
|
|
|
int32_t z1 = tileElement->BaseHeight;
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() == TileElementType::Path)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-09-16 16:17:35 +02:00
|
|
|
if (tileElement->AsPath()->IsSloped())
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2018-10-04 15:07:48 +02:00
|
|
|
int32_t slope = tileElement->AsPath()->GetSlopeDirection();
|
2018-03-12 00:28:33 +01:00
|
|
|
// Sloped footpaths don't connect sideways
|
2017-06-06 23:24:18 +02:00
|
|
|
if ((slope - direction) & 1)
|
|
|
|
continue;
|
|
|
|
|
2018-03-12 00:28:33 +01:00
|
|
|
// When a path is sloped, the higher point of the path is 2 units higher
|
|
|
|
z1 += (slope == direction) ? 2 : 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-03-12 00:28:33 +01:00
|
|
|
|
|
|
|
// When clearance checks were disabled a neighbouring path can be connected to both the path-ghost and to something
|
|
|
|
// else, so before removing edges from neighbouring paths we have to make sure there is nothing else they are connected
|
|
|
|
// to.
|
2022-10-04 08:51:27 +02:00
|
|
|
if (!TileElementWantsPathConnectionTowards({ TileCoordsXY{ footpathPos }, z1, direction }, tileElement))
|
2018-03-12 00:28:33 +01:00
|
|
|
{
|
2021-12-11 00:39:39 +01:00
|
|
|
bool isQueue = tileElement->GetType() == TileElementType::Path ? tileElement->AsPath()->IsQueue() : false;
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t z0 = z1 - 2;
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathRemoveEdgesTowards(
|
2020-01-04 12:52:18 +01:00
|
|
|
{ footpathPos + CoordsDirectionDelta[direction], z0 * COORDS_Z_STEP, z1 * COORDS_Z_STEP }, direction, isQueue);
|
2018-03-12 00:28:33 +01:00
|
|
|
}
|
2018-03-16 16:15:48 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// A footpath may stay connected, but its edges must be fixed later on when another edge does get removed.
|
|
|
|
fixCorners = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only fix corners when needed, to avoid changing corners that have been set for its looks.
|
2018-09-14 11:14:19 +02:00
|
|
|
if (fixCorners && tileElement->IsGhost())
|
2018-03-16 16:15:48 +01:00
|
|
|
{
|
2020-01-04 12:33:19 +01:00
|
|
|
auto tileFootpathPos = TileCoordsXY{ footpathPos };
|
2022-10-04 08:51:27 +02:00
|
|
|
FootpathFixCornersAround(tileFootpathPos, tileElement);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2021-12-11 00:39:39 +01:00
|
|
|
if (tileElement->GetType() == TileElementType::Path)
|
2018-10-05 13:33:46 +02:00
|
|
|
tileElement->AsPath()->SetEdgesAndCorners(0);
|
2015-07-06 23:12:45 +02:00
|
|
|
}
|
2016-05-15 17:07:19 +02:00
|
|
|
|
2022-07-02 16:02:27 +02:00
|
|
|
static ObjectEntryIndex FootpathGetDefaultSurface(bool queue)
|
|
|
|
{
|
|
|
|
bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode);
|
|
|
|
for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_SURFACE_OBJECTS; i++)
|
|
|
|
{
|
|
|
|
auto pathEntry = GetPathSurfaceEntry(i);
|
|
|
|
if (pathEntry != nullptr)
|
|
|
|
{
|
|
|
|
if (!showEditorPaths && (pathEntry->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (queue == ((pathEntry->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0))
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return OBJECT_ENTRY_INDEX_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool FootpathIsSurfaceEntryOkay(ObjectEntryIndex index, bool queue)
|
|
|
|
{
|
|
|
|
auto pathEntry = GetPathSurfaceEntry(index);
|
|
|
|
if (pathEntry != nullptr)
|
|
|
|
{
|
|
|
|
bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode);
|
|
|
|
if (!showEditorPaths && (pathEntry->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (queue == ((pathEntry->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ObjectEntryIndex FootpathGetDefaultRailings()
|
|
|
|
{
|
|
|
|
for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_RAILINGS_OBJECTS; i++)
|
|
|
|
{
|
|
|
|
const auto* railingEntry = GetPathRailingsEntry(i);
|
|
|
|
if (railingEntry != nullptr)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return OBJECT_ENTRY_INDEX_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool FootpathIsLegacyPathEntryOkay(ObjectEntryIndex index)
|
|
|
|
{
|
|
|
|
bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode);
|
|
|
|
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
|
|
|
|
auto footpathObj = static_cast<FootpathObject*>(objManager.GetLoadedObject(ObjectType::Paths, index));
|
|
|
|
if (footpathObj != nullptr)
|
|
|
|
{
|
2023-01-19 09:16:44 +01:00
|
|
|
auto pathEntry = reinterpret_cast<FootpathEntry*>(footpathObj->GetLegacyData());
|
2022-07-02 16:02:27 +02:00
|
|
|
return showEditorPaths || !(pathEntry->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ObjectEntryIndex FootpathGetDefaultLegacyPath()
|
|
|
|
{
|
|
|
|
for (ObjectEntryIndex i = 0; i < MAX_PATH_OBJECTS; i++)
|
|
|
|
{
|
|
|
|
if (FootpathIsLegacyPathEntryOkay(i))
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return OBJECT_ENTRY_INDEX_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FootpathSelectDefault()
|
|
|
|
{
|
|
|
|
// Select default footpath
|
|
|
|
auto surfaceIndex = FootpathGetDefaultSurface(false);
|
|
|
|
if (FootpathIsSurfaceEntryOkay(gFootpathSelection.NormalSurface, false))
|
|
|
|
{
|
|
|
|
surfaceIndex = gFootpathSelection.NormalSurface;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select default queue
|
|
|
|
auto queueIndex = FootpathGetDefaultSurface(true);
|
|
|
|
if (FootpathIsSurfaceEntryOkay(gFootpathSelection.QueueSurface, true))
|
|
|
|
{
|
|
|
|
queueIndex = gFootpathSelection.QueueSurface;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select default railing
|
|
|
|
auto railingIndex = FootpathGetDefaultRailings();
|
|
|
|
const auto* railingEntry = GetPathRailingsEntry(gFootpathSelection.Railings);
|
|
|
|
if (railingEntry != nullptr)
|
|
|
|
{
|
|
|
|
railingIndex = gFootpathSelection.Railings;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select default legacy path
|
|
|
|
auto legacyPathIndex = FootpathGetDefaultLegacyPath();
|
|
|
|
if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL)
|
|
|
|
{
|
|
|
|
if (FootpathIsLegacyPathEntryOkay(gFootpathSelection.LegacyPath))
|
|
|
|
{
|
|
|
|
// Keep legacy path selected
|
|
|
|
legacyPathIndex = gFootpathSelection.LegacyPath;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Reset legacy path, we default to a surface (if there are any)
|
|
|
|
gFootpathSelection.LegacyPath = OBJECT_ENTRY_INDEX_NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (surfaceIndex == OBJECT_ENTRY_INDEX_NULL)
|
|
|
|
{
|
|
|
|
if (legacyPathIndex == OBJECT_ENTRY_INDEX_NULL)
|
|
|
|
{
|
|
|
|
// No surfaces or legacy paths available
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No surfaces available, so default to legacy path
|
|
|
|
gFootpathSelection.LegacyPath = legacyPathIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
gFootpathSelection.NormalSurface = surfaceIndex;
|
|
|
|
gFootpathSelection.QueueSurface = queueIndex;
|
|
|
|
gFootpathSelection.Railings = railingIndex;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-05 18:44:43 +02:00
|
|
|
const FootpathObject* GetLegacyFootpathEntry(ObjectEntryIndex entryIndex)
|
2019-01-20 21:38:38 +01:00
|
|
|
{
|
|
|
|
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
|
2020-11-09 10:36:37 +01:00
|
|
|
auto obj = objMgr.GetLoadedObject(ObjectType::Paths, entryIndex);
|
2021-09-05 18:44:43 +02:00
|
|
|
if (obj == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
const FootpathObject* footpathObject = (static_cast<FootpathObject*>(obj));
|
|
|
|
return footpathObject;
|
2016-05-15 17:07:19 +02:00
|
|
|
}
|
2018-02-06 10:05:48 +01:00
|
|
|
|
2021-09-03 19:19:42 +02:00
|
|
|
const FootpathSurfaceObject* GetPathSurfaceEntry(ObjectEntryIndex entryIndex)
|
|
|
|
{
|
|
|
|
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
|
|
|
|
auto obj = objMgr.GetLoadedObject(ObjectType::FootpathSurface, entryIndex);
|
|
|
|
if (obj == nullptr)
|
|
|
|
return nullptr;
|
2021-09-10 09:13:20 +02:00
|
|
|
|
2021-09-03 19:19:42 +02:00
|
|
|
return static_cast<FootpathSurfaceObject*>(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
const FootpathRailingsObject* GetPathRailingsEntry(ObjectEntryIndex entryIndex)
|
|
|
|
{
|
|
|
|
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
|
|
|
|
auto obj = objMgr.GetLoadedObject(ObjectType::FootpathRailings, entryIndex);
|
|
|
|
if (obj == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return static_cast<FootpathRailingsObject*>(obj);
|
|
|
|
}
|
|
|
|
|
2022-01-19 14:17:11 +01:00
|
|
|
RideId PathElement::GetRideIndex() const
|
2018-10-05 12:39:37 +02:00
|
|
|
{
|
|
|
|
return rideIndex;
|
|
|
|
}
|
|
|
|
|
2022-01-19 14:17:11 +01:00
|
|
|
void PathElement::SetRideIndex(RideId newRideIndex)
|
2018-10-05 12:39:37 +02:00
|
|
|
{
|
|
|
|
rideIndex = newRideIndex;
|
2018-10-05 12:49:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t PathElement::GetAdditionStatus() const
|
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
return AdditionStatus;
|
2018-10-05 12:49:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetAdditionStatus(uint8_t newStatus)
|
|
|
|
{
|
2020-03-04 18:43:09 +01:00
|
|
|
AdditionStatus = newStatus;
|
2018-10-05 13:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t PathElement::GetEdges() const
|
|
|
|
{
|
2020-09-07 19:21:19 +02:00
|
|
|
return EdgesAndCorners & FOOTPATH_PROPERTIES_EDGES_EDGES_MASK;
|
2018-10-05 13:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetEdges(uint8_t newEdges)
|
|
|
|
{
|
2020-09-07 19:21:19 +02:00
|
|
|
EdgesAndCorners &= ~FOOTPATH_PROPERTIES_EDGES_EDGES_MASK;
|
|
|
|
EdgesAndCorners |= (newEdges & FOOTPATH_PROPERTIES_EDGES_EDGES_MASK);
|
2018-10-05 13:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t PathElement::GetCorners() const
|
|
|
|
{
|
2020-09-07 19:21:19 +02:00
|
|
|
return EdgesAndCorners >> 4;
|
2018-10-05 13:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetCorners(uint8_t newCorners)
|
|
|
|
{
|
2020-09-07 19:21:19 +02:00
|
|
|
EdgesAndCorners &= ~FOOTPATH_PROPERTIES_EDGES_CORNERS_MASK;
|
|
|
|
EdgesAndCorners |= (newCorners << 4);
|
2018-10-05 13:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t PathElement::GetEdgesAndCorners() const
|
|
|
|
{
|
2020-09-07 19:21:19 +02:00
|
|
|
return EdgesAndCorners;
|
2018-10-05 13:11:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PathElement::SetEdgesAndCorners(uint8_t newEdgesAndCorners)
|
|
|
|
{
|
2020-09-07 19:21:19 +02:00
|
|
|
EdgesAndCorners = newEdgesAndCorners;
|
2018-11-08 22:13:44 +01:00
|
|
|
}
|
2020-01-05 17:59:02 +01:00
|
|
|
|
2020-02-13 10:25:42 +01:00
|
|
|
bool PathElement::IsLevelCrossing(const CoordsXY& coords) const
|
2020-01-05 17:59:02 +01:00
|
|
|
{
|
2022-10-12 14:14:45 +02:00
|
|
|
auto trackElement = MapGetTrackElementAt({ coords, GetBaseZ() });
|
2020-01-05 17:59:02 +01:00
|
|
|
if (trackElement == nullptr)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-18 08:28:41 +02:00
|
|
|
if (trackElement->GetTrackType() != TrackElemType::Flat)
|
2020-01-05 17:59:02 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-17 01:32:54 +01:00
|
|
|
auto ride = GetRide(trackElement->GetRideIndex());
|
2020-01-05 17:59:02 +01:00
|
|
|
if (ride == nullptr)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-12 22:53:50 +01:00
|
|
|
return ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_SUPPORTS_LEVEL_CROSSINGS);
|
2020-01-05 17:59:02 +01:00
|
|
|
}
|