diff --git a/distribution/changelog.txt b/distribution/changelog.txt
index 62a9fa8660..f7cbe597a8 100644
--- a/distribution/changelog.txt
+++ b/distribution/changelog.txt
@@ -1,5 +1,6 @@
0.4.14 (in development)
------------------------------------------------------------------------
+- Feature: [#15750] Allow using different types of park entrance in one park.
- Change: [#21659] Increase the Hybrid Roller Coaster’s maximum lift speed to 17 km/h (11 mph).
- Change: [#22466] The Clear Scenery tool now uses a bulldozer cursor instead of a generic crosshair.
diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp
index f07b493f23..d9a8ca5d6f 100644
--- a/src/openrct2-ui/WindowManager.cpp
+++ b/src/openrct2-ui/WindowManager.cpp
@@ -144,6 +144,8 @@ public:
return TransparencyOpen();
case WindowClass::AssetPacks:
return AssetPacksOpen();
+ case WindowClass::EditorParkEntrance:
+ return EditorParkEntranceOpen();
default:
Console::Error::WriteLine("Unhandled window class (%d)", wc);
return nullptr;
diff --git a/src/openrct2-ui/input/Shortcuts.cpp b/src/openrct2-ui/input/Shortcuts.cpp
index e31fdf7878..7167da4447 100644
--- a/src/openrct2-ui/input/Shortcuts.cpp
+++ b/src/openrct2-ui/input/Shortcuts.cpp
@@ -127,11 +127,11 @@ static void ShortcutRotateConstructionObject()
}
// Rotate park entrance
- w = WindowFindByClass(WindowClass::Map);
- if (w != nullptr && !WidgetIsDisabled(*w, WC_MAP__WIDX_ROTATE_90)
- && w->widgets[WC_MAP__WIDX_ROTATE_90].type != WindowWidgetType::Empty)
+ w = WindowFindByClass(WindowClass::EditorParkEntrance);
+ if (w != nullptr && !WidgetIsDisabled(*w, WC_EDITOR_PARK_ENTRANCE__WIDX_ROTATE_ENTRANCE_BUTTON)
+ && w->widgets[WC_EDITOR_PARK_ENTRANCE__WIDX_ROTATE_ENTRANCE_BUTTON].type != WindowWidgetType::Empty)
{
- w->OnMouseUp(WC_MAP__WIDX_ROTATE_90);
+ w->OnMouseUp(WC_EDITOR_PARK_ENTRANCE__WIDX_ROTATE_ENTRANCE_BUTTON);
return;
}
diff --git a/src/openrct2-ui/interface/Theme.cpp b/src/openrct2-ui/interface/Theme.cpp
index 3434b4cde5..3ab5777b3f 100644
--- a/src/openrct2-ui/interface/Theme.cpp
+++ b/src/openrct2-ui/interface/Theme.cpp
@@ -196,6 +196,7 @@ static constexpr WindowThemeDesc WindowThemeDescriptors[] =
{ WindowClass::Chat, "WC_CHAT", STR_CHAT, COLOURS_1(translucent(COLOUR_GREY) ) },
{ WindowClass::Console, "WC_CONSOLE", STR_CONSOLE, COLOURS_2(translucent(COLOUR_LIGHT_BLUE), opaque(COLOUR_WHITE) ) },
{ WindowClass::ProgressWindow, "WC_PROGRESS_WINDOW", STR_THEME_LOADING_WINDOW, COLOURS_1(opaque(COLOUR_BLACK) ) },
+ { WindowClass::EditorParkEntrance, "WC_EDITOR_PARK_ENTRANCE", STR_OBJECT_SELECTION_PARK_ENTRANCE, COLOURS_2(opaque(COLOUR_DARK_GREEN), opaque(COLOUR_DARK_BROWN) ) },
};
#pragma endregion
diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj
index dd474ce56a..567cbcba21 100644
--- a/src/openrct2-ui/libopenrct2ui.vcxproj
+++ b/src/openrct2-ui/libopenrct2ui.vcxproj
@@ -170,6 +170,7 @@
+
diff --git a/src/openrct2-ui/windows/EditorParkEntrance.cpp b/src/openrct2-ui/windows/EditorParkEntrance.cpp
new file mode 100644
index 0000000000..c141238f56
--- /dev/null
+++ b/src/openrct2-ui/windows/EditorParkEntrance.cpp
@@ -0,0 +1,380 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2024 OpenRCT2 developers
+ *
+ * For a complete list of all authors, please refer to contributors.md
+ * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is licensed under the GNU General Public License version 3.
+ *****************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace OpenRCT2::Ui::Windows
+{
+ static constexpr StringId kWindowTitle = STR_OBJECT_SELECTION_PARK_ENTRANCE;
+ static constexpr int32_t kImageSize = 116;
+ static constexpr int32_t kNumColumns = 4;
+ static constexpr int32_t kNumRows = 1;
+ static constexpr int32_t kScrollPadding = 2;
+ static constexpr int32_t kScrollWidth = (kImageSize * kNumColumns) + kScrollBarWidth + 4;
+ static constexpr int32_t kScrollHeight = (kImageSize * kNumRows);
+ static constexpr int32_t kWindowWidth = kScrollWidth + 28;
+ static constexpr int32_t kWindowHeight = kScrollHeight + 50;
+
+ struct EntranceSelection
+ {
+ ObjectEntryIndex entryIndex = OBJECT_ENTRY_INDEX_NULL;
+ StringId stringId = STR_NONE;
+ ImageIndex imageId = static_cast(SPR_NONE);
+ };
+
+ enum WindowEditorParkEntranceListWidgetIdx
+ {
+ WIDX_BACKGROUND,
+ WIDX_TITLE,
+ WIDX_CLOSE,
+ WIDX_TAB_CONTENT_PANEL,
+ WIDX_TAB,
+ WIDX_LIST,
+ WIDX_ROTATE_ENTRANCE_BUTTON,
+ };
+
+ validate_global_widx(WC_EDITOR_PARK_ENTRANCE, WIDX_ROTATE_ENTRANCE_BUTTON);
+
+ // clang-format off
+ static Widget _widgets[] = {
+ WINDOW_SHIM(kWindowTitle, kWindowWidth, kWindowHeight),
+ MakeWidget ({ 0, 43 }, { kWindowWidth, kWindowHeight - 43 }, WindowWidgetType::Resize, WindowColour::Secondary ),
+ MakeTab ({ 3, 17 }, STR_NONE ),
+ MakeWidget ({ 2, 45 }, { kScrollWidth, kScrollHeight }, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_VERTICAL ),
+ MakeWidget ({ kWindowWidth - 26, 59 }, { 24, 24 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_ROTATE_ARROW), STR_ROTATE_OBJECTS_90 ),
+ kWidgetsEnd,
+ };
+ // clang-format on
+
+ class EditorParkEntrance final : public Window
+ {
+ private:
+ ObjectEntryIndex _selectedEntranceType = 0;
+ ObjectEntryIndex _highlightedEntranceType = 0;
+ std::vector _entranceTypes{};
+
+ void InitParkEntranceItems()
+ {
+ _entranceTypes.clear();
+ for (ObjectEntryIndex objectIndex = 0; objectIndex < MAX_PARK_ENTRANCE_OBJECTS; objectIndex++)
+ {
+ auto& objManager = GetContext()->GetObjectManager();
+ auto* object = static_cast(objManager.GetLoadedObject(ObjectType::ParkEntrance, objectIndex));
+ if (object != nullptr)
+ {
+ const auto* legacyData = reinterpret_cast(object->GetLegacyData());
+ _entranceTypes.push_back({ objectIndex, legacyData->string_idx, legacyData->image_id });
+ }
+ }
+ }
+
+ void PaintPreview(DrawPixelInfo& dpi, ImageIndex imageStart, ScreenCoordsXY screenCoords, Direction direction)
+ {
+ imageStart += (direction * 3);
+
+ switch (direction)
+ {
+ case 0:
+ GfxDrawSprite(dpi, ImageId(imageStart + 1), screenCoords + ScreenCoordsXY{ -32, 14 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 0), screenCoords + ScreenCoordsXY{ 0, 28 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 2), screenCoords + ScreenCoordsXY{ 32, 44 });
+ break;
+ case 1:
+ GfxDrawSprite(dpi, ImageId(imageStart + 1), screenCoords + ScreenCoordsXY{ 32, 14 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 0), screenCoords + ScreenCoordsXY{ 0, 28 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 2), screenCoords + ScreenCoordsXY{ -32, 44 });
+ break;
+ case 2:
+ GfxDrawSprite(dpi, ImageId(imageStart + 2), screenCoords + ScreenCoordsXY{ -32, 14 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 0), screenCoords + ScreenCoordsXY{ 0, 28 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 1), screenCoords + ScreenCoordsXY{ 32, 44 });
+ break;
+ case 3:
+ GfxDrawSprite(dpi, ImageId(imageStart + 2), screenCoords + ScreenCoordsXY{ 32, 14 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 0), screenCoords + ScreenCoordsXY{ 0, 28 });
+ GfxDrawSprite(dpi, ImageId(imageStart + 1), screenCoords + ScreenCoordsXY{ -32, 44 });
+ break;
+ }
+ }
+
+ CoordsXYZD PlaceParkEntranceGetMapPosition(const ScreenCoordsXY& screenCoords)
+ {
+ CoordsXYZD parkEntranceMapPosition{ 0, 0, 0, INVALID_DIRECTION };
+ const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords);
+ parkEntranceMapPosition = { mapCoords.x, mapCoords.y, 0, INVALID_DIRECTION };
+ if (parkEntranceMapPosition.IsNull())
+ return parkEntranceMapPosition;
+
+ auto surfaceElement = MapGetSurfaceElementAt(mapCoords);
+ if (surfaceElement == nullptr)
+ {
+ parkEntranceMapPosition.SetNull();
+ return parkEntranceMapPosition;
+ }
+
+ parkEntranceMapPosition.z = surfaceElement->GetWaterHeight();
+ if (parkEntranceMapPosition.z == 0)
+ {
+ parkEntranceMapPosition.z = surfaceElement->GetBaseZ();
+ if ((surfaceElement->GetSlope() & kTileSlopeRaisedCornersMask) != 0)
+ {
+ parkEntranceMapPosition.z += 16;
+ if (surfaceElement->GetSlope() & kTileSlopeDiagonalFlag)
+ {
+ parkEntranceMapPosition.z += 16;
+ }
+ }
+ }
+ parkEntranceMapPosition.direction = (gWindowSceneryRotation - GetCurrentRotation()) & 3;
+ return parkEntranceMapPosition;
+ }
+
+ void PlaceParkEntranceToolUpdate(const ScreenCoordsXY& screenCoords)
+ {
+ MapInvalidateSelectionRect();
+ MapInvalidateMapSelectionTiles();
+ gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
+ gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
+ gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
+ CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords);
+ if (parkEntrancePosition.IsNull())
+ {
+ ParkEntranceRemoveGhost();
+ return;
+ }
+
+ int32_t sideDirection = (parkEntrancePosition.direction + 1) & 3;
+ gMapSelectionTiles.clear();
+ gMapSelectionTiles.push_back({ parkEntrancePosition.x, parkEntrancePosition.y });
+ gMapSelectionTiles.push_back({ parkEntrancePosition.x + CoordsDirectionDelta[sideDirection].x,
+ parkEntrancePosition.y + CoordsDirectionDelta[sideDirection].y });
+ gMapSelectionTiles.push_back({ parkEntrancePosition.x - CoordsDirectionDelta[sideDirection].x,
+ parkEntrancePosition.y - CoordsDirectionDelta[sideDirection].y });
+
+ gMapSelectArrowPosition = parkEntrancePosition;
+ gMapSelectArrowDirection = parkEntrancePosition.direction;
+
+ gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT | MAP_SELECT_FLAG_ENABLE_ARROW;
+ MapInvalidateMapSelectionTiles();
+ if (gParkEntranceGhostExists && parkEntrancePosition == gParkEntranceGhostPosition)
+ {
+ return;
+ }
+
+ ParkEntranceRemoveGhost();
+
+ auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId, _selectedEntranceType);
+ gameAction.SetFlags(GAME_COMMAND_FLAG_GHOST);
+
+ auto result = GameActions::Execute(&gameAction);
+ if (result.Error == GameActions::Status::Ok)
+ {
+ gParkEntranceGhostPosition = parkEntrancePosition;
+ gParkEntranceGhostExists = true;
+ }
+ }
+
+ void PlaceParkEntranceToolDown(const ScreenCoordsXY& screenCoords)
+ {
+ ParkEntranceRemoveGhost();
+
+ CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords);
+ if (!parkEntrancePosition.IsNull())
+ {
+ auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId, _selectedEntranceType);
+ auto result = GameActions::Execute(&gameAction);
+ if (result.Error == GameActions::Status::Ok)
+ {
+ Audio::Play3D(Audio::SoundId::PlaceItem, result.Position);
+ }
+ }
+ }
+
+ ObjectEntryIndex ScrollGetEntranceListItemAt(const ScreenCoordsXY& screenCoords)
+ {
+ if (screenCoords.x <= 0 || screenCoords.y <= 0)
+ return OBJECT_ENTRY_INDEX_NULL;
+
+ size_t column = screenCoords.x / kImageSize;
+ size_t row = screenCoords.y / kImageSize;
+ if (column >= 5)
+ return OBJECT_ENTRY_INDEX_NULL;
+
+ size_t index = column + (row * 5);
+ if (index >= _entranceTypes.size())
+ return OBJECT_ENTRY_INDEX_NULL;
+
+ return _entranceTypes[index].entryIndex;
+ }
+
+ public:
+ void OnOpen() override
+ {
+ widgets = _widgets;
+
+ InitScrollWidgets();
+
+ list_information_type = 0;
+ min_width = kWindowWidth;
+ min_height = kWindowHeight;
+ max_width = kWindowWidth;
+ max_height = kWindowHeight;
+
+ InitParkEntranceItems();
+ pressed_widgets |= 1LL << WIDX_TAB;
+
+ ToolSet(*this, WIDX_LIST, Tool::EntranceDown);
+ InputSetFlag(INPUT_FLAG_6, true);
+ }
+
+ void OnMouseUp(WidgetIndex widgetIndex) override
+ {
+ switch (widgetIndex)
+ {
+ case WIDX_CLOSE:
+ Close();
+ break;
+ case WIDX_ROTATE_ENTRANCE_BUTTON:
+ gWindowSceneryRotation = DirectionNext(gWindowSceneryRotation);
+ Invalidate();
+ break;
+ }
+ }
+
+ void OnClose() override
+ {
+ if (gCurrentToolWidget.window_classification == classification)
+ ToolCancel();
+ }
+
+ void OnUpdate() override
+ {
+ if (gCurrentToolWidget.window_classification != classification)
+ Close();
+ }
+
+ void OnDraw(DrawPixelInfo& dpi) override
+ {
+ DrawWidgets(dpi);
+ GfxDrawSprite(
+ dpi, ImageId(SPR_TAB_PARK_ENTRANCE),
+ windowPos + ScreenCoordsXY{ widgets[WIDX_TAB].left, widgets[WIDX_TAB].top });
+ }
+
+ void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override
+ {
+ GfxClear(dpi, ColourMapA[colours[1].colour].mid_light);
+
+ ScreenCoordsXY coords{ 1, 1 };
+
+ for (auto& entranceType : _entranceTypes)
+ {
+ // Draw flat button rectangle
+ int32_t buttonFlags = 0;
+ if (_selectedEntranceType == entranceType.entryIndex)
+ buttonFlags |= INSET_RECT_FLAG_BORDER_INSET;
+ else if (_highlightedEntranceType == entranceType.entryIndex)
+ buttonFlags |= INSET_RECT_FLAG_FILL_MID_LIGHT;
+
+ if (buttonFlags != 0)
+ GfxFillRectInset(
+ dpi, { coords, coords + ScreenCoordsXY{ kImageSize - 1, kImageSize - 1 } }, colours[1],
+ INSET_RECT_FLAG_FILL_MID_LIGHT | buttonFlags);
+
+ DrawPixelInfo clipDPI;
+ auto screenPos = coords + ScreenCoordsXY{ kScrollPadding, kScrollPadding };
+ if (ClipDrawPixelInfo(
+ clipDPI, dpi, screenPos, kImageSize - (2 * kScrollPadding), kImageSize - (2 * kScrollPadding)))
+ {
+ PaintPreview(
+ clipDPI, entranceType.imageId, ScreenCoordsXY{ kImageSize / 2, kImageSize / 2 },
+ gWindowSceneryRotation);
+ }
+
+ // Next position
+ coords.x += kImageSize;
+ if (coords.x >= kImageSize * kNumColumns + 1)
+ {
+ coords.x = 1;
+ coords.y += kImageSize;
+ }
+ }
+ }
+
+ void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
+ {
+ PlaceParkEntranceToolDown(screenCoords);
+ }
+
+ void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override
+ {
+ PlaceParkEntranceToolUpdate(screenCoords);
+ }
+
+ void OnToolAbort(WidgetIndex widgetIndex) override
+ {
+ ParkEntranceRemoveGhost();
+ Invalidate();
+ HideGridlines();
+ HideLandRights();
+ HideConstructionRights();
+ }
+
+ void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
+ {
+ auto highlighted = ScrollGetEntranceListItemAt(screenCoords);
+ if (highlighted != OBJECT_ENTRY_INDEX_NULL)
+ {
+ _highlightedEntranceType = highlighted;
+ Invalidate();
+ }
+ }
+
+ void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
+ {
+ auto selected = ScrollGetEntranceListItemAt(screenCoords);
+ if (selected == OBJECT_ENTRY_INDEX_NULL)
+ {
+ return;
+ }
+
+ _selectedEntranceType = selected;
+
+ Audio::Play(Audio::SoundId::Click1, 0, windowPos.x + (width / 2));
+ Invalidate();
+ }
+ };
+
+ WindowBase* EditorParkEntranceOpen()
+ {
+ WindowBase* window;
+
+ // Check if window is already open
+ window = WindowBringToFrontByClass(WindowClass::EditorParkEntrance);
+ if (window != nullptr)
+ return window;
+
+ window = WindowCreate(
+ WindowClass::EditorParkEntrance, kWindowWidth, kWindowHeight, WF_10 | WF_RESIZABLE);
+
+ return window;
+ }
+} // namespace OpenRCT2::Ui::Windows
diff --git a/src/openrct2-ui/windows/Map.cpp b/src/openrct2-ui/windows/Map.cpp
index c714895f80..69f7282319 100644
--- a/src/openrct2-ui/windows/Map.cpp
+++ b/src/openrct2-ui/windows/Map.cpp
@@ -7,11 +7,10 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
-#include "../interface/ViewportInteraction.h"
-#include "../interface/ViewportQuery.h"
-
#include
#include
+#include
+#include
#include
#include
#include
@@ -22,21 +21,17 @@
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include
-#include
-#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -126,12 +121,9 @@ namespace OpenRCT2::Ui::Windows
WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX,
WIDX_LAND_SALE_CHECKBOX,
WIDX_CONSTRUCTION_RIGHTS_SALE_CHECKBOX,
- WIDX_ROTATE_90,
WIDX_MAP_GENERATOR
};
- validate_global_widx(WC_MAP, WIDX_ROTATE_90);
-
// clang-format off
static Widget window_map_widgets[] = {
WINDOW_SHIM(WINDOW_TITLE, WW, WH),
@@ -152,7 +144,6 @@ static Widget window_map_widgets[] = {
MakeWidget ({ 58, 197}, {184, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_CONSTRUCTION_RIGHTS_OWNED, STR_SET_CONSTRUCTION_RIGHTS_TO_BE_OWNED_TIP ),
MakeWidget ({ 58, 197}, {184, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_LAND_SALE, STR_SET_LAND_TO_BE_AVAILABLE_TIP ),
MakeWidget ({ 58, 197}, {174, 12}, WindowWidgetType::Checkbox, WindowColour::Secondary, STR_CONSTRUCTION_RIGHTS_SALE, STR_SET_CONSTRUCTION_RIGHTS_TO_BE_AVAILABLE_TIP),
- MakeWidget ({218, 45}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_ROTATE_ARROW), STR_ROTATE_OBJECTS_90 ),
MakeWidget ({110, 189}, {131, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_MAPGEN_WINDOW_TITLE, STR_MAP_GENERATOR_TIP ),
kWidgetsEnd,
};
@@ -334,20 +325,13 @@ static Widget window_map_widgets[] = {
Invalidate();
break;
case WIDX_BUILD_PARK_ENTRANCE:
- Invalidate();
- if (ToolSet(*this, widgetIndex, Tool::UpArrow))
- break;
-
- gParkEntranceGhostExists = false;
- InputSetFlag(INPUT_FLAG_6, true);
-
- ShowGridlines();
- ShowLandRights();
- ShowConstructionRights();
- break;
- case WIDX_ROTATE_90:
- gWindowSceneryRotation = (gWindowSceneryRotation + 1) & 3;
+ {
+ if (!WindowFindByClass(WindowClass::EditorParkEntrance))
+ ContextOpenWindow(WindowClass::EditorParkEntrance);
+ else
+ WindowCloseByClass(WindowClass::EditorParkEntrance);
break;
+ }
case WIDX_PEOPLE_STARTING_POSITION:
if (ToolSet(*this, widgetIndex, Tool::UpArrow))
break;
@@ -475,9 +459,6 @@ static Widget window_map_widgets[] = {
case WIDX_SET_LAND_RIGHTS:
SetLandRightsToolUpdate(screenCoords);
break;
- case WIDX_BUILD_PARK_ENTRANCE:
- PlaceParkEntranceToolUpdate(screenCoords);
- break;
case WIDX_PEOPLE_STARTING_POSITION:
SetPeepSpawnToolUpdate(screenCoords);
break;
@@ -488,9 +469,6 @@ static Widget window_map_widgets[] = {
{
switch (widgetIndex)
{
- case WIDX_BUILD_PARK_ENTRANCE:
- PlaceParkEntranceToolDown(screenCoords);
- break;
case WIDX_PEOPLE_STARTING_POSITION:
SetPeepSpawnToolDown(screenCoords);
break;
@@ -523,13 +501,6 @@ static Widget window_map_widgets[] = {
HideLandRights();
HideConstructionRights();
break;
- case WIDX_BUILD_PARK_ENTRANCE:
- ParkEntranceRemoveGhost();
- Invalidate();
- HideGridlines();
- HideLandRights();
- HideConstructionRights();
- break;
case WIDX_PEOPLE_STARTING_POSITION:
Invalidate();
HideGridlines();
@@ -569,99 +540,6 @@ static Widget window_map_widgets[] = {
MapInvalidateSelectionRect();
}
- CoordsXYZD PlaceParkEntranceGetMapPosition(const ScreenCoordsXY& screenCoords)
- {
- CoordsXYZD parkEntranceMapPosition{ 0, 0, 0, INVALID_DIRECTION };
- const CoordsXY mapCoords = ViewportInteractionGetTileStartAtCursor(screenCoords);
- parkEntranceMapPosition = { mapCoords.x, mapCoords.y, 0, INVALID_DIRECTION };
- if (parkEntranceMapPosition.IsNull())
- return parkEntranceMapPosition;
-
- auto surfaceElement = MapGetSurfaceElementAt(mapCoords);
- if (surfaceElement == nullptr)
- {
- parkEntranceMapPosition.SetNull();
- return parkEntranceMapPosition;
- }
-
- parkEntranceMapPosition.z = surfaceElement->GetWaterHeight();
- if (parkEntranceMapPosition.z == 0)
- {
- parkEntranceMapPosition.z = surfaceElement->GetBaseZ();
- if ((surfaceElement->GetSlope() & kTileSlopeRaisedCornersMask) != 0)
- {
- parkEntranceMapPosition.z += 16;
- if (surfaceElement->GetSlope() & kTileSlopeDiagonalFlag)
- {
- parkEntranceMapPosition.z += 16;
- }
- }
- }
- parkEntranceMapPosition.direction = (gWindowSceneryRotation - GetCurrentRotation()) & 3;
- return parkEntranceMapPosition;
- }
-
- void PlaceParkEntranceToolUpdate(const ScreenCoordsXY& screenCoords)
- {
- MapInvalidateSelectionRect();
- MapInvalidateMapSelectionTiles();
- gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE;
- gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW;
- gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT;
- CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords);
- if (parkEntrancePosition.IsNull())
- {
- ParkEntranceRemoveGhost();
- return;
- }
-
- int32_t sideDirection = (parkEntrancePosition.direction + 1) & 3;
- gMapSelectionTiles.clear();
- gMapSelectionTiles.push_back({ parkEntrancePosition.x, parkEntrancePosition.y });
- gMapSelectionTiles.push_back({ parkEntrancePosition.x + CoordsDirectionDelta[sideDirection].x,
- parkEntrancePosition.y + CoordsDirectionDelta[sideDirection].y });
- gMapSelectionTiles.push_back({ parkEntrancePosition.x - CoordsDirectionDelta[sideDirection].x,
- parkEntrancePosition.y - CoordsDirectionDelta[sideDirection].y });
-
- gMapSelectArrowPosition = parkEntrancePosition;
- gMapSelectArrowDirection = parkEntrancePosition.direction;
-
- gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE_CONSTRUCT | MAP_SELECT_FLAG_ENABLE_ARROW;
- MapInvalidateMapSelectionTiles();
- if (gParkEntranceGhostExists && parkEntrancePosition == gParkEntranceGhostPosition)
- {
- return;
- }
-
- ParkEntranceRemoveGhost();
-
- auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId);
- gameAction.SetFlags(GAME_COMMAND_FLAG_GHOST);
-
- auto result = GameActions::Execute(&gameAction);
- if (result.Error == GameActions::Status::Ok)
- {
- gParkEntranceGhostPosition = parkEntrancePosition;
- gParkEntranceGhostExists = true;
- }
- }
-
- void PlaceParkEntranceToolDown(const ScreenCoordsXY& screenCoords)
- {
- ParkEntranceRemoveGhost();
-
- CoordsXYZD parkEntrancePosition = PlaceParkEntranceGetMapPosition(screenCoords);
- if (!parkEntrancePosition.IsNull())
- {
- auto gameAction = ParkEntrancePlaceAction(parkEntrancePosition, gFootpathSelectedId);
- auto result = GameActions::Execute(&gameAction);
- if (result.Error == GameActions::Status::Ok)
- {
- Audio::Play3D(Audio::SoundId::PlaceItem, result.Position);
- }
- }
- }
-
void SetPeepSpawnToolUpdate(const ScreenCoordsXY& screenCoords)
{
int32_t direction;
@@ -881,6 +759,9 @@ static Widget window_map_widgets[] = {
if (_activeTool & (1 << 0))
pressed_widgets |= (1uLL << WIDX_CONSTRUCTION_RIGHTS_OWNED_CHECKBOX);
+ if (WindowFindByClass(WindowClass::EditorParkEntrance))
+ pressed_widgets |= (1uLL << WIDX_BUILD_PARK_ENTRANCE);
+
// Set disabled widgets
auto& gameState = GetGameState();
SetWidgetDisabled(WIDX_MAP_SIZE_LINK, gameState.MapSize.x != gameState.MapSize.y);
@@ -908,8 +789,6 @@ static Widget window_map_widgets[] = {
widgets[WIDX_SET_LAND_RIGHTS].bottom = height - 70 + 23;
widgets[WIDX_BUILD_PARK_ENTRANCE].top = height - 46;
widgets[WIDX_BUILD_PARK_ENTRANCE].bottom = height - 46 + 23;
- widgets[WIDX_ROTATE_90].top = height - 46;
- widgets[WIDX_ROTATE_90].bottom = height - 46 + 23;
widgets[WIDX_PEOPLE_STARTING_POSITION].top = height - 46;
widgets[WIDX_PEOPLE_STARTING_POSITION].bottom = height - 46 + 23;
@@ -941,13 +820,6 @@ static Widget window_map_widgets[] = {
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || GetGameState().Cheats.SandboxMode)
{
- // scenario editor: build park entrance selected, show rotate button
- if ((InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)) && gCurrentToolWidget.window_classification == WindowClass::Map
- && gCurrentToolWidget.widget_index == WIDX_BUILD_PARK_ENTRANCE)
- {
- widgets[WIDX_ROTATE_90].type = WindowWidgetType::FlatBtn;
- }
-
// Always show set land rights button
widgets[WIDX_SET_LAND_RIGHTS].type = WindowWidgetType::FlatBtn;
diff --git a/src/openrct2-ui/windows/Themes.cpp b/src/openrct2-ui/windows/Themes.cpp
index 3e7814d0ec..2ba167e351 100644
--- a/src/openrct2-ui/windows/Themes.cpp
+++ b/src/openrct2-ui/windows/Themes.cpp
@@ -149,6 +149,7 @@ static WindowClass window_themes_tab_1_classes[] = {
static WindowClass window_themes_tab_2_classes[] = {
WindowClass::ParkInformation,
+ WindowClass::EditorParkEntrance,
WindowClass::Finances,
WindowClass::NewCampaign,
WindowClass::Research,
diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h
index 8f135fb7f2..f120f888d4 100644
--- a/src/openrct2-ui/windows/Window.h
+++ b/src/openrct2-ui/windows/Window.h
@@ -90,6 +90,7 @@ namespace OpenRCT2::Ui::Windows
WindowBase* ViewClippingOpen();
WindowBase* TransparencyOpen();
WindowBase* AssetPacksOpen();
+ WindowBase* EditorParkEntranceOpen();
// WC_FINANCES
WindowBase* FinancesOpen();
diff --git a/src/openrct2/EditorObjectSelectionSession.cpp b/src/openrct2/EditorObjectSelectionSession.cpp
index a89ff15af4..0f010fda93 100644
--- a/src/openrct2/EditorObjectSelectionSession.cpp
+++ b/src/openrct2/EditorObjectSelectionSession.cpp
@@ -194,7 +194,8 @@ void SetupInUseSelectionFlags()
if (parkEntranceEl->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)
break;
- Editor::SetSelectedObject(ObjectType::ParkEntrance, 0, ObjectSelectionFlags::InUse);
+ type = iter.element->AsEntrance()->getEntryIndex();
+ Editor::SetSelectedObject(ObjectType::ParkEntrance, type, ObjectSelectionFlags::InUse);
// Skip if not the middle part
if (parkEntranceEl->GetSequenceIndex() != 0)
@@ -692,9 +693,8 @@ int32_t EditorRemoveUnusedObjects()
if (ObjectTypeIsIntransient(objectType))
continue;
- // These object types require exactly one object to be selected at all times.
- // Removing that object can badly break the game state.
- if (objectType == ObjectType::ParkEntrance || objectType == ObjectType::Water)
+ // The water type controls the entire palette. Removing that object can badly break the game state.
+ if (objectType == ObjectType::Water)
continue;
// It’s hard to determine exactly if a scenery group is used, so do not remove these automatically.
diff --git a/src/openrct2/actions/ParkEntrancePlaceAction.cpp b/src/openrct2/actions/ParkEntrancePlaceAction.cpp
index fc56bf0b4d..8ad58ec0c8 100644
--- a/src/openrct2/actions/ParkEntrancePlaceAction.cpp
+++ b/src/openrct2/actions/ParkEntrancePlaceAction.cpp
@@ -24,9 +24,11 @@
using namespace OpenRCT2;
-ParkEntrancePlaceAction::ParkEntrancePlaceAction(const CoordsXYZD& location, ObjectEntryIndex pathType)
+ParkEntrancePlaceAction::ParkEntrancePlaceAction(
+ const CoordsXYZD& location, ObjectEntryIndex pathType, ObjectEntryIndex entranceType)
: _loc(location)
, _pathType(pathType)
+ , _entranceType(entranceType)
{
}
@@ -34,6 +36,7 @@ void ParkEntrancePlaceAction::AcceptParameters(GameActionParameterVisitor& visit
{
visitor.Visit(_loc);
visitor.Visit("footpathSurfaceObject", _pathType);
+ visitor.Visit("entranceObject", _entranceType);
}
uint16_t ParkEntrancePlaceAction::GetActionFlags() const
@@ -47,6 +50,7 @@ void ParkEntrancePlaceAction::Serialise(DataSerialiser& stream)
stream << DS_TAG(_loc);
stream << DS_TAG(_pathType);
+ stream << DS_TAG(_entranceType);
}
GameActions::Result ParkEntrancePlaceAction::Query() const
@@ -156,6 +160,7 @@ GameActions::Result ParkEntrancePlaceAction::Execute() const
entranceElement->SetDirection(_loc.direction);
entranceElement->SetSequenceIndex(index);
entranceElement->SetEntranceType(ENTRANCE_TYPE_PARK_ENTRANCE);
+ entranceElement->setEntryIndex(_entranceType);
if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL)
{
entranceElement->SetSurfaceEntryIndex(gFootpathSelection.NormalSurface);
diff --git a/src/openrct2/actions/ParkEntrancePlaceAction.h b/src/openrct2/actions/ParkEntrancePlaceAction.h
index 006eeb1123..137357f6e0 100644
--- a/src/openrct2/actions/ParkEntrancePlaceAction.h
+++ b/src/openrct2/actions/ParkEntrancePlaceAction.h
@@ -16,10 +16,11 @@ class ParkEntrancePlaceAction final : public GameActionBase
+
diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp
index 32273c985e..7954508464 100644
--- a/src/openrct2/network/NetworkBase.cpp
+++ b/src/openrct2/network/NetworkBase.cpp
@@ -49,7 +49,7 @@ using namespace OpenRCT2;
// It is used for making sure only compatible builds get connected, even within
// single OpenRCT2 version.
-constexpr uint8_t kNetworkStreamVersion = 1;
+constexpr uint8_t kNetworkStreamVersion = 2;
const std::string kNetworkStreamID = std::string(OPENRCT2_VERSION) + "-" + std::to_string(kNetworkStreamVersion);
diff --git a/src/openrct2/object/EntranceObject.cpp b/src/openrct2/object/EntranceObject.cpp
index 1a2000c1ca..d527b8b2f7 100644
--- a/src/openrct2/object/EntranceObject.cpp
+++ b/src/openrct2/object/EntranceObject.cpp
@@ -13,6 +13,8 @@
#include "../core/Json.hpp"
#include "../core/String.hpp"
#include "../drawing/Drawing.h"
+#include "../localisation/LocalisationService.h"
+#include "../paint/tile_element/Paint.TileElement.h"
using namespace OpenRCT2;
@@ -45,7 +47,6 @@ void EntranceObject::Unload()
void EntranceObject::DrawPreview(DrawPixelInfo& dpi, int32_t width, int32_t height) const
{
auto screenCoords = ScreenCoordsXY{ width / 2, height / 2 };
-
GfxDrawSprite(dpi, ImageId(_legacyType.image_id + 1), screenCoords + ScreenCoordsXY{ -32, 14 });
GfxDrawSprite(dpi, ImageId(_legacyType.image_id + 0), screenCoords + ScreenCoordsXY{ 0, 28 });
GfxDrawSprite(dpi, ImageId(_legacyType.image_id + 2), screenCoords + ScreenCoordsXY{ 32, 44 });
diff --git a/src/openrct2/object/ObjectLimits.h b/src/openrct2/object/ObjectLimits.h
index 7ee211c21b..563d54a3c3 100644
--- a/src/openrct2/object/ObjectLimits.h
+++ b/src/openrct2/object/ObjectLimits.h
@@ -21,7 +21,7 @@ constexpr uint16_t MAX_BANNER_OBJECTS = 255;
constexpr uint16_t MAX_PATH_OBJECTS = 255;
constexpr uint16_t MAX_PATH_ADDITION_OBJECTS = 255;
constexpr uint16_t MAX_SCENERY_GROUP_OBJECTS = 255;
-constexpr uint16_t MAX_PARK_ENTRANCE_OBJECTS = 1;
+constexpr uint16_t MAX_PARK_ENTRANCE_OBJECTS = 4;
constexpr uint16_t MAX_WATER_OBJECTS = 1;
constexpr uint16_t MAX_SCENARIO_TEXT_OBJECTS = 0;
constexpr uint16_t MAX_TERRAIN_SURFACE_OBJECTS = 255;
diff --git a/src/openrct2/paint/tile_element/Paint.Entrance.cpp b/src/openrct2/paint/tile_element/Paint.Entrance.cpp
index 39cf0831b2..3f3e6d1a71 100644
--- a/src/openrct2/paint/tile_element/Paint.Entrance.cpp
+++ b/src/openrct2/paint/tile_element/Paint.Entrance.cpp
@@ -284,7 +284,8 @@ static void PaintParkEntrance(PaintSession& session, uint8_t direction, int32_t
}
auto& objManager = GetContext()->GetObjectManager();
- auto entrance = reinterpret_cast(objManager.GetLoadedObject(ObjectType::ParkEntrance, 0));
+ auto entrance = reinterpret_cast(
+ objManager.GetLoadedObject(ObjectType::ParkEntrance, entranceEl.getEntryIndex()));
auto sequence = entranceEl.GetSequenceIndex();
switch (sequence)
{
diff --git a/src/openrct2/park/ParkFile.h b/src/openrct2/park/ParkFile.h
index da6bf79e7c..e538ac9fbb 100644
--- a/src/openrct2/park/ParkFile.h
+++ b/src/openrct2/park/ParkFile.h
@@ -11,7 +11,7 @@ namespace OpenRCT2
struct GameState_t;
// Current version that is saved.
- constexpr uint32_t PARK_FILE_CURRENT_VERSION = 34;
+ constexpr uint32_t PARK_FILE_CURRENT_VERSION = 35;
// The minimum version that is forwards compatible with the current version.
constexpr uint32_t PARK_FILE_MIN_VERSION = 33;
diff --git a/src/openrct2/world/Entrance.cpp b/src/openrct2/world/Entrance.cpp
index e5ed5e23c9..a10b6ac9e2 100644
--- a/src/openrct2/world/Entrance.cpp
+++ b/src/openrct2/world/Entrance.cpp
@@ -236,107 +236,3 @@ void ParkEntranceUpdateLocations()
}
}
}
-
-StationIndex EntranceElement::GetStationIndex() const
-{
- return stationIndex;
-}
-
-void EntranceElement::SetStationIndex(StationIndex newStationIndex)
-{
- stationIndex = newStationIndex;
-}
-
-uint8_t EntranceElement::GetEntranceType() const
-{
- return entranceType;
-}
-
-void EntranceElement::SetEntranceType(uint8_t newType)
-{
- entranceType = newType;
-}
-
-RideId EntranceElement::GetRideIndex() const
-{
- return rideIndex;
-}
-
-void EntranceElement::SetRideIndex(RideId newRideIndex)
-{
- rideIndex = newRideIndex;
-}
-
-uint8_t EntranceElement::GetSequenceIndex() const
-{
- return SequenceIndex & 0xF;
-}
-
-void EntranceElement::SetSequenceIndex(uint8_t newSequenceIndex)
-{
- SequenceIndex &= ~0xF;
- SequenceIndex |= (newSequenceIndex & 0xF);
-}
-
-bool EntranceElement::HasLegacyPathEntry() const
-{
- return (flags2 & ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY) != 0;
-}
-
-ObjectEntryIndex EntranceElement::GetLegacyPathEntryIndex() const
-{
- if (HasLegacyPathEntry())
- return PathType;
-
- return OBJECT_ENTRY_INDEX_NULL;
-}
-
-const FootpathObject* EntranceElement::GetLegacyPathEntry() const
-{
- auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
- return static_cast(objMgr.GetLoadedObject(ObjectType::Paths, GetLegacyPathEntryIndex()));
-}
-
-void EntranceElement::SetLegacyPathEntryIndex(ObjectEntryIndex newPathType)
-{
- PathType = newPathType;
- flags2 |= ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
-}
-
-ObjectEntryIndex EntranceElement::GetSurfaceEntryIndex() const
-{
- if (HasLegacyPathEntry())
- return OBJECT_ENTRY_INDEX_NULL;
-
- return PathType;
-}
-
-const FootpathSurfaceObject* EntranceElement::GetSurfaceEntry() const
-{
- auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
- return static_cast(objMgr.GetLoadedObject(ObjectType::FootpathSurface, GetSurfaceEntryIndex()));
-}
-
-void EntranceElement::SetSurfaceEntryIndex(ObjectEntryIndex newIndex)
-{
- PathType = newIndex;
- flags2 &= ~ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
-}
-
-const PathSurfaceDescriptor* EntranceElement::GetPathSurfaceDescriptor() const
-{
- if (HasLegacyPathEntry())
- {
- const auto* legacyPathEntry = GetLegacyPathEntry();
- if (legacyPathEntry == nullptr)
- return nullptr;
-
- return &legacyPathEntry->GetPathSurfaceDescriptor();
- }
-
- const auto* surfaceEntry = GetSurfaceEntry();
- if (surfaceEntry == nullptr)
- return nullptr;
-
- return &surfaceEntry->GetDescriptor();
-}
diff --git a/src/openrct2/world/Footpath.cpp b/src/openrct2/world/Footpath.cpp
index 927e01e8ba..d551f5bb30 100644
--- a/src/openrct2/world/Footpath.cpp
+++ b/src/openrct2/world/Footpath.cpp
@@ -87,13 +87,6 @@ const std::array DirectionOffsets = {
{ 0, -1 },
};
-// rct2: 0x0097B974
-static constexpr uint16_t EntranceDirections[] = {
- (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
-};
-
/** rct2: 0x0098D7F0 */
static constexpr uint8_t connected_path_count[] = {
0, // 0b0000
@@ -114,11 +107,6 @@ static constexpr uint8_t connected_path_count[] = {
4, // 0b1111
};
-int32_t EntranceElement::GetDirections() const
-{
- return EntranceDirections[(GetEntranceType() * 8) + GetSequenceIndex()];
-}
-
static bool entrance_has_direction(const EntranceElement& entranceElement, int32_t direction)
{
return entranceElement.GetDirections() & (1 << (direction & 3));
diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h
index ec5a46fb5a..74e78d7a29 100644
--- a/src/openrct2/world/TileElement.h
+++ b/src/openrct2/world/TileElement.h
@@ -553,15 +553,16 @@ struct EntranceElement : TileElementBase
static constexpr TileElementType ElementType = TileElementType::Entrance;
private:
- uint8_t entranceType; // 5
- uint8_t SequenceIndex; // 6. Only uses the lower nibble.
- StationIndex stationIndex; // 7
- ObjectEntryIndex PathType; // 8
- RideId rideIndex; // A
- uint8_t flags2; // C
+ uint8_t entranceType; // 5
+ uint8_t SequenceIndex; // 6. Only uses the lower nibble.
+ StationIndex stationIndex; // 7
+ ObjectEntryIndex PathType; // 8
+ RideId rideIndex; // A
+ uint8_t flags2; // C
+ ObjectEntryIndex entryIndex; // D
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
- uint8_t Pad0D[3];
+ uint8_t Pad0F[1];
#pragma clang diagnostic pop
public:
@@ -590,6 +591,9 @@ public:
const PathSurfaceDescriptor* GetPathSurfaceDescriptor() const;
int32_t GetDirections() const;
+
+ ObjectEntryIndex getEntryIndex() const;
+ void setEntryIndex(ObjectEntryIndex newIndex);
};
static_assert(sizeof(EntranceElement) == 16);
diff --git a/src/openrct2/world/tile_element/EntranceElement.cpp b/src/openrct2/world/tile_element/EntranceElement.cpp
new file mode 100644
index 0000000000..86ce7cfa43
--- /dev/null
+++ b/src/openrct2/world/tile_element/EntranceElement.cpp
@@ -0,0 +1,142 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2024 OpenRCT2 developers
+ *
+ * For a complete list of all authors, please refer to contributors.md
+ * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is licensed under the GNU General Public License version 3.
+ *****************************************************************************/
+
+#include "../../Context.h"
+#include "../../object/EntranceObject.h"
+#include "../../object/FootpathObject.h"
+#include "../../object/FootpathSurfaceObject.h"
+#include "../../object/ObjectManager.h"
+#include "../Entrance.h"
+#include "../TileElement.h"
+
+// rct2: 0x0097B974
+static constexpr uint16_t EntranceDirections[] = {
+ (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
+};
+
+uint8_t EntranceElement::GetEntranceType() const
+{
+ return entranceType;
+}
+
+void EntranceElement::SetEntranceType(uint8_t newType)
+{
+ entranceType = newType;
+}
+
+RideId EntranceElement::GetRideIndex() const
+{
+ return rideIndex;
+}
+
+void EntranceElement::SetRideIndex(RideId newRideIndex)
+{
+ rideIndex = newRideIndex;
+}
+
+StationIndex EntranceElement::GetStationIndex() const
+{
+ return stationIndex;
+}
+
+void EntranceElement::SetStationIndex(StationIndex newStationIndex)
+{
+ stationIndex = newStationIndex;
+}
+
+uint8_t EntranceElement::GetSequenceIndex() const
+{
+ return SequenceIndex & 0xF;
+}
+
+void EntranceElement::SetSequenceIndex(uint8_t newSequenceIndex)
+{
+ SequenceIndex &= ~0xF;
+ SequenceIndex |= (newSequenceIndex & 0xF);
+}
+
+bool EntranceElement::HasLegacyPathEntry() const
+{
+ return (flags2 & ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY) != 0;
+}
+
+ObjectEntryIndex EntranceElement::GetLegacyPathEntryIndex() const
+{
+ if (HasLegacyPathEntry())
+ return PathType;
+
+ return OBJECT_ENTRY_INDEX_NULL;
+}
+
+const FootpathObject* EntranceElement::GetLegacyPathEntry() const
+{
+ auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
+ return static_cast(objMgr.GetLoadedObject(ObjectType::Paths, GetLegacyPathEntryIndex()));
+}
+
+void EntranceElement::SetLegacyPathEntryIndex(ObjectEntryIndex newPathType)
+{
+ PathType = newPathType;
+ flags2 |= ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
+}
+
+ObjectEntryIndex EntranceElement::GetSurfaceEntryIndex() const
+{
+ if (HasLegacyPathEntry())
+ return OBJECT_ENTRY_INDEX_NULL;
+
+ return PathType;
+}
+
+const FootpathSurfaceObject* EntranceElement::GetSurfaceEntry() const
+{
+ auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
+ return static_cast(objMgr.GetLoadedObject(ObjectType::FootpathSurface, GetSurfaceEntryIndex()));
+}
+
+void EntranceElement::SetSurfaceEntryIndex(ObjectEntryIndex newIndex)
+{
+ PathType = newIndex;
+ flags2 &= ~ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
+}
+
+const PathSurfaceDescriptor* EntranceElement::GetPathSurfaceDescriptor() const
+{
+ if (HasLegacyPathEntry())
+ {
+ const auto* legacyPathEntry = GetLegacyPathEntry();
+ if (legacyPathEntry == nullptr)
+ return nullptr;
+
+ return &legacyPathEntry->GetPathSurfaceDescriptor();
+ }
+
+ const auto* surfaceEntry = GetSurfaceEntry();
+ if (surfaceEntry == nullptr)
+ return nullptr;
+
+ return &surfaceEntry->GetDescriptor();
+}
+
+int32_t EntranceElement::GetDirections() const
+{
+ return EntranceDirections[(GetEntranceType() * 8) + GetSequenceIndex()];
+}
+
+ObjectEntryIndex EntranceElement::getEntryIndex() const
+{
+ return entryIndex;
+}
+
+void EntranceElement::setEntryIndex(ObjectEntryIndex newIndex)
+{
+ entryIndex = newIndex;
+}