diff --git a/src/openrct2-ui/windows/Footpath.cpp b/src/openrct2-ui/windows/Footpath.cpp index ed4915bccc..e58c686ed8 100644 --- a/src/openrct2-ui/windows/Footpath.cpp +++ b/src/openrct2-ui/windows/Footpath.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -31,11 +35,12 @@ static constexpr const int32_t WH = 381; static constexpr const int32_t WW = 106; static constexpr const uint16_t ARROW_PULSE_DURATION = 200; -static uint8_t _footpathSelectedType; static uint8_t _footpathConstructDirection; static uint8_t _footpathConstructValidDirections; static uint8_t _footpathConstructionMode; +static std::vector> _dropdownEntries; + // clang-format off enum { @@ -178,7 +183,7 @@ static void window_footpath_start_bridge_at_point(const ScreenCoordsXY& screenCo static void window_footpath_construct(); static void window_footpath_remove(); static void window_footpath_set_enabled_and_pressed_widgets(); -static void footpath_get_next_path_info(int32_t* type, CoordsXYZ& footpathLoc, int32_t* slope); +static void footpath_get_next_path_info(ObjectEntryIndex* type, CoordsXYZ& footpathLoc, int32_t* slope); static bool footpath_select_default(); /** @@ -187,21 +192,10 @@ static bool footpath_select_default(); */ rct_window* window_footpath_open() { - // If a restricted path was selected when the game is no longer in Sandbox mode, reset it - const auto* legacyPathEntry = GetLegacyFootpathEntry(gFootpathSelectedId); - if (legacyPathEntry != nullptr && legacyPathEntry->GetPathSurfaceDescriptor().IsEditorOnly() && !gCheatsSandboxMode) + if (!footpath_select_default()) { - legacyPathEntry = nullptr; - } - - // Select the default path if we don't have one - if (legacyPathEntry == nullptr) - { - if (!footpath_select_default()) - { - // No path objects to select from, don't open window - return nullptr; - } + // No path objects to select from, don't open window + return nullptr; } // Check if window is already open @@ -211,7 +205,7 @@ rct_window* window_footpath_open() return window; } - window = WindowCreate(ScreenCoordsXY(0, 29), 106, 381, &window_footpath_events, WC_FOOTPATH, 0); + window = WindowCreate(ScreenCoordsXY(0, 29), WW, WH, &window_footpath_events, WC_FOOTPATH, 0); window->widgets = window_footpath_widgets; window->enabled_widgets = (1ULL << WIDX_CLOSE) | (1ULL << WIDX_FOOTPATH_TYPE) | (1ULL << WIDX_QUEUELINE_TYPE) | (1ULL << WIDX_DIRECTION_NW) | (1ULL << WIDX_DIRECTION_NE) | (1ULL << WIDX_DIRECTION_SW) | (1ULL << WIDX_DIRECTION_SE) @@ -344,53 +338,41 @@ static void window_footpath_mousedown(rct_window* w, rct_widgetindex widgetIndex */ static void window_footpath_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex) { + if (dropdownIndex < 0 || static_cast(dropdownIndex) >= _dropdownEntries.size()) + return; + + auto entryIndex = _dropdownEntries[dropdownIndex]; if (widgetIndex == WIDX_FOOTPATH_TYPE) { - _footpathSelectedType = SELECTED_PATH_TYPE_NORMAL; + gFootpathSelection.IsQueueSelected = false; + if (entryIndex.first == ObjectType::Paths) + { + gFootpathSelection.LegacyPath = entryIndex.second; + } + else + { + gFootpathSelection.LegacyPath = OBJECT_ENTRY_INDEX_NULL; + gFootpathSelection.NormalSurface = entryIndex.second; + } } else if (widgetIndex == WIDX_QUEUELINE_TYPE) { - _footpathSelectedType = SELECTED_PATH_TYPE_QUEUE; + gFootpathSelection.IsQueueSelected = true; + if (entryIndex.first == ObjectType::Paths) + { + gFootpathSelection.LegacyPath = entryIndex.second; + } + else + { + gFootpathSelection.LegacyPath = OBJECT_ENTRY_INDEX_NULL; + gFootpathSelection.QueueSurface = entryIndex.second; + } } else { return; } - // Get path id - int32_t pathId = dropdownIndex; - if (pathId == -1) - { - pathId = gFootpathSelectedId; - } - else - { - bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode); - - int32_t i = 0, j = 0; - for (; i < MAX_PATH_OBJECTS; i++) - { - const auto* legacyPathEntry = GetLegacyFootpathEntry(i); - if (legacyPathEntry == nullptr) - continue; - - const PathSurfaceDescriptor& surfaceDescriptor = legacyPathEntry->GetPathSurfaceDescriptor(); - if (surfaceDescriptor.IsEditorOnly() && !showEditorPaths) - { - continue; - } - - if (j == pathId) - { - break; - } - j++; - } - pathId = i; - } - - // Set selected path id - gFootpathSelectedId = pathId; footpath_provisional_update(); _window_footpath_cost = MONEY32_UNDEFINED; w->Invalidate(); @@ -458,7 +440,7 @@ static void window_footpath_toolup(rct_window* w, rct_widgetindex widgetIndex, c */ static void window_footpath_update_provisional_path_for_bridge_mode(rct_window* w) { - int32_t type, slope; + int32_t slope; if (_footpathConstructionMode != PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL) { @@ -475,9 +457,21 @@ static void window_footpath_update_provisional_path_for_bridge_mode(rct_window* // Update provisional bridge mode path if (!(gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_1)) { + ObjectEntryIndex type; + ObjectEntryIndex railings = gFootpathSelection.Railings; + CoordsXYZ footpathLoc; footpath_get_next_path_info(&type, footpathLoc, &slope); - _window_footpath_cost = footpath_provisional_set(type, footpathLoc, slope); + PathConstructFlags pathConstructFlags = 0; + if (gFootpathSelection.IsQueueSelected) + pathConstructFlags |= PathConstructFlag::IsQueue; + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL) + { + pathConstructFlags |= PathConstructFlag::IsLegacyPathObject; + type = gFootpathSelection.LegacyPath; + } + + _window_footpath_cost = footpath_provisional_set(type, railings, footpathLoc, slope, pathConstructFlags); widget_invalidate(w, WIDX_CONSTRUCT); } @@ -490,7 +484,7 @@ static void window_footpath_update_provisional_path_for_bridge_mode(rct_window* gProvisionalFootpath.Flags ^= PROVISIONAL_PATH_FLAG_SHOW_ARROW; CoordsXYZ footpathLoc; - footpath_get_next_path_info(&type, footpathLoc, &slope); + footpath_get_next_path_info(nullptr, footpathLoc, &slope); gMapSelectArrowPosition = footpathLoc; gMapSelectArrowDirection = _footpathConstructDirection; if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_SHOW_ARROW) @@ -564,25 +558,52 @@ static void window_footpath_invalidate(rct_window* w) // Press / unpress footpath and queue type buttons w->pressed_widgets &= ~(1ULL << WIDX_FOOTPATH_TYPE); w->pressed_widgets &= ~(1ULL << WIDX_QUEUELINE_TYPE); - w->pressed_widgets |= _footpathSelectedType == SELECTED_PATH_TYPE_NORMAL ? (1ULL << WIDX_FOOTPATH_TYPE) - : (1ULL << WIDX_QUEUELINE_TYPE); + w->pressed_widgets |= gFootpathSelection.IsQueueSelected ? (1ULL << WIDX_QUEUELINE_TYPE) : (1ULL << WIDX_FOOTPATH_TYPE); // Enable / disable construct button window_footpath_widgets[WIDX_CONSTRUCT].type = _footpathConstructionMode == PATH_CONSTRUCTION_MODE_BRIDGE_OR_TUNNEL ? WindowWidgetType::ImgBtn : WindowWidgetType::Empty; - // Set footpath and queue type button images - auto pathImage = static_cast(SPR_NONE); - auto queueImage = static_cast(SPR_NONE); - const auto* legacyPathEntry = GetLegacyFootpathEntry(gFootpathSelectedId); - if (legacyPathEntry != nullptr) + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) { - pathImage = legacyPathEntry->GetPathSurfaceDescriptor().PreviewImage; - queueImage = legacyPathEntry->GetQueueSurfaceDescriptor().PreviewImage; + // Set footpath and queue type button images + auto pathImage = static_cast(SPR_NONE); + auto queueImage = static_cast(SPR_NONE); + auto pathEntry = GetPathSurfaceEntry(gFootpathSelection.NormalSurface); + if (pathEntry != nullptr) + { + pathImage = pathEntry->PreviewImageId; + } + + pathEntry = GetPathSurfaceEntry(gFootpathSelection.QueueSurface); + if (pathEntry != nullptr) + { + queueImage = pathEntry->PreviewImageId; + } + + window_footpath_widgets[WIDX_FOOTPATH_TYPE].image = pathImage; + window_footpath_widgets[WIDX_QUEUELINE_TYPE].image = queueImage; + } + else + { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + + // Set footpath and queue type button images + auto pathImage = static_cast(SPR_NONE); + auto queueImage = static_cast(SPR_NONE); + auto pathObj = static_cast( + objManager.GetLoadedObject(ObjectType::Paths, gFootpathSelection.LegacyPath)); + if (pathObj != nullptr) + { + auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); + pathImage = pathEntry->GetPreviewImage(); + queueImage = pathEntry->GetQueuePreviewImage(); + } + + window_footpath_widgets[WIDX_FOOTPATH_TYPE].image = pathImage; + window_footpath_widgets[WIDX_QUEUELINE_TYPE].image = queueImage; } - window_footpath_widgets[WIDX_FOOTPATH_TYPE].image = pathImage; - window_footpath_widgets[WIDX_QUEUELINE_TYPE].image = queueImage; } /** @@ -607,22 +628,42 @@ static void window_footpath_paint(rct_window* w, rct_drawpixelinfo* dpi) { slope = TILE_ELEMENT_SLOPE_E_CORNER_UP; } - int32_t image = ConstructionPreviewImages[slope][direction]; - const auto* legacyPathEntry = GetLegacyFootpathEntry(gFootpathSelectedId); - if (legacyPathEntry != nullptr) + std::optional baseImage; + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) { - if (_footpathSelectedType == SELECTED_PATH_TYPE_NORMAL) - image += legacyPathEntry->GetPathSurfaceDescriptor().Image; - else - image += legacyPathEntry->GetQueueSurfaceDescriptor().Image; + auto selectedPath = gFootpathSelection.GetSelectedSurface(); + const auto* pathType = GetPathSurfaceEntry(selectedPath); + if (pathType != nullptr) + { + baseImage = pathType->BaseImageId; + } + } + else + { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto* pathObj = static_cast( + objManager.GetLoadedObject(ObjectType::Paths, gFootpathSelection.LegacyPath)); + if (pathObj != nullptr) + { + auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); + if (gFootpathSelection.IsQueueSelected) + baseImage = pathEntry->GetQueueImage(); + else + baseImage = pathEntry->image; + } } - // Draw construction image - screenCoords = w->windowPos - + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), - window_footpath_widgets[WIDX_CONSTRUCT].bottom - 60 }; - gfx_draw_sprite(dpi, ImageId(image), screenCoords); + if (baseImage) + { + auto image = *baseImage + ConstructionPreviewImages[slope][direction]; + + // Draw construction image + screenCoords = w->windowPos + + ScreenCoordsXY{ window_footpath_widgets[WIDX_CONSTRUCT].midX(), + window_footpath_widgets[WIDX_CONSTRUCT].bottom - 60 }; + gfx_draw_sprite(dpi, ImageId(image), screenCoords); + } // Draw build this... label screenCoords = w->windowPos @@ -651,30 +692,63 @@ static void window_footpath_paint(rct_window* w, rct_drawpixelinfo* dpi) */ static void window_footpath_show_footpath_types_dialog(rct_window* w, rct_widget* widget, bool showQueues) { + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + uint32_t numPathTypes = 0; // If the game is in sandbox mode, also show paths that are normally restricted to the scenario editor bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode); - for (int32_t i = 0; i < MAX_PATH_OBJECTS; i++) + _dropdownEntries.clear(); + std::optional defaultIndex; + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_SURFACE_OBJECTS; i++) { - const auto* legacyPathEntry = GetLegacyFootpathEntry(i); - if (legacyPathEntry == nullptr) + const auto* pathType = static_cast(objManager.GetLoadedObject(ObjectType::FootpathSurface, i)); + if (pathType == nullptr) { continue; } - - const auto& surfaceDescriptor = (showQueues) ? legacyPathEntry->GetQueueSurfaceDescriptor() - : legacyPathEntry->GetPathSurfaceDescriptor(); - - if (surfaceDescriptor.IsEditorOnly() && !showEditorPaths) + if ((pathType->Flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) && !showEditorPaths) { continue; } - - const auto image = surfaceDescriptor.PreviewImage; + if (showQueues != ((pathType->Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0)) + { + continue; + } + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL + && i == (showQueues ? gFootpathSelection.QueueSurface : gFootpathSelection.NormalSurface)) + { + defaultIndex = numPathTypes; + } gDropdownItemsFormat[numPathTypes] = STR_NONE; - gDropdownItemsArgs[numPathTypes] = image; + gDropdownItemsArgs[numPathTypes] = pathType->PreviewImageId; + _dropdownEntries.push_back({ ObjectType::FootpathSurface, i }); + numPathTypes++; + } + + for (ObjectEntryIndex i = 0; i < MAX_PATH_OBJECTS; i++) + { + auto* pathObj = static_cast(objManager.GetLoadedObject(ObjectType::Paths, i)); + if (pathObj == nullptr) + { + continue; + } + + auto pathEntry = reinterpret_cast(pathObj->GetLegacyData()); + if ((pathEntry->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR) && !showEditorPaths) + { + continue; + } + + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL && gFootpathSelection.LegacyPath == i) + { + defaultIndex = numPathTypes; + } + + gDropdownItemsFormat[numPathTypes] = STR_NONE; + gDropdownItemsArgs[numPathTypes] = showQueues ? pathEntry->GetQueuePreviewImage() : pathEntry->GetPreviewImage(); + _dropdownEntries.push_back({ ObjectType::Paths, i }); numPathTypes++; } @@ -682,6 +756,8 @@ static void window_footpath_show_footpath_types_dialog(rct_window* w, rct_widget WindowDropdownShowImage( w->windowPos.x + widget->left, w->windowPos.y + widget->top, widget->height() + 1, w->colours[1], 0, numPathTypes, 47, 36, itemsPerRow); + if (defaultIndex) + gDropdownDefaultIndex = static_cast(*defaultIndex); } /** @@ -777,9 +853,20 @@ static void window_footpath_set_provisional_path_at_point(const ScreenCoordsXY& slope &= ~RAISE_FOOTPATH_FLAG; z += PATH_HEIGHT_STEP; } - int32_t pathType = (_footpathSelectedType << 7) + (gFootpathSelectedId & 0xFF); - _window_footpath_cost = footpath_provisional_set(pathType, { info.Loc, z }, slope); + PathConstructFlags constructFlags = 0; + auto pathType = gFootpathSelection.GetSelectedSurface(); + if (gFootpathSelection.IsQueueSelected) + { + constructFlags |= PathConstructFlag::IsQueue; + } + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL) + { + constructFlags |= PathConstructFlag::IsLegacyPathObject; + pathType = gFootpathSelection.LegacyPath; + } + _window_footpath_cost = footpath_provisional_set( + pathType, gFootpathSelection.Railings, { info.Loc, z }, slope, constructFlags); window_invalidate_by_class(WC_FOOTPATH); } } @@ -872,11 +959,22 @@ static void window_footpath_place_path_at_point(const ScreenCoordsXY& screenCoor slope &= ~RAISE_FOOTPATH_FLAG; z += PATH_HEIGHT_STEP; } - auto selectedType = (_footpathSelectedType << 7) + (gFootpathSelectedId & 0xFF); // Try and place path gGameCommandErrorTitle = STR_CANT_BUILD_FOOTPATH_HERE; - auto footpathPlaceAction = FootpathPlaceAction({ info.Loc, z }, slope, selectedType); + auto selectedType = gFootpathSelection.GetSelectedSurface(); + PathConstructFlags constructFlags = 0; + if (gFootpathSelection.IsQueueSelected) + { + constructFlags |= PathConstructFlag::IsQueue; + } + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL) + { + constructFlags |= PathConstructFlag::IsLegacyPathObject; + selectedType = gFootpathSelection.LegacyPath; + } + auto footpathPlaceAction = FootpathPlaceAction( + { info.Loc, z }, slope, selectedType, gFootpathSelection.Railings, INVALID_DIRECTION, constructFlags); footpathPlaceAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) { if (result->Error == GameActions::Status::Ok) { @@ -960,12 +1058,22 @@ static void window_footpath_construct() _window_footpath_cost = MONEY32_UNDEFINED; footpath_provisional_update(); - int32_t type, slope; + ObjectEntryIndex type; + int32_t slope; CoordsXYZ footpathLoc; footpath_get_next_path_info(&type, footpathLoc, &slope); gGameCommandErrorTitle = STR_CANT_BUILD_FOOTPATH_HERE; - auto footpathPlaceAction = FootpathPlaceAction(footpathLoc, slope, type, _footpathConstructDirection); + PathConstructFlags constructFlags = 0; + if (gFootpathSelection.IsQueueSelected) + constructFlags |= PathConstructFlag::IsQueue; + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL) + { + constructFlags |= PathConstructFlag::IsLegacyPathObject; + type = gFootpathSelection.LegacyPath; + } + auto footpathPlaceAction = FootpathPlaceAction( + footpathLoc, slope, type, gFootpathSelection.Railings, _footpathConstructDirection, constructFlags); footpathPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { if (result->Error == GameActions::Status::Ok) { @@ -1201,15 +1309,16 @@ static void window_footpath_set_enabled_and_pressed_widgets() * * rct2: 0x006A7B20 */ -static void footpath_get_next_path_info(int32_t* type, CoordsXYZ& footpathLoc, int32_t* slope) +static void footpath_get_next_path_info(ObjectEntryIndex* type, CoordsXYZ& footpathLoc, int32_t* slope) { - int32_t direction; - - direction = _footpathConstructDirection; + auto direction = _footpathConstructDirection; footpathLoc.x = gFootpathConstructFromPosition.x + CoordsDirectionDelta[direction].x; footpathLoc.y = gFootpathConstructFromPosition.y + CoordsDirectionDelta[direction].y; footpathLoc.z = gFootpathConstructFromPosition.z; - *type = (_footpathSelectedType << 7) + (gFootpathSelectedId & 0xFF); + if (type != nullptr) + { + *type = gFootpathSelection.GetSelectedSurface(); + } *slope = TILE_ELEMENT_SLOPE_FLAT; if (gFootpathConstructSlope != 0) { @@ -1222,34 +1331,141 @@ static void footpath_get_next_path_info(int32_t* type, CoordsXYZ& footpathLoc, i } } -static bool footpath_select_default() +static ObjectEntryIndex footpath_get_default_surface(bool queue) { - // Select first available footpath - int32_t footpathId = -1; - for (int32_t i = 0; i < object_entry_group_counts[EnumValue(ObjectType::Paths)]; i++) + bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode); + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_SURFACE_OBJECTS; i++) { - const auto* footpathObject = GetLegacyFootpathEntry(i); - if (footpathObject == nullptr) - continue; - - footpathId = i; - - const PathSurfaceDescriptor& surfaceDescriptor = footpathObject->GetPathSurfaceDescriptor(); - // Prioritise non-restricted path - if (!surfaceDescriptor.IsEditorOnly()) + auto pathEntry = GetPathSurfaceEntry(i); + if (pathEntry != nullptr) { - break; + 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; + } } } - if (footpathId == -1) + return OBJECT_ENTRY_INDEX_NULL; +} + +static ObjectEntryIndex footpath_get_default_railing() +{ + for (ObjectEntryIndex i = 0; i < MAX_FOOTPATH_RAILINGS_OBJECTS; i++) { - return false; + const auto* railingEntry = GetPathRailingsEntry(i); + if (railingEntry != nullptr) + { + return i; + } } - else + return OBJECT_ENTRY_INDEX_NULL; +} + +static bool footpath_is_surface_okay(ObjectEntryIndex index, bool queue) +{ + auto pathEntry = GetPathSurfaceEntry(index); + if (pathEntry != nullptr) { - gFootpathSelectedId = footpathId; - return true; + 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 bool footpath_is_legacy_path_okay(ObjectEntryIndex index) +{ + bool showEditorPaths = ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode); + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto footpathObj = static_cast(objManager.GetLoadedObject(ObjectType::Paths, index)); + if (footpathObj != nullptr) + { + auto pathEntry = reinterpret_cast(footpathObj->GetLegacyData()); + return showEditorPaths || !(pathEntry->flags & FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR); + } + return false; +} + +static ObjectEntryIndex footpath_get_default_legacy_path() +{ + for (ObjectEntryIndex i = 0; i < MAX_PATH_OBJECTS; i++) + { + if (footpath_is_legacy_path_okay(i)) + { + return i; + } + } + return OBJECT_ENTRY_INDEX_NULL; +} + +static bool footpath_select_default() +{ + // Select default footpath + auto surfaceIndex = footpath_get_default_surface(false); + if (footpath_is_surface_okay(gFootpathSelection.NormalSurface, false)) + { + surfaceIndex = gFootpathSelection.NormalSurface; + } + + // Select default queue + auto queueIndex = footpath_get_default_surface(true); + if (footpath_is_surface_okay(gFootpathSelection.QueueSurface, true)) + { + queueIndex = gFootpathSelection.QueueSurface; + } + + // Select default railing + auto railingIndex = footpath_get_default_railing(); + const auto* railingEntry = GetPathRailingsEntry(gFootpathSelection.Railings); + if (railingEntry != nullptr) + { + railingIndex = gFootpathSelection.Railings; + } + + // Select default legacy path + auto legacyPathIndex = footpath_get_default_legacy_path(); + if (gFootpathSelection.LegacyPath != OBJECT_ENTRY_INDEX_NULL) + { + if (footpath_is_legacy_path_okay(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; + } + else + { + // No surfaces available, so default to legacy path + gFootpathSelection.LegacyPath = legacyPathIndex; + } + } + + gFootpathSelection.NormalSurface = surfaceIndex; + gFootpathSelection.QueueSurface = queueIndex; + gFootpathSelection.Railings = railingIndex; + return true; } void window_footpath_keyboard_shortcut_turn_left() @@ -1349,5 +1565,5 @@ void window_footpath_keyboard_shortcut_build_current() void window_footpath_reset_selected_path() { - _footpathSelectedType = 0; + gFootpathSelection = {}; } diff --git a/src/openrct2-ui/windows/TileInspector.cpp b/src/openrct2-ui/windows/TileInspector.cpp index 575710c2ea..dbc2dfe747 100644 --- a/src/openrct2-ui/windows/TileInspector.cpp +++ b/src/openrct2-ui/windows/TileInspector.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/src/openrct2/actions/FootpathPlaceAction.cpp b/src/openrct2/actions/FootpathPlaceAction.cpp index b294f1ee2a..de6bda9fa8 100644 --- a/src/openrct2/actions/FootpathPlaceAction.cpp +++ b/src/openrct2/actions/FootpathPlaceAction.cpp @@ -25,11 +25,15 @@ using namespace OpenRCT2; -FootpathPlaceAction::FootpathPlaceAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, Direction direction) +FootpathPlaceAction::FootpathPlaceAction( + const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, ObjectEntryIndex railingsType, Direction direction, + PathConstructFlags constructFlags) : _loc(loc) , _slope(slope) , _type(type) + , _railingsType(railingsType) , _direction(direction) + , _constructFlags(constructFlags) { } @@ -37,8 +41,10 @@ void FootpathPlaceAction::AcceptParameters(GameActionParameterVisitor& visitor) { visitor.Visit(_loc); visitor.Visit("object", _type); + visitor.Visit("railingsObject", _railingsType); visitor.Visit("direction", _direction); visitor.Visit("slope", _slope); + visitor.Visit("constructFlags", _constructFlags); } uint16_t FootpathPlaceAction::GetActionFlags() const @@ -50,7 +56,8 @@ void FootpathPlaceAction::Serialise(DataSerialiser& stream) { GameAction::Serialise(stream); - stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_direction); + stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_railingsType) << DS_TAG(_direction) + << DS_TAG(_constructFlags); } GameActions::Result::Ptr FootpathPlaceAction::Query() const @@ -149,11 +156,64 @@ GameActions::Result::Ptr FootpathPlaceAction::Execute() const } } +bool FootpathPlaceAction::IsSameAsPathElement(const PathElement* pathElement) const +{ + // Check if both this action and the element is queue + if (pathElement->IsQueue() != ((_constructFlags & PathConstructFlag::IsQueue) != 0)) + return false; + + auto footpathObj = pathElement->GetLegacyPathEntry(); + if (footpathObj == nullptr) + { + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + return false; + } + else + { + return pathElement->GetSurfaceEntryIndex() == _type && pathElement->GetRailingsEntryIndex() == _railingsType; + } + } + else + { + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + return pathElement->GetLegacyPathEntryIndex() == _type; + } + else + { + return false; + } + } +} + +bool FootpathPlaceAction::IsSameAsEntranceElement(const EntranceElement& entranceElement) const +{ + if (entranceElement.HasLegacyPathEntry()) + { + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + return entranceElement.GetLegacyPathEntryIndex() == _type; + } + else + { + return false; + } + } + + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + return false; + } + else + { + return entranceElement.GetSurfaceEntryIndex() == _type; + } +} + GameActions::Result::Ptr FootpathPlaceAction::ElementUpdateQuery(PathElement* pathElement, GameActions::Result::Ptr res) const { - const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4)); - const bool newPathIsQueue = ((_type >> 7) == 1); - if (pathElement->GetLegacyPathEntryIndex() != newFootpathType || pathElement->IsQueue() != newPathIsQueue) + if (!IsSameAsPathElement(pathElement)) { res->Cost += MONEY(6, 00); } @@ -167,9 +227,7 @@ GameActions::Result::Ptr FootpathPlaceAction::ElementUpdateQuery(PathElement* pa GameActions::Result::Ptr FootpathPlaceAction::ElementUpdateExecute(PathElement* pathElement, GameActions::Result::Ptr res) const { - const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4)); - const bool newPathIsQueue = ((_type >> 7) == 1); - if (pathElement->GetLegacyPathEntryIndex() != newFootpathType || pathElement->IsQueue() != newPathIsQueue) + if (!IsSameAsPathElement(pathElement)) { res->Cost += MONEY(6, 00); } @@ -181,14 +239,22 @@ GameActions::Result::Ptr FootpathPlaceAction::ElementUpdateExecute(PathElement* footpath_remove_edges_at(_loc, reinterpret_cast(pathElement)); } - pathElement->SetLegacyPathEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE); - bool isQueue = _type & FOOTPATH_ELEMENT_INSERT_QUEUE; - pathElement->SetIsQueue(isQueue); + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + pathElement->SetLegacyPathEntryIndex(_type); + } + else + { + pathElement->SetSurfaceEntryIndex(_type); + pathElement->SetRailingsEntryIndex(_railingsType); + } + + pathElement->SetIsQueue((_constructFlags & PathConstructFlag::IsQueue) != 0); auto* elem = pathElement->GetAdditionEntry(); if (elem != nullptr) { - if (isQueue) + if (_constructFlags & PathConstructFlag::IsQueue) { // remove any addition that isn't a TV or a lamp if ((elem->flags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) == 0 && (elem->flags & PATH_BIT_FLAG_LAMP) == 0) @@ -238,16 +304,16 @@ GameActions::Result::Ptr FootpathPlaceAction::ElementInsertQuery(GameActions::Re { entrancePath = true; // Make the price the same as replacing a path - if (entranceElement->GetLegacyPathEntryIndex() == (_type & 0xF)) + if (IsSameAsEntranceElement(*entranceElement)) entranceIsSamePath = true; else res->Cost -= MONEY(6, 00); } - // Do not attempt to build a crossing with a queue or a sloped. - uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT) - ? CREATE_CROSSING_MODE_NONE - : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + // Do not attempt to build a crossing with a queue or a sloped path. + auto isQueue = _constructFlags & PathConstructFlag::IsQueue; + uint8_t crossingMode = isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; auto canBuild = MapCanConstructWithClearAt( { _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), crossingMode); if (!entrancePath && canBuild->Error != GameActions::Status::Ok) @@ -304,16 +370,16 @@ GameActions::Result::Ptr FootpathPlaceAction::ElementInsertExecute(GameActions:: { entrancePath = true; // Make the price the same as replacing a path - if (entranceElement->GetLegacyPathEntryIndex() == (_type & 0xF)) + if (IsSameAsEntranceElement(*entranceElement)) entranceIsSamePath = true; else res->Cost -= MONEY(6, 00); } // Do not attempt to build a crossing with a queue or a sloped. - uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT) - ? CREATE_CROSSING_MODE_NONE - : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + auto isQueue = _constructFlags & PathConstructFlag::IsQueue; + uint8_t crossingMode = isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; auto canBuild = MapCanConstructWithClearAt( { _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GAME_COMMAND_FLAG_APPLY | GetFlags(), crossingMode); @@ -338,8 +404,14 @@ GameActions::Result::Ptr FootpathPlaceAction::ElementInsertExecute(GameActions:: { if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath) { - // Set the path type but make sure it's not a queue as that will not show up - entranceElement->SetLegacyPathEntryIndex(_type & 0x7F); + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + entranceElement->SetLegacyPathEntryIndex(_type); + } + else + { + entranceElement->SetSurfaceEntryIndex(_type); + } map_invalidate_tile_full(_loc); } } @@ -349,10 +421,18 @@ GameActions::Result::Ptr FootpathPlaceAction::ElementInsertExecute(GameActions:: Guard::Assert(pathElement != nullptr); pathElement->SetClearanceZ(zHigh); - pathElement->SetLegacyPathEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE); + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + pathElement->SetLegacyPathEntryIndex(_type); + } + else + { + pathElement->SetSurfaceEntryIndex(_type); + pathElement->SetRailingsEntryIndex(_railingsType); + } pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK); pathElement->SetSloped(_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED); - pathElement->SetIsQueue(_type & FOOTPATH_ELEMENT_INSERT_QUEUE); + pathElement->SetIsQueue(isQueue); pathElement->SetAddition(0); pathElement->SetRideIndex(RIDE_ID_NULL); pathElement->SetAdditionStatus(255); diff --git a/src/openrct2/actions/FootpathPlaceAction.h b/src/openrct2/actions/FootpathPlaceAction.h index f8b4ff83ce..94df946e20 100644 --- a/src/openrct2/actions/FootpathPlaceAction.h +++ b/src/openrct2/actions/FootpathPlaceAction.h @@ -18,12 +18,15 @@ private: CoordsXYZ _loc; uint8_t _slope{}; ObjectEntryIndex _type{}; + ObjectEntryIndex _railingsType{}; Direction _direction{ INVALID_DIRECTION }; + PathConstructFlags _constructFlags{}; public: FootpathPlaceAction() = default; - FootpathPlaceAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, Direction direction = INVALID_DIRECTION); - + FootpathPlaceAction( + const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, ObjectEntryIndex railingsType, + Direction direction = INVALID_DIRECTION, PathConstructFlags constructFlags = 0); void AcceptParameters(GameActionParameterVisitor & visitor) override; uint16_t GetActionFlags() const override; @@ -40,4 +43,6 @@ private: void AutomaticallySetPeepSpawn() const; void RemoveIntersectingWalls(PathElement * pathElement) const; PathElement* map_get_footpath_element_slope(const CoordsXYZ& footpathPos, int32_t slope) const; + bool IsSameAsPathElement(const PathElement* pathElement) const; + bool IsSameAsEntranceElement(const EntranceElement& entranceElement) const; }; diff --git a/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp b/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp index 0c73ba201d..3a9e192aa6 100644 --- a/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp +++ b/src/openrct2/actions/FootpathPlaceFromTrackAction.cpp @@ -22,11 +22,14 @@ #include "../world/Wall.h" FootpathPlaceFromTrackAction::FootpathPlaceFromTrackAction( - const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, uint8_t edges) + const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, ObjectEntryIndex railingsType, uint8_t edges, + PathConstructFlags constructFlags) : _loc(loc) , _slope(slope) , _type(type) + , _railingsType(railingsType) , _edges(edges) + , _constructFlags(constructFlags) { } @@ -34,7 +37,8 @@ void FootpathPlaceFromTrackAction::Serialise(DataSerialiser& stream) { GameAction::Serialise(stream); - stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_edges); + stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_railingsType) << DS_TAG(_edges) + << DS_TAG(_constructFlags); } uint16_t FootpathPlaceFromTrackAction::GetActionFlags() const @@ -122,16 +126,16 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertQuery(GameAc { entrancePath = true; // Make the price the same as replacing a path - if (entranceElement->GetLegacyPathEntryIndex() == (_type & 0xF)) + if (IsSameAsEntranceElement(*entranceElement)) entranceIsSamePath = true; else res->Cost -= MONEY(6, 00); } - // Do not attempt to build a crossing with a queue or a sloped. - uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT) - ? CREATE_CROSSING_MODE_NONE - : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + // Do not attempt to build a crossing with a queue or a sloped path. + auto isQueue = _constructFlags & PathConstructFlag::IsQueue; + uint8_t crossingMode = isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; auto canBuild = MapCanConstructWithClearAt( { _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), crossingMode); if (!entrancePath && canBuild->Error != GameActions::Status::Ok) @@ -189,16 +193,16 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(Game { entrancePath = true; // Make the price the same as replacing a path - if (entranceElement->GetLegacyPathEntryIndex() == (_type & 0xF)) + if (IsSameAsEntranceElement(*entranceElement)) entranceIsSamePath = true; else res->Cost -= MONEY(6, 00); } - // Do not attempt to build a crossing with a queue or a sloped. - uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT) - ? CREATE_CROSSING_MODE_NONE - : CREATE_CROSSING_MODE_PATH_OVER_TRACK; + // Do not attempt to build a crossing with a queue or a sloped path. + auto isQueue = _constructFlags & PathConstructFlag::IsQueue; + uint8_t crossingMode = isQueue || (_slope != TILE_ELEMENT_SLOPE_FLAT) ? CREATE_CROSSING_MODE_NONE + : CREATE_CROSSING_MODE_PATH_OVER_TRACK; auto canBuild = MapCanConstructWithClearAt( { _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GAME_COMMAND_FLAG_APPLY | GetFlags(), crossingMode); @@ -222,8 +226,14 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(Game { if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath) { - // Set the path type but make sure it's not a queue as that will not show up - entranceElement->SetLegacyPathEntryIndex(_type & 0x7F); + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + entranceElement->SetLegacyPathEntryIndex(_type); + } + else + { + entranceElement->SetSurfaceEntryIndex(_type); + } map_invalidate_tile_full(_loc); } } @@ -233,10 +243,18 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(Game Guard::Assert(pathElement != nullptr); pathElement->SetClearanceZ(zHigh); - pathElement->SetLegacyPathEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE); + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + pathElement->SetLegacyPathEntryIndex(_type); + } + else + { + pathElement->SetSurfaceEntryIndex(_type); + pathElement->SetRailingsEntryIndex(_railingsType); + } pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK); pathElement->SetSloped(_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED); - pathElement->SetIsQueue(_type & FOOTPATH_ELEMENT_INSERT_QUEUE); + pathElement->SetIsQueue(isQueue); pathElement->SetAddition(0); pathElement->SetRideIndex(RIDE_ID_NULL); pathElement->SetAdditionStatus(255); @@ -254,3 +272,27 @@ GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(Game return res; } + +bool FootpathPlaceFromTrackAction::IsSameAsEntranceElement(const EntranceElement& entranceElement) const +{ + if (entranceElement.HasLegacyPathEntry()) + { + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + return entranceElement.GetLegacyPathEntryIndex() == _type; + } + else + { + return false; + } + } + + if (_constructFlags & PathConstructFlag::IsLegacyPathObject) + { + return false; + } + else + { + return entranceElement.GetSurfaceEntryIndex() == _type; + } +} diff --git a/src/openrct2/actions/FootpathPlaceFromTrackAction.h b/src/openrct2/actions/FootpathPlaceFromTrackAction.h index 33016bc45f..0eb0726dd7 100644 --- a/src/openrct2/actions/FootpathPlaceFromTrackAction.h +++ b/src/openrct2/actions/FootpathPlaceFromTrackAction.h @@ -17,11 +17,15 @@ private: CoordsXYZ _loc; uint8_t _slope{}; ObjectEntryIndex _type{}; + ObjectEntryIndex _railingsType{}; uint8_t _edges{}; + PathConstructFlags _constructFlags{}; public: FootpathPlaceFromTrackAction() = default; - FootpathPlaceFromTrackAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, uint8_t edges); + FootpathPlaceFromTrackAction( + const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, ObjectEntryIndex railingsType, uint8_t edges, + PathConstructFlags constructFlags = 0); uint16_t GetActionFlags() const override; @@ -32,4 +36,5 @@ public: private: GameActions::Result::Ptr ElementInsertQuery(GameActions::Result::Ptr res) const; GameActions::Result::Ptr ElementInsertExecute(GameActions::Result::Ptr res) const; + bool IsSameAsEntranceElement(const EntranceElement& entranceElement) const; }; diff --git a/src/openrct2/actions/PlaceParkEntranceAction.cpp b/src/openrct2/actions/PlaceParkEntranceAction.cpp index 72c9d102b8..5ef8207e34 100644 --- a/src/openrct2/actions/PlaceParkEntranceAction.cpp +++ b/src/openrct2/actions/PlaceParkEntranceAction.cpp @@ -146,7 +146,14 @@ GameActions::Result::Ptr PlaceParkEntranceAction::Execute() const entranceElement->SetDirection(_loc.direction); entranceElement->SetSequenceIndex(index); entranceElement->SetEntranceType(ENTRANCE_TYPE_PARK_ENTRANCE); - entranceElement->SetLegacyPathEntryIndex(_pathType); + if (gFootpathSelection.LegacyPath == OBJECT_ENTRY_INDEX_NULL) + { + entranceElement->SetSurfaceEntryIndex(gFootpathSelection.NormalSurface); + } + else + { + entranceElement->SetLegacyPathEntryIndex(gFootpathSelection.LegacyPath); + } if (!entranceElement->IsGhost()) { diff --git a/src/openrct2/object/FootpathRailingsObject.cpp b/src/openrct2/object/FootpathRailingsObject.cpp index 70b742339f..b1045726f1 100644 --- a/src/openrct2/object/FootpathRailingsObject.cpp +++ b/src/openrct2/object/FootpathRailingsObject.cpp @@ -31,6 +31,7 @@ void FootpathRailingsObject::Load() _descriptor.Flags = Flags; _descriptor.ScrollingMode = ScrollingMode; _descriptor.SupportType = SupportType; + _descriptor.SupportColour = Colour; _descriptor.RailingsImage = RailingsImageId; } diff --git a/src/openrct2/paint/Paint.h b/src/openrct2/paint/Paint.h index 974b73f786..0117d4b2ad 100644 --- a/src/openrct2/paint/Paint.h +++ b/src/openrct2/paint/Paint.h @@ -20,6 +20,7 @@ #include struct TileElement; +enum class RailingEntrySupportType : uint8_t; enum class ViewportInteractionItem : uint8_t; struct attached_paint_struct @@ -251,6 +252,18 @@ struct paint_session : public PaintSessionCore } }; +struct FootpathPaintInfo +{ + uint32_t SurfaceImageId{}; + uint32_t BridgeImageId{}; + uint32_t RailingsImageId{}; + uint32_t SurfaceFlags{}; + uint32_t RailingFlags{}; + uint8_t ScrollingMode{}; + RailingEntrySupportType SupportType{}; + colour_t SupportColour = 255; +}; + struct RecordedPaintSession { PaintSessionCore Session; diff --git a/src/openrct2/paint/Supports.cpp b/src/openrct2/paint/Supports.cpp index ed60fe98ef..c60a18ad63 100644 --- a/src/openrct2/paint/Supports.cpp +++ b/src/openrct2/paint/Supports.cpp @@ -10,6 +10,7 @@ #include "Supports.h" #include "../interface/Viewport.h" +#include "../object/FootpathRailingsObject.h" #include "../sprites.h" #include "../world/Surface.h" #include "Paint.h" @@ -1168,7 +1169,7 @@ bool metal_b_supports_paint_setup( */ bool path_a_supports_paint_setup( paint_session* session, int32_t supportType, int32_t special, int32_t height, uint32_t imageColourFlags, - const PathRailingsDescriptor* railingsDescriptor, bool* underground) + const FootpathPaintInfo& pathPaintInfo, bool* underground) { if (underground != nullptr) { @@ -1202,7 +1203,7 @@ bool path_a_supports_paint_setup( { // save dx2 PaintAddImageAsParent( - session, (railingsDescriptor->BridgeImage + 48) | imageColourFlags, { 0, 0, baseHeight - 2 }, { 32, 32, 0 }); + session, (pathPaintInfo.BridgeImageId + 48) | imageColourFlags, { 0, 0, baseHeight - 2 }, { 32, 32, 0 }); hasSupports = true; } else if (session->Support.slope & 0x10) @@ -1216,7 +1217,7 @@ bool path_a_supports_paint_setup( } uint32_t imageId = (supportType * 24) + word_97B3C4[session->Support.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK] - + railingsDescriptor->BridgeImage; + + pathPaintInfo.BridgeImageId; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 11 }, { 0, 0, baseHeight + 2 }); @@ -1239,7 +1240,7 @@ bool path_a_supports_paint_setup( } uint32_t ebx = (supportType * 24) + word_97B3C4[session->Support.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK] - + railingsDescriptor->BridgeImage; + + pathPaintInfo.BridgeImageId; PaintAddImageAsParent(session, ebx | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, 11 }, { 0, 0, baseHeight + 2 }); @@ -1251,7 +1252,7 @@ bool path_a_supports_paint_setup( { if (baseHeight & 0x10 || heightSteps == 1 || baseHeight + 16 == session->WaterHeight) { - uint32_t imageId = (supportType * 24) + railingsDescriptor->BridgeImage + 23; + uint32_t imageId = (supportType * 24) + pathPaintInfo.BridgeImageId + 23; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, ((heightSteps == 1) ? 7 : 12) }); @@ -1261,7 +1262,7 @@ bool path_a_supports_paint_setup( } else { - uint32_t imageId = (supportType * 24) + railingsDescriptor->BridgeImage + 22; + uint32_t imageId = (supportType * 24) + pathPaintInfo.BridgeImageId + 22; PaintAddImageAsParent( session, imageId | imageColourFlags, { 0, 0, baseHeight }, { 32, 32, ((heightSteps == 2) ? 23 : 28) }); @@ -1275,7 +1276,7 @@ bool path_a_supports_paint_setup( { uint16_t specialIndex = (special - 1) & 0xFFFF; - uint32_t imageId = railingsDescriptor->BridgeImage + 55 + specialIndex; + uint32_t imageId = pathPaintInfo.BridgeImageId + 55 + specialIndex; const unk_supports_desc& supportsDesc = byte_98D8D4[specialIndex]; const unk_supports_desc_bound_box& boundBox = supportsDesc.bounding_box; @@ -1320,7 +1321,7 @@ bool path_a_supports_paint_setup( */ bool path_b_supports_paint_setup( paint_session* session, int32_t segment, int32_t special, int32_t height, uint32_t imageColourFlags, - const PathRailingsDescriptor* railingsDescriptor) + const FootpathPaintInfo& pathPaintInfo) { support_height* supportSegments = session->SupportSegments; @@ -1342,7 +1343,7 @@ bool path_b_supports_paint_setup( uint16_t baseHeight; if ((supportSegments[segment].slope & 0x20) || (height - supportSegments[segment].height < 6) - || !(railingsDescriptor->Flags & RAILING_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE)) + || !(pathPaintInfo.RailingFlags & RAILING_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE)) { baseHeight = supportSegments[segment].height; } @@ -1352,7 +1353,7 @@ bool path_b_supports_paint_setup( baseHeight = supportSegments[segment].height; PaintAddImageAsParent( - session, (railingsDescriptor->BridgeImage + 37 + imageOffset) | imageColourFlags, + session, (pathPaintInfo.BridgeImageId + 37 + imageOffset) | imageColourFlags, { SupportBoundBoxes[segment].x, SupportBoundBoxes[segment].y, baseHeight }, { 0, 0, 5 }); baseHeight += 6; } @@ -1371,7 +1372,7 @@ bool path_b_supports_paint_setup( if (heightDiff > 0) { PaintAddImageAsParent( - session, (railingsDescriptor->BridgeImage + 20 + (heightDiff - 1)) | imageColourFlags, + session, (pathPaintInfo.BridgeImageId + 20 + (heightDiff - 1)) | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, heightDiff - 1 }); } @@ -1404,7 +1405,7 @@ bool path_b_supports_paint_setup( } PaintAddImageAsParent( - session, (railingsDescriptor->BridgeImage + 20 + (z - 1)) | imageColourFlags, + session, (pathPaintInfo.BridgeImageId + 20 + (z - 1)) | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, (z - 1) }); baseHeight += z; @@ -1415,7 +1416,7 @@ bool path_b_supports_paint_setup( break; } - uint32_t imageId = railingsDescriptor->BridgeImage + 20 + (z - 1); + uint32_t imageId = pathPaintInfo.BridgeImageId + 20 + (z - 1); if (z == 16) { imageId += 1; @@ -1449,7 +1450,7 @@ bool path_b_supports_paint_setup( break; } - uint32_t imageId = railingsDescriptor->BridgeImage + 20 + (z - 1); + uint32_t imageId = pathPaintInfo.BridgeImageId + 20 + (z - 1); PaintAddImageAsParent( session, imageId | imageColourFlags, { SupportBoundBoxes[segment], baseHeight }, { 0, 0, 0 }, { SupportBoundBoxes[segment], baseHeight }); diff --git a/src/openrct2/paint/Supports.h b/src/openrct2/paint/Supports.h index 3e8ff8fb57..95c52baca3 100644 --- a/src/openrct2/paint/Supports.h +++ b/src/openrct2/paint/Supports.h @@ -12,6 +12,8 @@ #include "../common.h" #include "../world/Footpath.h" +struct FootpathPaintInfo; + constexpr const uint8_t NumVanillaWoodenSupportTypes = 49; bool wooden_a_supports_paint_setup( @@ -24,10 +26,10 @@ bool metal_b_supports_paint_setup( paint_session* session, uint8_t supportType, uint8_t segment, int32_t special, int32_t height, uint32_t imageColourFlags); bool path_a_supports_paint_setup( paint_session* session, int32_t supportType, int32_t special, int32_t height, uint32_t imageColourFlags, - const PathRailingsDescriptor* railingsDescriptor, bool* underground); + const FootpathPaintInfo& pathPaintInfo, bool* underground); bool path_b_supports_paint_setup( paint_session* session, int32_t supportType, int32_t special, int32_t height, uint32_t imageColourFlags, - const PathRailingsDescriptor* railingsDescriptor); + const FootpathPaintInfo& pathPaintInfo); // There are 13 types of metal supports. A graphic showing all of them is available here: // https://cloud.githubusercontent.com/assets/737603/19420485/7eaba28e-93ec-11e6-83cb-03190accc094.png diff --git a/src/openrct2/paint/tile_element/Paint.Entrance.cpp b/src/openrct2/paint/tile_element/Paint.Entrance.cpp index 7b0ab9c16d..bd08305a1c 100644 --- a/src/openrct2/paint/tile_element/Paint.Entrance.cpp +++ b/src/openrct2/paint/tile_element/Paint.Entrance.cpp @@ -14,6 +14,8 @@ #include "../../drawing/LightFX.h" #include "../../interface/Viewport.h" #include "../../localisation/Localisation.h" +#include "../../object/FootpathObject.h" +#include "../../object/FootpathSurfaceObject.h" #include "../../object/StationObject.h" #include "../../ride/RideData.h" #include "../../ride/TrackDesign.h" @@ -260,6 +262,7 @@ static void park_entrance_paint(paint_session* session, uint8_t direction, int32 switch (part_index) { case 0: + { if (surfaceDescriptor != nullptr) { image_id = (surfaceDescriptor->Image + 5 * (1 + (direction & 1))) | ghost_id; @@ -317,6 +320,7 @@ static void park_entrance_paint(paint_session* session, uint8_t direction, int32 PaintAddImageAsChild(session, stsetup, 0, 0, 0x1C, 0x1C, 0x2F, text_height, 2, 2, text_height); } break; + } case 1: case 2: entrance = static_cast(object_entry_get_chunk(ObjectType::ParkEntrance, 0)); diff --git a/src/openrct2/paint/tile_element/Paint.Path.cpp b/src/openrct2/paint/tile_element/Paint.Path.cpp index 33892bbe61..c11d0991e3 100644 --- a/src/openrct2/paint/tile_element/Paint.Path.cpp +++ b/src/openrct2/paint/tile_element/Paint.Path.cpp @@ -7,13 +7,18 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ +#include "../../Context.h" #include "../../Game.h" #include "../../config/Config.h" #include "../../core/Numerics.hpp" #include "../../drawing/LightFX.h" #include "../../interface/Viewport.h" #include "../../localisation/Localisation.h" +#include "../../object/FootpathObject.h" +#include "../../object/FootpathRailingsObject.h" +#include "../../object/FootpathSurfaceObject.h" #include "../../object/ObjectList.h" +#include "../../object/ObjectManager.h" #include "../../peep/Peep.h" #include "../../peep/Staff.h" #include "../../ride/Track.h" @@ -87,11 +92,11 @@ static constexpr const uint8_t byte_98D8A4[] = { // clang-format on void path_paint_box_support( - paint_session* session, const PathElement& pathElement, int32_t height, const PathSurfaceDescriptor* footpathEntry, - const PathRailingsDescriptor* railingEntry, bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags); + paint_session* session, const PathElement& pathElement, int32_t height, const FootpathPaintInfo& pathPaintInfo, + bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags); void path_paint_pole_support( - paint_session* session, const PathElement& pathElement, int16_t height, const PathSurfaceDescriptor* surfaceDescriptor, - const PathRailingsDescriptor* railingsDescriptor, bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags); + paint_session* session, const PathElement& pathElement, int16_t height, const FootpathPaintInfo& pathPaintInfo, + bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags); /* rct2: 0x006A5AE5 */ static void path_bit_lights_paint( @@ -327,9 +332,9 @@ static void path_bit_jumping_fountains_paint( */ static void sub_6A4101( paint_session* session, const PathElement& pathElement, uint16_t height, uint32_t connectedEdges, bool hasSupports, - const PathRailingsDescriptor* railingsDescriptor, uint32_t imageFlags) + const FootpathPaintInfo& pathPaintInfo, uint32_t imageFlags) { - uint32_t base_image_id = railingsDescriptor->RailingsImage | imageFlags; + uint32_t base_image_id = pathPaintInfo.RailingsImageId | imageFlags; if (pathElement.IsQueue()) { @@ -448,7 +453,7 @@ static void sub_6A4101( auto ride = get_ride(pathElement.GetRideIndex()); if (direction < 2 && ride != nullptr && imageFlags == 0) { - uint16_t scrollingMode = railingsDescriptor->ScrollingMode; + uint16_t scrollingMode = pathPaintInfo.ScrollingMode; scrollingMode += direction; auto ft = Formatter(); @@ -496,7 +501,8 @@ static void sub_6A4101( drawnCorners = (connectedEdges & FOOTPATH_PROPERTIES_EDGES_CORNERS_MASK) >> 4; } - if (pathElement.IsSloped()) + auto slopeRailingsSupported = !(pathPaintInfo.SurfaceFlags & FOOTPATH_ENTRY_FLAG_NO_SLOPE_RAILINGS); + if ((hasSupports || slopeRailingsSupported) && pathElement.IsSloped()) { switch ((pathElement.GetSlopeDirection() + session->CurrentRotation) & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK) { @@ -670,7 +676,7 @@ static void sub_6A4101( */ static void sub_6A3F61( paint_session* session, const PathElement& pathElement, uint16_t connectedEdges, uint16_t height, - const PathRailingsDescriptor* railingsDescriptor, uint32_t imageFlags, uint32_t sceneryImageFlags, bool hasSupports) + const FootpathPaintInfo& pathPaintInfo, uint32_t imageFlags, uint32_t sceneryImageFlags, bool hasSupports) { // eax -- // ebx -- @@ -750,7 +756,7 @@ static void sub_6A3F61( // Redundant zoom-level check removed if (paintScenery) - sub_6A4101(session, pathElement, height, connectedEdges, hasSupports, railingsDescriptor, imageFlags); + sub_6A4101(session, pathElement, height, connectedEdges, hasSupports, pathPaintInfo, imageFlags); } // This is about tunnel drawing @@ -800,6 +806,31 @@ static void sub_6A3F61( } } +static FootpathPaintInfo GetFootpathPaintInfo(const PathElement& pathEl) +{ + FootpathPaintInfo pathPaintInfo; + + const auto* surfaceDescriptor = pathEl.GetSurfaceDescriptor(); + if (surfaceDescriptor != nullptr) + { + pathPaintInfo.SurfaceImageId = surfaceDescriptor->Image; + pathPaintInfo.SurfaceFlags = surfaceDescriptor->Flags; + } + + const auto* railingsDescriptor = pathEl.GetRailingsDescriptor(); + if (railingsDescriptor != nullptr) + { + pathPaintInfo.BridgeImageId = railingsDescriptor->BridgeImage; + pathPaintInfo.RailingsImageId = railingsDescriptor->RailingsImage; + pathPaintInfo.RailingFlags = railingsDescriptor->Flags; + pathPaintInfo.ScrollingMode = railingsDescriptor->ScrollingMode; + pathPaintInfo.SupportType = railingsDescriptor->SupportType; + pathPaintInfo.SupportColour = railingsDescriptor->SupportColour; + } + + return pathPaintInfo; +} + /** * rct2: 0x0006A3590 */ @@ -870,7 +901,11 @@ void PaintPath(paint_session* session, uint16_t height, const PathElement& tileE } else if (surface->GetBaseZ() != height) { - hasSupports = true; + const auto* surfaceEntry = tileElement.GetSurfaceEntry(); + const bool showUndergroundRailings = surfaceEntry == nullptr + || !(surfaceEntry->Flags & FOOTPATH_ENTRY_FLAG_NO_SLOPE_RAILINGS); + if (surface->GetBaseZ() < height || showUndergroundRailings) + hasSupports = true; } else { @@ -944,23 +979,14 @@ void PaintPath(paint_session* session, uint16_t height, const PathElement& tileE PaintAddImageAsParent(session, imageId, { 16, 16, heightMarkerBaseZ }, { 1, 1, 0 }); } - const PathSurfaceDescriptor* surfaceDescriptor = tileElement.GetSurfaceDescriptor(); - const PathRailingsDescriptor* railingsDescriptor = tileElement.GetRailingsDescriptor(); - - if (surfaceDescriptor != nullptr && railingsDescriptor != nullptr) + auto pathPaintInfo = GetFootpathPaintInfo(tileElement); + if (pathPaintInfo.SupportType == RailingEntrySupportType::Pole) { - if (railingsDescriptor->SupportType == RailingEntrySupportType::Pole) - { - path_paint_pole_support( - session, tileElement, height, surfaceDescriptor, railingsDescriptor, hasSupports, imageFlags, - sceneryImageFlags); - } - else - { - path_paint_box_support( - session, tileElement, height, surfaceDescriptor, railingsDescriptor, hasSupports, imageFlags, - sceneryImageFlags); - } + path_paint_pole_support(session, tileElement, height, pathPaintInfo, hasSupports, imageFlags, sceneryImageFlags); + } + else + { + path_paint_box_support(session, tileElement, height, pathPaintInfo, hasSupports, imageFlags, sceneryImageFlags); } #ifdef __ENABLE_LIGHTFX__ @@ -996,8 +1022,8 @@ void PaintPath(paint_session* session, uint16_t height, const PathElement& tileE } void path_paint_box_support( - paint_session* session, const PathElement& pathElement, int32_t height, const PathSurfaceDescriptor* footpathEntry, - const PathRailingsDescriptor* railingEntry, bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags) + paint_session* session, const PathElement& pathElement, int32_t height, const FootpathPaintInfo& pathPaintInfo, + bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags) { // Rol edges around rotation uint8_t edges = ((pathElement.GetEdges() << session->CurrentRotation) & 0xF) @@ -1011,19 +1037,17 @@ void path_paint_box_support( uint16_t edi = edges | (corners << 4); - uint32_t imageId; + uint32_t imageId = pathPaintInfo.SurfaceImageId; if (pathElement.IsSloped()) { - imageId = ((pathElement.GetSlopeDirection() + session->CurrentRotation) & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK) + imageId += ((pathElement.GetSlopeDirection() + session->CurrentRotation) & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK) + 16; } else { - imageId = byte_98D6E0[edi]; + imageId += byte_98D6E0[edi]; } - imageId += footpathEntry->Image; - if (!session->DidPassSurface) { boundBoxOffset.x = 3; @@ -1057,18 +1081,17 @@ void path_paint_box_support( if (pathElement.IsSloped()) { image_id = ((pathElement.GetSlopeDirection() + session->CurrentRotation) & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK) - + railingEntry->BridgeImage + 51; + + pathPaintInfo.BridgeImageId + 51; } else { - image_id = byte_98D8A4[edges] + railingEntry->BridgeImage + 49; + image_id = byte_98D8A4[edges] + pathPaintInfo.BridgeImageId + 49; } PaintAddImageAsParent( session, image_id | imageFlags, { 0, 0, height }, { boundBoxSize, 0 }, { boundBoxOffset, height + boundingBoxZOffset }); - // TODO: Revert this when path import works correctly. if (!pathElement.IsQueue() && !pathElement.ShouldDrawPathOverSupports()) { // don't draw @@ -1081,7 +1104,7 @@ void path_paint_box_support( } } - sub_6A3F61(session, pathElement, edi, height, railingEntry, imageFlags, sceneryImageFlags, hasSupports); + sub_6A3F61(session, pathElement, edi, height, pathPaintInfo, imageFlags, sceneryImageFlags, hasSupports); uint16_t ax = 0; if (pathElement.IsSloped()) @@ -1089,14 +1112,8 @@ void path_paint_box_support( ax = ((pathElement.GetSlopeDirection() + session->CurrentRotation) & 0x3) + 1; } - if (byte_98D8A4[edges] == 0) - { - path_a_supports_paint_setup(session, 0, ax, height, imageFlags, railingEntry, nullptr); - } - else - { - path_a_supports_paint_setup(session, 1, ax, height, imageFlags, railingEntry, nullptr); - } + auto supportType = byte_98D8A4[edges] == 0 ? 0 : 1; + path_a_supports_paint_setup(session, supportType, ax, height, imageFlags, pathPaintInfo, nullptr); height += 32; if (pathElement.IsSloped()) @@ -1142,8 +1159,8 @@ void path_paint_box_support( } void path_paint_pole_support( - paint_session* session, const PathElement& pathElement, int16_t height, const PathSurfaceDescriptor* surfaceDescriptor, - const PathRailingsDescriptor* railingsDescriptor, bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags) + paint_session* session, const PathElement& pathElement, int16_t height, const FootpathPaintInfo& pathPaintInfo, + bool hasSupports, uint32_t imageFlags, uint32_t sceneryImageFlags) { // Rol edges around rotation uint8_t edges = ((pathElement.GetEdges() << session->CurrentRotation) & 0xF) @@ -1158,18 +1175,16 @@ void path_paint_pole_support( uint16_t edi = edges | (corners << 4); - uint32_t imageId; + uint32_t imageId = pathPaintInfo.SurfaceImageId; if (pathElement.IsSloped()) { - imageId = ((pathElement.GetSlopeDirection() + session->CurrentRotation) & 3) + 16; + imageId += ((pathElement.GetSlopeDirection() + session->CurrentRotation) & 3) + 16; } else { - imageId = byte_98D6E0[edi]; + imageId += byte_98D6E0[edi]; } - imageId += surfaceDescriptor->Image; - // Below Surface if (!session->DidPassSurface) { @@ -1205,11 +1220,11 @@ void path_paint_pole_support( { bridgeImage = ((pathElement.GetSlopeDirection() + session->CurrentRotation) & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK) - + railingsDescriptor->BridgeImage + 16; + + pathPaintInfo.BridgeImageId + 16; } else { - bridgeImage = edges + railingsDescriptor->BridgeImage; + bridgeImage = edges + pathPaintInfo.BridgeImageId; bridgeImage |= imageFlags; } @@ -1217,7 +1232,6 @@ void path_paint_pole_support( session, bridgeImage | imageFlags, { 0, 0, height }, { boundBoxSize, 0 }, { boundBoxOffset, height + boundingBoxZOffset }); - // TODO: Revert this when path import works correctly. if (pathElement.IsQueue() || pathElement.ShouldDrawPathOverSupports()) { PaintAddImageAsChild( @@ -1226,8 +1240,8 @@ void path_paint_pole_support( } } - sub_6A3F61( - session, pathElement, edi, height, railingsDescriptor, imageFlags, sceneryImageFlags, hasSupports); // TODO: arguments + sub_6A3F61(session, pathElement, edi, height, pathPaintInfo, imageFlags, sceneryImageFlags, + hasSupports); // TODO: arguments uint16_t ax = 0; if (pathElement.IsSloped()) @@ -1246,7 +1260,10 @@ void path_paint_pole_support( { if (!(edges & (1 << i))) { - path_b_supports_paint_setup(session, supports[i], ax, height, imageFlags, railingsDescriptor); + const int32_t extraFlags = (pathPaintInfo.SupportColour != COLOUR_NULL && !pathElement.IsGhost()) + ? SPRITE_ID_PALETTE_COLOUR_1(pathPaintInfo.SupportColour) + : 0; + path_b_supports_paint_setup(session, supports[i], ax, height, imageFlags | extraFlags, pathPaintInfo); } } diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 3cbc658e65..3e720a163f 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -1161,11 +1161,7 @@ static std::optional TrackDesignPlaceSceneryElement( z = (scenery.z * COORDS_Z_STEP + originZ) / COORDS_Z_STEP; if (mode == 0) { - if (scenery.flags & (1 << 7)) - { - // dh - entry_index |= (1 << 7); - } + auto isQueue = scenery.IsQueue(); uint8_t bh = ((scenery.flags & 0xF) << rotation); flags = bh >> 4; @@ -1195,8 +1191,12 @@ static std::optional TrackDesignPlaceSceneryElement( } uint8_t slope = ((bh >> 5) & 0x3) | ((bh >> 2) & 0x4); uint8_t edges = bh & 0xF; + PathConstructFlags constructFlags = PathConstructFlag::IsLegacyPathObject; + if (isQueue) + constructFlags |= PathConstructFlag::IsQueue; auto footpathPlaceAction = FootpathPlaceFromTrackAction( - { mapCoord.x, mapCoord.y, z * COORDS_Z_STEP }, slope, entry_index, edges); + { mapCoord.x, mapCoord.y, z * COORDS_Z_STEP }, slope, entry_index, OBJECT_ENTRY_INDEX_NULL, edges, + constructFlags); footpathPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&footpathPlaceAction) : GameActions::QueryNested(&footpathPlaceAction); diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index e78fa32c68..4cf7615825 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -39,6 +39,11 @@ struct TrackDesignSceneryElement uint8_t flags; // 0x13 direction quadrant tertiary colour uint8_t primary_colour; // 0x14 uint8_t secondary_colour; // 0x15 + + bool IsQueue() const + { + return (flags & (1 << 7)) != 0; + } }; /** diff --git a/src/openrct2/world/Footpath.cpp b/src/openrct2/world/Footpath.cpp index c691b7d521..c3c78decda 100644 --- a/src/openrct2/world/Footpath.cpp +++ b/src/openrct2/world/Footpath.cpp @@ -43,6 +43,7 @@ using namespace OpenRCT2::TrackMetaData; void footpath_update_queue_entrance_banner(const CoordsXY& footpathPos, TileElement* tileElement); +FootpathSelection gFootpathSelection; ProvisionalFootpath gProvisionalFootpath; uint16_t gFootpathSelectedId; CoordsXYZ gFootpathConstructFromPosition; @@ -139,21 +140,25 @@ money32 footpath_remove(const CoordsXYZ& footpathLoc, int32_t flags) * * rct2: 0x006A76FF */ -money32 footpath_provisional_set(int32_t type, const CoordsXYZ& footpathLoc, int32_t slope) +money32 footpath_provisional_set( + ObjectEntryIndex type, ObjectEntryIndex railingsType, const CoordsXYZ& footpathLoc, int32_t slope, + PathConstructFlags constructFlags) { money32 cost; footpath_provisional_remove(); - auto footpathPlaceAction = FootpathPlaceAction(footpathLoc, slope, type); + auto footpathPlaceAction = FootpathPlaceAction(footpathLoc, slope, type, railingsType, INVALID_DIRECTION, constructFlags); footpathPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); auto res = GameActions::Execute(&footpathPlaceAction); cost = res->Error == GameActions::Status::Ok ? res->Cost : MONEY32_UNDEFINED; if (res->Error == GameActions::Status::Ok) { - gProvisionalFootpath.Type = type; + gProvisionalFootpath.SurfaceIndex = type; + gProvisionalFootpath.RailingsIndex = railingsType; gProvisionalFootpath.Position = footpathLoc; gProvisionalFootpath.Slope = slope; + gProvisionalFootpath.ConstructFlags = constructFlags; gProvisionalFootpath.Flags |= PROVISIONAL_PATH_FLAG_1; if (gFootpathGroundFlags & ELEMENT_IS_UNDERGROUND) @@ -1653,13 +1658,13 @@ ObjectEntryIndex PathElement::GetLegacyPathEntryIndex() const } const FootpathObject* PathElement::GetLegacyPathEntry() const -{ + { return GetLegacyFootpathEntry(GetLegacyPathEntryIndex()); -} + } void PathElement::SetLegacyPathEntryIndex(ObjectEntryIndex newIndex) { - SurfaceIndex = newIndex & ~FOOTPATH_ELEMENT_INSERT_QUEUE; + SurfaceIndex = newIndex; RailingsIndex = OBJECT_ENTRY_INDEX_NULL; Flags2 |= FOOTPATH_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY; } @@ -1761,6 +1766,7 @@ void PathElement::SetQueueBannerDirection(uint8_t direction) bool PathElement::ShouldDrawPathOverSupports() const { + // TODO: make this an actual decision of the tile element. return (GetRailingsDescriptor()->Flags & RAILING_ENTRY_FLAG_DRAW_PATH_OVER_SUPPORTS); } @@ -2331,6 +2337,26 @@ const FootpathObject* GetLegacyFootpathEntry(ObjectEntryIndex entryIndex) return footpathObject; } +const FootpathSurfaceObject* GetPathSurfaceEntry(ObjectEntryIndex entryIndex) +{ + auto& objMgr = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = objMgr.GetLoadedObject(ObjectType::FootpathSurface, entryIndex); + if (obj == nullptr) + return nullptr; + + return static_cast(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(obj); +} + ride_id_t PathElement::GetRideIndex() const { return rideIndex; diff --git a/src/openrct2/world/Footpath.h b/src/openrct2/world/Footpath.h index 8072c7ab10..82c198df7a 100644 --- a/src/openrct2/world/Footpath.h +++ b/src/openrct2/world/Footpath.h @@ -13,6 +13,10 @@ #include "../interface/Viewport.h" #include "../object/Object.h" +class FootpathObject; +class FootpathSurfaceObject; +class FootpathRailingsObject; + enum { PROVISIONAL_PATH_FLAG_SHOW_ARROW = (1 << 0), @@ -25,8 +29,6 @@ constexpr auto FootpathMinHeight = 2 * COORDS_Z_STEP; constexpr auto PATH_HEIGHT_STEP = 2 * COORDS_Z_STEP; constexpr auto PATH_CLEARANCE = 4 * COORDS_Z_STEP; -#define FOOTPATH_ELEMENT_INSERT_QUEUE 0x80 - class FootpathObject; enum class RailingEntrySupportType : uint8_t @@ -99,16 +101,41 @@ struct PathRailingsDescriptor uint32_t BridgeImage; uint32_t RailingsImage; RailingEntrySupportType SupportType; + colour_t SupportColour; uint8_t Flags; uint8_t ScrollingMode; }; +using PathConstructFlags = uint8_t; +namespace PathConstructFlag +{ + constexpr PathConstructFlags IsQueue = 1 << 0; + constexpr PathConstructFlags IsLegacyPathObject = 1 << 1; +} // namespace PathConstructFlag + +struct FootpathSelection +{ + ObjectEntryIndex LegacyPath = OBJECT_ENTRY_INDEX_NULL; + ObjectEntryIndex NormalSurface = OBJECT_ENTRY_INDEX_NULL; + ObjectEntryIndex QueueSurface = OBJECT_ENTRY_INDEX_NULL; + ObjectEntryIndex Railings = OBJECT_ENTRY_INDEX_NULL; + bool IsQueueSelected{}; + + ObjectEntryIndex GetSelectedSurface() const + { + return IsQueueSelected ? QueueSurface : NormalSurface; + } +}; + struct ProvisionalFootpath { ObjectEntryIndex Type; CoordsXYZ Position; uint8_t Slope; uint8_t Flags; + ObjectEntryIndex SurfaceIndex; + ObjectEntryIndex RailingsIndex; + PathConstructFlags ConstructFlags; }; // Masks for values stored in TileElement.type @@ -192,6 +219,7 @@ enum FOOTPATH_CONNECTED_MAP_EDGE_IGNORE_NO_ENTRY = (1 << 7) }; +extern FootpathSelection gFootpathSelection; extern ProvisionalFootpath gProvisionalFootpath; extern uint16_t gFootpathSelectedId; extern CoordsXYZ gFootpathConstructFromPosition; @@ -206,7 +234,9 @@ extern const CoordsXY BenchUseOffsets[NumOrthogonalDirections * 2]; TileElement* map_get_footpath_element(const CoordsXYZ& coords); void footpath_interrupt_peeps(const CoordsXYZ& footpathPos); money32 footpath_remove(const CoordsXYZ& footpathLoc, int32_t flags); -money32 footpath_provisional_set(int32_t type, const CoordsXYZ& footpathLoc, int32_t slope); +money32 footpath_provisional_set( + ObjectEntryIndex type, ObjectEntryIndex railingsType, const CoordsXYZ& footpathLoc, int32_t slope, + PathConstructFlags constructFlags); void footpath_provisional_remove(); void footpath_provisional_update(); CoordsXY footpath_get_coordinates_from_pos(const ScreenCoordsXY& screenCoords, int32_t* direction, TileElement** tileElement); @@ -224,6 +254,8 @@ int32_t footpath_is_connected_to_map_edge(const CoordsXYZ& footpathPos, int32_t void footpath_remove_edges_at(const CoordsXY& footpathPos, TileElement* tileElement); const FootpathObject* GetLegacyFootpathEntry(ObjectEntryIndex entryIndex); +const FootpathSurfaceObject* GetPathSurfaceEntry(ObjectEntryIndex entryIndex); +const FootpathRailingsObject* GetPathRailingsEntry(ObjectEntryIndex entryIndex); void footpath_queue_chain_reset(); void footpath_queue_chain_push(ride_id_t rideIndex); diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index 04e5f8e4aa..3a5b9f00a2 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -1574,7 +1574,9 @@ void map_restore_provisional_elements() if (gProvisionalFootpath.Flags & PROVISIONAL_PATH_FLAG_1) { gProvisionalFootpath.Flags &= ~PROVISIONAL_PATH_FLAG_1; - footpath_provisional_set(gProvisionalFootpath.Type, gProvisionalFootpath.Position, gProvisionalFootpath.Slope); + footpath_provisional_set( + gProvisionalFootpath.SurfaceIndex, gProvisionalFootpath.RailingsIndex, gProvisionalFootpath.Position, + gProvisionalFootpath.Slope, gProvisionalFootpath.ConstructFlags); } if (window_find_by_class(WC_RIDE_CONSTRUCTION) != nullptr) { diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h index 68f289c8ce..7df39ad9d3 100644 --- a/src/openrct2/world/TileElement.h +++ b/src/openrct2/world/TileElement.h @@ -293,6 +293,9 @@ public: const PathSurfaceDescriptor* GetSurfaceDescriptor() const; const PathRailingsDescriptor* GetRailingsDescriptor() const; + bool ShouldDrawPathOverSupports() const; + void SetShouldDrawPathOverSupports(bool on); + uint8_t GetQueueBannerDirection() const; void SetQueueBannerDirection(uint8_t direction); @@ -341,9 +344,6 @@ public: uint8_t GetAdditionStatus() const; void SetAdditionStatus(uint8_t newStatus); - bool ShouldDrawPathOverSupports() const; - void SetShouldDrawPathOverSupports(bool on); - bool IsLevelCrossing(const CoordsXY& coords) const; }; assert_struct_size(PathElement, 16);