diff --git a/src/openrct2-ui/windows/TileInspector.cpp b/src/openrct2-ui/windows/TileInspector.cpp index c0dd6a4fbc..93a95289d1 100644 --- a/src/openrct2-ui/windows/TileInspector.cpp +++ b/src/openrct2-ui/windows/TileInspector.cpp @@ -179,11 +179,13 @@ static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_X_DECREASE == WIDX_SPINNER_X_DECRE static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_Y_INCREASE == WIDX_SPINNER_Y_INCREASE); static_assert(WC_TILE_INSPECTOR__WIDX_SPINNER_Y_DECREASE == WIDX_SPINNER_Y_DECREASE); -static constexpr const StringId WINDOW_TITLE = STR_TILE_INSPECTOR_TITLE; +#pragma region MEASUREMENTS +static constexpr const StringId WINDOW_TITLE = STR_TILE_INSPECTOR_TITLE; // Window sizes static constexpr const int32_t WW = 400; static constexpr const int32_t WH = 170; + constexpr int32_t MIN_WW = WW; constexpr int32_t MAX_WW = WW; constexpr int32_t MIN_WH = 130; @@ -217,6 +219,7 @@ constexpr int32_t HORIZONTAL_GROUPBOX_PADDING = 5; constexpr int32_t VERTICAL_GROUPBOX_PADDING = 4; constexpr auto PropertyButtonSize = ScreenSize{ 130, 18 }; constexpr auto PropertyFullWidth = ScreenSize{ 370, 18 }; +#pragma endregion constexpr ScreenCoordsXY PropertyRowCol(ScreenCoordsXY anchor, int32_t row, int32_t column) { @@ -436,53 +439,6 @@ static constexpr int32_t ViewportInteractionFlags = EnumsToFlags( ViewportInteractionItem::Scenery, ViewportInteractionItem::Footpath, ViewportInteractionItem::FootpathItem, ViewportInteractionItem::ParkEntrance, ViewportInteractionItem::Wall, ViewportInteractionItem::LargeScenery, ViewportInteractionItem::Banner); - -static int16_t windowTileInspectorHighlightedIndex = -1; -static bool windowTileInspectorTileSelected = false; -static int32_t windowTileInspectorToolMouseX = 0; -static int32_t windowTileInspectorToolMouseY = 0; -static bool windowTileInspectorToolCtrlDown = false; -static CoordsXY windowTileInspectorToolMap = {}; -static bool windowTileInspectorApplyToAll = false; -static bool windowTileInspectorElementCopied = false; -static TileElement tileInspectorCopiedElement; - -static void WindowTileInspectorMouseup(rct_window* w, WidgetIndex widgetIndex); -static void WindowTileInspectorResize(rct_window* w); -static void WindowTileInspectorMousedown(rct_window* w, WidgetIndex widgetIndex, rct_widget* widget); -static void WindowTileInspectorUpdate(rct_window* w); -static void WindowTileInspectorDropdown(rct_window* w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowTileInspectorToolUpdate(rct_window* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -static void WindowTileInspectorUpdateSelectedTile(rct_window* w, const ScreenCoordsXY& screenCoords); -static void WindowTileInspectorToolDown(rct_window* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -static void WindowTileInspectorToolDrag(rct_window* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords); -static void WindowTileInspectorScrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height); -static void WindowTileInspectorScrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowTileInspectorScrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowTileInspectorInvalidate(rct_window* w); -static void WindowTileInspectorPaint(rct_window* w, rct_drawpixelinfo* dpi); -static void WindowTileInspectorScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex); -static void WindowTileInspectorSetPage(rct_window* w, const TileInspectorPage page); -static void WindowTileInspectorClose(rct_window* w); - -static WindowEventList TileInspectorWindowEvents([](auto& events) { - events.mouse_up = &WindowTileInspectorMouseup; - events.resize = &WindowTileInspectorResize; - events.mouse_down = &WindowTileInspectorMousedown; - events.dropdown = &WindowTileInspectorDropdown; - events.update = &WindowTileInspectorUpdate; - events.tool_update = &WindowTileInspectorToolUpdate; - events.tool_down = &WindowTileInspectorToolDown; - events.tool_drag = &WindowTileInspectorToolDrag; - events.get_scroll_size = &WindowTileInspectorScrollgetsize; - events.scroll_mousedown = &WindowTileInspectorScrollmousedown; - events.scroll_mouseover = &WindowTileInspectorScrollmouseover; - events.invalidate = &WindowTileInspectorInvalidate; - events.paint = &WindowTileInspectorPaint; - events.scroll_paint = &WindowTileInspectorScrollpaint; - events.close = &WindowTileInspectorClose; -}); - // clang-format off static uint64_t PageHoldDownWidgets[] = { @@ -510,1900 +466,1886 @@ static uint64_t PageDisabledWidgets[] = { }; // clang-format on -rct_window* WindowTileInspectorOpen() +class TileInspector final : public Window { - rct_window* window; +private: + int16_t _highlightedIndex = -1; + bool _tileSelected = false; + int32_t _toolMouseX = 0; + int32_t _toolMouseY = 0; + bool _toolCtrlDown = false; + CoordsXY _toolMap = {}; + bool _applyToAll = false; + bool _elementCopied = false; + TileElement _copiedElement; - // Check if window is already open - window = window_bring_to_front_by_class(WindowClass::TileInspector); - if (window != nullptr) - return window; - - window = WindowCreate(ScreenCoordsXY(0, 29), WW, WH, &TileInspectorWindowEvents, WindowClass::TileInspector, WF_RESIZABLE); - - WindowTileInspectorSetPage(window, TileInspectorPage::Default); - window->min_width = MIN_WW; - window->min_height = MIN_WH; - window->max_width = MAX_WW; - window->max_height = MAX_WH; - windowTileInspectorSelectedIndex = -1; - WindowInitScrollWidgets(*window); - - windowTileInspectorTileSelected = false; - - tool_set(*window, WIDX_BACKGROUND, Tool::Crosshair); - - return window; -} - -void WindowTileInspectorClearClipboard() -{ - windowTileInspectorElementCopied = false; -} - -static TileElement* WindowTileInspectorGetSelectedElement(rct_window* w) -{ - openrct2_assert( - windowTileInspectorSelectedIndex >= 0 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount, - "Selected list item out of range"); - return map_get_first_element_at(windowTileInspectorToolMap) + windowTileInspectorSelectedIndex; -} - -static void WindowTileInspectorSelectElementFromList(rct_window* w, int32_t index) -{ - if (index < 0 || index >= windowTileInspectorElementCount) +public: + void OnOpen() override { + min_width = MIN_WW; + min_height = MIN_WH; + max_width = MAX_WW; + max_height = MAX_WH; + windowTileInspectorSelectedIndex = -1; - OpenRCT2::TileInspector::SetSelectedElement(nullptr); - } - else - { - windowTileInspectorSelectedIndex = index; + SetPage(TileInspectorPage::Default); + WindowInitScrollWidgets(*this); + _tileSelected = false; - const TileElement* const tileElement = WindowTileInspectorGetSelectedElement(w); - OpenRCT2::TileInspector::SetSelectedElement(tileElement); + tool_set(*this, WIDX_BACKGROUND, Tool::Crosshair); } - w->Invalidate(); -} - -static void WindowTileInspectorLoadTile(rct_window* w, TileElement* elementToSelect) -{ - windowTileInspectorSelectedIndex = -1; - w->scrolls[0].v_top = 0; - - TileElement* element = map_get_first_element_at(windowTileInspectorToolMap); - int16_t numItems = 0; - do + void OnUpdate() override { - if (element == nullptr) - break; - if (element == elementToSelect) + // Check if the mouse is hovering over the list + if (!WidgetIsHighlighted(*this, WIDX_LIST)) { - windowTileInspectorSelectedIndex = numItems; + _highlightedIndex = -1; + InvalidateWidget(WIDX_LIST); } + if (gCurrentToolWidget.window_classification != WindowClass::TileInspector) + Close(); + } - numItems++; - } while (!(element++)->IsLastForTile()); - - windowTileInspectorElementCount = numItems; - - w->Invalidate(); -} - -static void WindowTileInspectorRemoveElement(int32_t elementIndex) -{ - openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyRemove, elementIndex); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorRotateElement(int32_t elementIndex) -{ - openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyRotate, elementIndex); - GameActions::Execute(&modifyTile); -} - -// Swap element with its parent -static void WindowTileInspectorSwapElements(int16_t first, int16_t second) -{ - bool firstInRange = first >= 0 && first < windowTileInspectorElementCount; - bool secondInRange = second >= 0 && second < windowTileInspectorElementCount; - // This might happen if two people are modifying the same tile. - if (!firstInRange || !secondInRange) - return; - - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnySwap, first, second); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorSortElements() -{ - openrct2_assert(windowTileInspectorTileSelected, "No tile selected"); - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnySort); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorCopyElement(rct_window* w) -{ - // Copy value, in case the element gets moved - tileInspectorCopiedElement = *WindowTileInspectorGetSelectedElement(w); - windowTileInspectorElementCopied = true; - w->Invalidate(); -} - -static void WindowTileInspectorPasteElement(rct_window* w) -{ - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyPaste, 0, 0, tileInspectorCopiedElement); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorBaseHeightOffset(int16_t elementIndex, int8_t heightOffset) -{ - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::AnyBaseHeightOffset, elementIndex, heightOffset); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorSurfaceShowParkFences(bool showFences) -{ - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceShowParkFences, showFences); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorSurfaceToggleCorner(int32_t cornerIndex) -{ - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceToggleCorner, cornerIndex); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorSurfaceToggleDiagonal() -{ - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::SurfaceToggleDiagonal); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorPathSetSloped(int32_t elementIndex, bool sloped) -{ - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathSetSlope, elementIndex, sloped); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorPathSetBroken(int32_t elementIndex, bool broken) -{ - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathSetBroken, elementIndex, broken); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorPathToggleEdge(int32_t elementIndex, int32_t cornerIndex) -{ - openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - openrct2_assert(cornerIndex >= 0 && cornerIndex < 8, "cornerIndex out of range"); - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::PathToggleEdge, elementIndex, cornerIndex); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorEntranceMakeUsable(int32_t elementIndex) -{ - Guard::ArgumentInRange(elementIndex, 0, windowTileInspectorElementCount - 1); - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::EntranceMakeUsable, elementIndex); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorWallSetSlope(int32_t elementIndex, int32_t slopeValue) -{ - // Make sure only the correct bits are set - openrct2_assert((slopeValue & 3) == slopeValue, "slopeValue doesn't match its mask"); - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::WallSetSlope, elementIndex, slopeValue); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorWallAnimationFrameOffset(int16_t elementIndex, int8_t animationFrameOffset) -{ - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::WallSetAnimationFrame, elementIndex, animationFrameOffset); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorTrackBlockHeightOffset(int32_t elementIndex, int8_t heightOffset) -{ - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::TrackBaseHeightOffset, elementIndex, heightOffset); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorTrackBlockSetLift(int32_t elementIndex, bool entireTrackBlock, bool chain) -{ - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, entireTrackBlock ? TileModifyType::TrackSetChainBlock : TileModifyType::TrackSetChain, - elementIndex, chain); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorTrackSetBlockBrake(int32_t elementIndex, bool blockBrake) -{ - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::TrackSetBlockBrake, elementIndex, blockBrake); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorTrackSetIndestructible(int32_t elementIndex, bool isIndestructible) -{ - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::TrackSetIndestructible, elementIndex, isIndestructible); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorQuarterTileSet(int32_t elementIndex, const int32_t quarterIndex) -{ - // quarterIndex is widget index relative to WIDX_SCENERY_CHECK_QUARTER_N, so a value from 0-3 - openrct2_assert(quarterIndex >= 0 && quarterIndex < 4, "quarterIndex out of range"); - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::ScenerySetQuarterLocation, elementIndex, - (quarterIndex - get_current_rotation()) & 3); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorToggleQuadrantCollosion(int32_t elementIndex, const int32_t quadrantIndex) -{ - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::ScenerySetQuarterCollision, elementIndex, - (quadrantIndex + 2 - get_current_rotation()) & 3); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorBannerToggleBlock(int32_t elementIndex, int32_t edgeIndex) -{ - openrct2_assert(edgeIndex >= 0 && edgeIndex < 4, "edgeIndex out of range"); - - // Make edgeIndex abstract - edgeIndex = (edgeIndex - get_current_rotation()) & 3; - auto modifyTile = TileModifyAction( - windowTileInspectorToolMap, TileModifyType::BannerToggleBlockingEdge, elementIndex, edgeIndex); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorToggleInvisibility(int32_t elementIndex) -{ - openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); - auto modifyTile = TileModifyAction(windowTileInspectorToolMap, TileModifyType::AnyToggleInvisilibity, elementIndex); - GameActions::Execute(&modifyTile); -} - -static void WindowTileInspectorClose(rct_window* w) -{ - OpenRCT2::TileInspector::SetSelectedElement(nullptr); -} - -static void WindowTileInspectorMouseup(rct_window* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) + void OnMouseUp(WidgetIndex widgetIndex) override { - case WIDX_CLOSE: - tool_cancel(); - window_close(*w); - return; - case WIDX_BUTTON_REMOVE: + switch (widgetIndex) { - int32_t nextItemToSelect = windowTileInspectorSelectedIndex - 1; - WindowTileInspectorRemoveElement(windowTileInspectorSelectedIndex); - WindowTileInspectorSelectElementFromList(w, nextItemToSelect); - break; - } - case WIDX_BUTTON_ROTATE: - WindowTileInspectorRotateElement(windowTileInspectorSelectedIndex); - break; - case WIDX_BUTTON_SORT: - WindowTileInspectorSortElements(); - break; - case WIDX_BUTTON_COPY: - WindowTileInspectorCopyElement(w); - break; - case WIDX_BUTTON_PASTE: - WindowTileInspectorPasteElement(w); - break; - case WIDX_BUTTON_MOVE_UP: - WindowTileInspectorSwapElements(windowTileInspectorSelectedIndex, windowTileInspectorSelectedIndex + 1); - break; - case WIDX_BUTTON_MOVE_DOWN: - WindowTileInspectorSwapElements(windowTileInspectorSelectedIndex - 1, windowTileInspectorSelectedIndex); - break; - } + case WIDX_CLOSE: + tool_cancel(); + Close(); + return; - // Only element-specific widgets from now on - if (w->tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) - { - return; - } - - TileElement* const tileElement = WindowTileInspectorGetSelectedElement(w); - - // Update selection, can be nullptr. - OpenRCT2::TileInspector::SetSelectedElement(tileElement); - - if (tileElement == nullptr) - return; - - // Page widgets - switch (tileElement->GetType()) - { - case TileElementType::Surface: - switch (widgetIndex) + case WIDX_BUTTON_REMOVE: { - case WIDX_SURFACE_BUTTON_REMOVE_FENCES: - WindowTileInspectorSurfaceShowParkFences(false); - break; - case WIDX_SURFACE_BUTTON_RESTORE_FENCES: - WindowTileInspectorSurfaceShowParkFences(true); - break; - case WIDX_SURFACE_CHECK_CORNER_N: - case WIDX_SURFACE_CHECK_CORNER_E: - case WIDX_SURFACE_CHECK_CORNER_S: - case WIDX_SURFACE_CHECK_CORNER_W: - WindowTileInspectorSurfaceToggleCorner( - ((widgetIndex - WIDX_SURFACE_CHECK_CORNER_N) + 2 - get_current_rotation()) & 3); - break; - case WIDX_SURFACE_CHECK_DIAGONAL: - WindowTileInspectorSurfaceToggleDiagonal(); - break; - } // switch widgetindex - break; - - case TileElementType::Path: - switch (widgetIndex) - { - case WIDX_PATH_CHECK_SLOPED: - WindowTileInspectorPathSetSloped(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsSloped()); - break; - case WIDX_PATH_CHECK_BROKEN: - WindowTileInspectorPathSetBroken(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsBroken()); - break; - case WIDX_PATH_CHECK_EDGE_E: - case WIDX_PATH_CHECK_EDGE_S: - case WIDX_PATH_CHECK_EDGE_W: - case WIDX_PATH_CHECK_EDGE_N: - { - // 0 = east/right, 1 = south/bottom, 2 = west/left, 3 = north/top - const int32_t eswn = (widgetIndex - WIDX_PATH_CHECK_EDGE_E) / 2; - // Transform to world orientation - const int32_t index = (eswn - get_current_rotation()) & 3; - WindowTileInspectorPathToggleEdge( - windowTileInspectorSelectedIndex, - index + 4); // The corners are stored in the 4 most significant bits, hence the + 4 - break; - } - case WIDX_PATH_CHECK_EDGE_NE: - case WIDX_PATH_CHECK_EDGE_SE: - case WIDX_PATH_CHECK_EDGE_SW: - case WIDX_PATH_CHECK_EDGE_NW: - { - // 0 = NE, 1 = SE, 2 = SW, 3 = NW - const int32_t neseswnw = (widgetIndex - WIDX_PATH_CHECK_EDGE_NE) / 2; - // Transform to world orientation - const int32_t index = (neseswnw - get_current_rotation()) & 3; - WindowTileInspectorPathToggleEdge(windowTileInspectorSelectedIndex, index); - break; - } - } // switch widget index - break; - - case TileElementType::Track: - switch (widgetIndex) - { - case WIDX_TRACK_CHECK_APPLY_TO_ALL: - windowTileInspectorApplyToAll ^= 1; - widget_invalidate(*w, widgetIndex); - break; - case WIDX_TRACK_CHECK_CHAIN_LIFT: - { - bool entireTrackBlock = WidgetIsPressed(*w, WIDX_TRACK_CHECK_APPLY_TO_ALL); - bool newLift = !tileElement->AsTrack()->HasChain(); - WindowTileInspectorTrackBlockSetLift(windowTileInspectorSelectedIndex, entireTrackBlock, newLift); - break; - } - case WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED: - WindowTileInspectorTrackSetBlockBrake( - windowTileInspectorSelectedIndex, !tileElement->AsTrack()->BlockBrakeClosed()); - break; - case WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE: - WindowTileInspectorTrackSetIndestructible( - windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsIndestructible()); - break; - } // switch widget index - break; - - case TileElementType::SmallScenery: - switch (widgetIndex) - { - case WIDX_SCENERY_CHECK_QUARTER_N: - case WIDX_SCENERY_CHECK_QUARTER_E: - case WIDX_SCENERY_CHECK_QUARTER_S: - case WIDX_SCENERY_CHECK_QUARTER_W: - WindowTileInspectorQuarterTileSet( - windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_QUARTER_N); - break; - case WIDX_SCENERY_CHECK_COLLISION_N: - case WIDX_SCENERY_CHECK_COLLISION_E: - case WIDX_SCENERY_CHECK_COLLISION_S: - case WIDX_SCENERY_CHECK_COLLISION_W: - WindowTileInspectorToggleQuadrantCollosion( - windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_COLLISION_N); - break; - } // switch widget index - break; - - case TileElementType::Entrance: - switch (widgetIndex) - { - case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: - WindowTileInspectorEntranceMakeUsable(windowTileInspectorSelectedIndex); - break; - } // switch widget index - break; - - case TileElementType::Banner: - switch (widgetIndex) - { - case WIDX_BANNER_CHECK_BLOCK_NE: - case WIDX_BANNER_CHECK_BLOCK_SE: - case WIDX_BANNER_CHECK_BLOCK_SW: - case WIDX_BANNER_CHECK_BLOCK_NW: - WindowTileInspectorBannerToggleBlock( - windowTileInspectorSelectedIndex, widgetIndex - WIDX_BANNER_CHECK_BLOCK_NE); - break; - } // switch widget index - break; - - case TileElementType::LargeScenery: - case TileElementType::Wall: - default: - break; - } -} - -static void WindowTileInspectorResize(rct_window* w) -{ - if (w->width < w->min_width) - { - w->Invalidate(); - w->width = w->min_width; - } - if (w->height < w->min_height) - { - w->Invalidate(); - w->height = w->min_height; - } -} - -static void WindowTileInspectorMousedown(rct_window* w, WidgetIndex widgetIndex, rct_widget* widget) -{ - switch (widgetIndex) - { - case WIDX_SPINNER_X_INCREASE: - windowTileInspectorTile.x = std::min(windowTileInspectorTile.x + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); - windowTileInspectorToolMap.x = std::min(windowTileInspectorToolMap.x + 32, MAXIMUM_TILE_START_XY); - WindowTileInspectorLoadTile(w, nullptr); - break; - case WIDX_SPINNER_X_DECREASE: - windowTileInspectorTile.x = std::max(windowTileInspectorTile.x - 1, 0); - windowTileInspectorToolMap.x = std::max(windowTileInspectorToolMap.x - 32, 0); - WindowTileInspectorLoadTile(w, nullptr); - break; - case WIDX_SPINNER_Y_INCREASE: - windowTileInspectorTile.y = std::min(windowTileInspectorTile.y + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); - windowTileInspectorToolMap.y = std::min(windowTileInspectorToolMap.y + 32, MAXIMUM_TILE_START_XY); - WindowTileInspectorLoadTile(w, nullptr); - break; - case WIDX_SPINNER_Y_DECREASE: - windowTileInspectorTile.y = std::max(windowTileInspectorTile.y - 1, 0); - windowTileInspectorToolMap.y = std::max(windowTileInspectorToolMap.y - 32, 0); - WindowTileInspectorLoadTile(w, nullptr); - break; - } // switch widget index - - // Only element-specific widgets from now on - if (w->tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) - { - return; - } - - const TileElement* tileElement = WindowTileInspectorGetSelectedElement(w); - if (tileElement == nullptr) - return; - - switch (tileElement->GetType()) - { - case TileElementType::Surface: - switch (widgetIndex) - { - case WIDX_SURFACE_SPINNER_HEIGHT_INCREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_SURFACE_SPINNER_HEIGHT_DECREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Path: - switch (widgetIndex) - { - case WIDX_PATH_SPINNER_HEIGHT_INCREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_PATH_SPINNER_HEIGHT_DECREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Track: - switch (widgetIndex) - { - case WIDX_TRACK_SPINNER_HEIGHT_INCREASE: - if (WidgetIsPressed(*w, WIDX_TRACK_CHECK_APPLY_TO_ALL)) - { - WindowTileInspectorTrackBlockHeightOffset(windowTileInspectorSelectedIndex, 1); - } - else - { - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - } - break; - case WIDX_TRACK_SPINNER_HEIGHT_DECREASE: - if (WidgetIsPressed(*w, WIDX_TRACK_CHECK_APPLY_TO_ALL)) - { - WindowTileInspectorTrackBlockHeightOffset(windowTileInspectorSelectedIndex, -1); - } - else - { - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - } - break; - } // switch widget index - break; - - case TileElementType::SmallScenery: - switch (widgetIndex) - { - case WIDX_SCENERY_SPINNER_HEIGHT_INCREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_SCENERY_SPINNER_HEIGHT_DECREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Entrance: - switch (widgetIndex) - { - case WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: - WindowTileInspectorEntranceMakeUsable(windowTileInspectorSelectedIndex); - break; - } // switch widget index - break; - - case TileElementType::Wall: - switch (widgetIndex) - { - case WIDX_WALL_SPINNER_HEIGHT_INCREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_WALL_SPINNER_HEIGHT_DECREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - case WIDX_WALL_DROPDOWN_SLOPE_BUTTON: - { - // Use dropdown instead of dropdown button - widget--; - - // Fill dropdown list - gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; - gDropdownItems[0].Args = STR_TILE_INSPECTOR_WALL_FLAT; - gDropdownItems[1].Args = STR_TILE_INSPECTOR_WALL_SLOPED_LEFT; - gDropdownItems[2].Args = STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT; - WindowDropdownShowTextCustomWidth( - { w->windowPos.x + widget->left, w->windowPos.y + widget->top }, widget->height() + 1, w->colours[1], 0, - Dropdown::Flag::StayOpen, 3, widget->width() - 3); - - // Set current value as checked - Dropdown::SetChecked(tileElement->AsWall()->GetSlope(), true); - break; - } - case WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE: - WindowTileInspectorWallAnimationFrameOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE: - WindowTileInspectorWallAnimationFrameOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::LargeScenery: - switch (widgetIndex) - { - case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - case TileElementType::Banner: - switch (widgetIndex) - { - case WIDX_BANNER_SPINNER_HEIGHT_INCREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, 1); - break; - case WIDX_BANNER_SPINNER_HEIGHT_DECREASE: - WindowTileInspectorBaseHeightOffset(windowTileInspectorSelectedIndex, -1); - break; - } // switch widget index - break; - - default: - break; - } -} - -static void WindowTileInspectorUpdate(rct_window* w) -{ - // Check if the mouse is hovering over the list - if (!WidgetIsHighlighted(*w, WIDX_LIST)) - { - windowTileInspectorHighlightedIndex = -1; - widget_invalidate(*w, WIDX_LIST); - } - - if (gCurrentToolWidget.window_classification != WindowClass::TileInspector) - window_close(*w); -} - -static void WindowTileInspectorDropdown(rct_window* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (dropdownIndex == -1) - { - return; - } - - // Get selected element - TileElement* const tileElement = WindowTileInspectorGetSelectedElement(w); - - if (w->tileInspectorPage == TileInspectorPage::Wall) - { - openrct2_assert(tileElement->GetType() == TileElementType::Wall, "Element is not a wall"); - - if (widgetIndex == WIDX_WALL_DROPDOWN_SLOPE_BUTTON) - { - WindowTileInspectorWallSetSlope(windowTileInspectorSelectedIndex, dropdownIndex); - } - } -} - -static void WindowTileInspectorToolUpdate(rct_window* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - map_invalidate_selection_rect(); - - gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; - - CoordsXY mapCoords; - TileElement* clickedElement = nullptr; - bool mouseOnViewport = false; - if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z)) - { - auto info = get_map_coordinates_from_pos(screenCoords, ViewportInteractionFlags); - clickedElement = info.Element; - mapCoords = info.Loc; - } - - // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor - if (clickedElement == nullptr) - { - auto mouseCoords = screen_pos_to_map_pos(screenCoords, nullptr); - if (mouseCoords.has_value()) - { - mouseOnViewport = true; - mapCoords = mouseCoords.value(); - } - } - - if (mouseOnViewport) - { - gMapSelectPositionA = gMapSelectPositionB = mapCoords; - } - else if (windowTileInspectorTileSelected) - { - gMapSelectPositionA = gMapSelectPositionB = windowTileInspectorToolMap; - } - else - { - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - } - - gMapSelectType = MAP_SELECT_TYPE_FULL; - map_invalidate_selection_rect(); -} - -static void WindowTileInspectorUpdateSelectedTile(rct_window* w, const ScreenCoordsXY& screenCoords) -{ - const bool ctrlIsHeldDown = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z); - - // Mouse hasn't moved - if (screenCoords.x == windowTileInspectorToolMouseX && screenCoords.y == windowTileInspectorToolMouseY - && windowTileInspectorToolCtrlDown == ctrlIsHeldDown) - { - return; - } - - windowTileInspectorToolMouseX = screenCoords.x; - windowTileInspectorToolMouseY = screenCoords.y; - windowTileInspectorToolCtrlDown = ctrlIsHeldDown; - - CoordsXY mapCoords{}; - TileElement* clickedElement = nullptr; - if (ctrlIsHeldDown) - { - auto info = get_map_coordinates_from_pos(screenCoords, ViewportInteractionFlags); - clickedElement = info.Element; - mapCoords = info.Loc; - } - - // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor - if (clickedElement == nullptr) - { - auto mouseCoords = screen_pos_to_map_pos(screenCoords, nullptr); - - if (!mouseCoords.has_value()) - { - return; - } - - mapCoords = mouseCoords.value(); - // Tile is already selected - if (windowTileInspectorTileSelected && mapCoords.x == windowTileInspectorToolMap.x - && mapCoords.y == windowTileInspectorToolMap.y) - { - return; - } - } - - windowTileInspectorTileSelected = true; - windowTileInspectorToolMap = mapCoords; - windowTileInspectorTile = TileCoordsXY(mapCoords); - - OpenRCT2::TileInspector::SetSelectedElement(clickedElement); - - WindowTileInspectorLoadTile(w, clickedElement); -} - -static void WindowTileInspectorToolDown(rct_window* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - WindowTileInspectorUpdateSelectedTile(w, screenCoords); -} - -static void WindowTileInspectorToolDrag(rct_window* w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) -{ - WindowTileInspectorUpdateSelectedTile(w, screenCoords); -} - -static void WindowTileInspectorScrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) -{ - *width = WW - 30; - *height = w->widgets[WIDX_LIST].height() - 2; - *height = std::max(windowTileInspectorElementCount * SCROLLABLE_ROW_HEIGHT, *height); -} - -static void WindowTileInspectorSetPage(rct_window* w, const TileInspectorPage page) -{ - // Invalidate the window already, because the size may change - w->Invalidate(); - - // subtract current page height, then add new page height - if (w->tileInspectorPage != TileInspectorPage::Default) - { - auto index = EnumValue(w->tileInspectorPage) - 1; - w->height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - w->min_height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - } - if (page != TileInspectorPage::Default) - { - auto index = EnumValue(page) - 1; - w->height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - w->min_height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; - } - w->tileInspectorPage = page; - auto pageIndex = EnumValue(page); - w->widgets = PageWidgets[pageIndex]; - w->hold_down_widgets = PageHoldDownWidgets[pageIndex]; - w->disabled_widgets = PageDisabledWidgets[pageIndex]; - w->pressed_widgets = 0; -} - -static void WindowTileInspectorScrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - // There is nothing to interact with when no tile is selected - if (!windowTileInspectorTileSelected) - return; - - // Because the list items are displayed in reverse order, subtract the calculated index from the amount of elements - const int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; - const ScreenRect checkboxColumnRect{ { 2, 0 }, { 15, screenCoords.y } }; - if (index >= 0 && checkboxColumnRect.Contains(screenCoords)) - { // Checkbox was clicked - WindowTileInspectorToggleInvisibility(index); - } - else - { - WindowTileInspectorSelectElementFromList(w, index); - } -} - -static void WindowTileInspectorScrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; - if (index < 0 || index >= windowTileInspectorElementCount) - windowTileInspectorHighlightedIndex = -1; - else - windowTileInspectorHighlightedIndex = index; - - widget_invalidate(*w, WIDX_LIST); -} - -static void WindowTileInspectorInvalidate(rct_window* w) -{ - // Set the correct page automatically - TileInspectorPage page = TileInspectorPage::Default; - if (windowTileInspectorSelectedIndex != -1) - { - const auto element = WindowTileInspectorGetSelectedElement(w); - switch (element->GetType()) - { - default: - case TileElementType::Surface: - page = TileInspectorPage::Surface; + int32_t nextItemToSelect = windowTileInspectorSelectedIndex - 1; + RemoveElement(windowTileInspectorSelectedIndex); + SelectElementFromList(nextItemToSelect); break; - case TileElementType::Path: - page = TileInspectorPage::Path; - break; - case TileElementType::Track: - page = TileInspectorPage::Track; - break; - case TileElementType::SmallScenery: - page = TileInspectorPage::Scenery; - break; - case TileElementType::Entrance: - page = TileInspectorPage::Entrance; - break; - case TileElementType::Wall: - page = TileInspectorPage::Wall; - break; - case TileElementType::LargeScenery: - page = TileInspectorPage::LargeScenery; - break; - case TileElementType::Banner: - page = TileInspectorPage::Banner; - break; - } - } - - if (w->tileInspectorPage != page) - { - WindowTileInspectorSetPage(w, page); - w->Invalidate(); - } - - // X and Y spinners - WidgetSetEnabled( - *w, WIDX_SPINNER_X_INCREASE, - (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.x / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); - WidgetSetEnabled( - *w, WIDX_SPINNER_X_DECREASE, (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.x / 32) > 0))); - WidgetSetEnabled( - *w, WIDX_SPINNER_Y_INCREASE, - (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.y / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); - WidgetSetEnabled( - *w, WIDX_SPINNER_Y_DECREASE, (windowTileInspectorTileSelected && ((windowTileInspectorToolMap.y / 32) > 0))); - - // Sort buttons - WidgetSetEnabled(*w, WIDX_BUTTON_SORT, (windowTileInspectorTileSelected && windowTileInspectorElementCount > 1)); - - // Move Up button - WidgetSetEnabled( - *w, WIDX_BUTTON_MOVE_UP, - (windowTileInspectorSelectedIndex != -1 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount - 1)); - widget_invalidate(*w, WIDX_BUTTON_MOVE_UP); - - // Move Down button - WidgetSetEnabled(*w, WIDX_BUTTON_MOVE_DOWN, (windowTileInspectorSelectedIndex > 0)); - widget_invalidate(*w, WIDX_BUTTON_MOVE_DOWN); - - // Copy button - WidgetSetEnabled(*w, WIDX_BUTTON_COPY, windowTileInspectorSelectedIndex >= 0); - widget_invalidate(*w, WIDX_BUTTON_COPY); - - // Paste button - WidgetSetEnabled(*w, WIDX_BUTTON_PASTE, windowTileInspectorTileSelected && windowTileInspectorElementCopied); - widget_invalidate(*w, WIDX_BUTTON_PASTE); - - w->widgets[WIDX_BACKGROUND].bottom = w->height - 1; - - if (w->tileInspectorPage == TileInspectorPage::Default) - { - w->widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Empty; - w->widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Empty; - w->widgets[WIDX_LIST].bottom = w->height - PADDING_BOTTOM; - } - else - { - w->widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Groupbox; - w->widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Groupbox; - auto pageIndex = EnumValue(w->tileInspectorPage) - 1; - w->widgets[WIDX_GROUPBOX_DETAILS].text = PageGroupBoxSettings[pageIndex].string_id; - w->widgets[WIDX_GROUPBOX_DETAILS].top = w->height - PageGroupBoxSettings[pageIndex].details_top_offset; - w->widgets[WIDX_GROUPBOX_DETAILS].bottom = w->height - PageGroupBoxSettings[pageIndex].details_bottom_offset; - w->widgets[WIDX_GROUPBOX_PROPERTIES].top = w->height - PageGroupBoxSettings[pageIndex].properties_top_offset; - w->widgets[WIDX_GROUPBOX_PROPERTIES].bottom = w->height - PageGroupBoxSettings[pageIndex].properties_bottom_offset; - w->widgets[WIDX_LIST].bottom = w->widgets[WIDX_GROUPBOX_DETAILS].top - GROUPBOX_PADDING; - } - - // The default page doesn't need further invalidation - if (w->tileInspectorPage == TileInspectorPage::Default) - { - return; - } - - // Using a switch, because I don't think giving each page their own callbacks is - // needed here, as only the mouseup and invalidate functions are different. - const int32_t propertiesAnchor = w->widgets[WIDX_GROUPBOX_PROPERTIES].top; - const TileElement* const tileElement = WindowTileInspectorGetSelectedElement(w); - if (tileElement == nullptr) - return; - - switch (tileElement->GetType()) - { - case TileElementType::Surface: - w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].top = GBBT(propertiesAnchor, 1); - w->widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].bottom = GBBB(propertiesAnchor, 1); - w->widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].top = GBBT(propertiesAnchor, 1); - w->widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].bottom = GBBB(propertiesAnchor, 1); - w->widgets[WIDX_SURFACE_CHECK_CORNER_N].top = GBBT(propertiesAnchor, 2) + 7 * 0; - w->widgets[WIDX_SURFACE_CHECK_CORNER_N].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_N].top + 13; - w->widgets[WIDX_SURFACE_CHECK_CORNER_E].top = GBBT(propertiesAnchor, 2) + 7 * 1; - w->widgets[WIDX_SURFACE_CHECK_CORNER_E].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_E].top + 13; - w->widgets[WIDX_SURFACE_CHECK_CORNER_S].top = GBBT(propertiesAnchor, 2) + 7 * 2; - w->widgets[WIDX_SURFACE_CHECK_CORNER_S].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_S].top + 13; - w->widgets[WIDX_SURFACE_CHECK_CORNER_W].top = GBBT(propertiesAnchor, 2) + 7 * 1; - w->widgets[WIDX_SURFACE_CHECK_CORNER_W].bottom = w->widgets[WIDX_SURFACE_CHECK_CORNER_W].top + 13; - w->widgets[WIDX_SURFACE_CHECK_DIAGONAL].top = GBBT(propertiesAnchor, 3) + 7 * 1; - w->widgets[WIDX_SURFACE_CHECK_DIAGONAL].bottom = w->widgets[WIDX_SURFACE_CHECK_DIAGONAL].top + 13; - WidgetSetCheckboxValue( - *w, WIDX_SURFACE_CHECK_CORNER_N, - tileElement->AsSurface()->GetSlope() & (1 << ((2 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_SURFACE_CHECK_CORNER_E, - tileElement->AsSurface()->GetSlope() & (1 << ((3 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_SURFACE_CHECK_CORNER_S, - tileElement->AsSurface()->GetSlope() & (1 << ((0 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_SURFACE_CHECK_CORNER_W, - tileElement->AsSurface()->GetSlope() & (1 << ((1 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_SURFACE_CHECK_DIAGONAL, tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT); - break; - case TileElementType::Path: - w->widgets[WIDX_PATH_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - w->widgets[WIDX_PATH_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - w->widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_PATH_CHECK_BROKEN].top = GBBT(propertiesAnchor, 1); - w->widgets[WIDX_PATH_CHECK_BROKEN].bottom = GBBB(propertiesAnchor, 1); - w->widgets[WIDX_PATH_CHECK_SLOPED].top = GBBT(propertiesAnchor, 2); - w->widgets[WIDX_PATH_CHECK_SLOPED].bottom = GBBB(propertiesAnchor, 2); - w->widgets[WIDX_PATH_CHECK_EDGE_N].top = GBBT(propertiesAnchor, 3) + 7 * 0; - w->widgets[WIDX_PATH_CHECK_EDGE_N].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_N].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_NE].top = GBBT(propertiesAnchor, 3) + 7 * 1; - w->widgets[WIDX_PATH_CHECK_EDGE_NE].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_NE].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_E].top = GBBT(propertiesAnchor, 3) + 7 * 2; - w->widgets[WIDX_PATH_CHECK_EDGE_E].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_E].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_SE].top = GBBT(propertiesAnchor, 3) + 7 * 3; - w->widgets[WIDX_PATH_CHECK_EDGE_SE].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_SE].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_S].top = GBBT(propertiesAnchor, 3) + 7 * 4; - w->widgets[WIDX_PATH_CHECK_EDGE_S].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_S].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_SW].top = GBBT(propertiesAnchor, 3) + 7 * 3; - w->widgets[WIDX_PATH_CHECK_EDGE_SW].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_SW].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_W].top = GBBT(propertiesAnchor, 3) + 7 * 2; - w->widgets[WIDX_PATH_CHECK_EDGE_W].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_W].top + 13; - w->widgets[WIDX_PATH_CHECK_EDGE_NW].top = GBBT(propertiesAnchor, 3) + 7 * 1; - w->widgets[WIDX_PATH_CHECK_EDGE_NW].bottom = w->widgets[WIDX_PATH_CHECK_EDGE_NW].top + 13; - WidgetSetCheckboxValue(*w, WIDX_PATH_CHECK_SLOPED, tileElement->AsPath()->IsSloped()); - WidgetSetCheckboxValue(*w, WIDX_PATH_CHECK_BROKEN, tileElement->AsPath()->IsBroken()); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_NE, tileElement->AsPath()->GetEdges() & (1 << ((0 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_SE, tileElement->AsPath()->GetEdges() & (1 << ((1 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_SW, tileElement->AsPath()->GetEdges() & (1 << ((2 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_NW, tileElement->AsPath()->GetEdges() & (1 << ((3 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_E, tileElement->AsPath()->GetCorners() & (1 << ((0 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_S, tileElement->AsPath()->GetCorners() & (1 << ((1 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_W, tileElement->AsPath()->GetCorners() & (1 << ((2 - get_current_rotation()) & 3))); - WidgetSetCheckboxValue( - *w, WIDX_PATH_CHECK_EDGE_N, tileElement->AsPath()->GetCorners() & (1 << ((3 - get_current_rotation()) & 3))); - break; - case TileElementType::Track: - w->widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].top = GBBT(propertiesAnchor, 0); - w->widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].bottom = GBBB(propertiesAnchor, 0); - w->widgets[WIDX_TRACK_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 1) + 3; - w->widgets[WIDX_TRACK_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 1) - 3; - w->widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 1) + 4; - w->widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 1) - 4; - w->widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 1) + 4; - w->widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 1) - 4; - w->widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].top = GBBT(propertiesAnchor, 2); - w->widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].bottom = GBBB(propertiesAnchor, 2); - w->widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].top = GBBT(propertiesAnchor, 3); - w->widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].bottom = GBBB(propertiesAnchor, 3); - w->widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].top = GBBT(propertiesAnchor, 4); - w->widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].bottom = GBBB(propertiesAnchor, 4); - WidgetSetCheckboxValue(*w, WIDX_TRACK_CHECK_APPLY_TO_ALL, windowTileInspectorApplyToAll); - WidgetSetCheckboxValue(*w, WIDX_TRACK_CHECK_CHAIN_LIFT, tileElement->AsTrack()->HasChain()); - WidgetSetCheckboxValue(*w, WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED, tileElement->AsTrack()->BlockBrakeClosed()); - WidgetSetCheckboxValue(*w, WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, tileElement->AsTrack()->IsIndestructible()); - break; - case TileElementType::SmallScenery: - { - // Raise / Lower - w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - - // Quadrant checkboxes - w->widgets[WIDX_SCENERY_CHECK_QUARTER_N].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 0; - w->widgets[WIDX_SCENERY_CHECK_QUARTER_N].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_N].top + 13; - w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; - w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].top + 13; - w->widgets[WIDX_SCENERY_CHECK_QUARTER_S].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 2; - w->widgets[WIDX_SCENERY_CHECK_QUARTER_S].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_S].top + 13; - w->widgets[WIDX_SCENERY_CHECK_QUARTER_W].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; - w->widgets[WIDX_SCENERY_CHECK_QUARTER_W].bottom = w->widgets[WIDX_SCENERY_CHECK_QUARTER_W].top + 13; - // This gets the relative rotation, by subtracting the camera's rotation, and wrapping it between 0-3 inclusive - bool N = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((0 - get_current_rotation()) & 3); - bool E = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((1 - get_current_rotation()) & 3); - bool S = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((2 - get_current_rotation()) & 3); - bool W = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((3 - get_current_rotation()) & 3); - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_QUARTER_N, N); - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_QUARTER_E, E); - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_QUARTER_S, S); - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_QUARTER_W, W); - - // Collision checkboxes - w->widgets[WIDX_SCENERY_CHECK_COLLISION_N].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 0; - w->widgets[WIDX_SCENERY_CHECK_COLLISION_N].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_N].top + 13; - w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; - w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].top + 13; - w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 2; - w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_S].top + 13; - w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; - w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].bottom = w->widgets[WIDX_SCENERY_CHECK_COLLISION_W].top + 13; - auto occupiedQuadrants = tileElement->GetOccupiedQuadrants(); - N = (occupiedQuadrants & (1 << ((2 - get_current_rotation()) & 3))) != 0; - E = (occupiedQuadrants & (1 << ((3 - get_current_rotation()) & 3))) != 0; - S = (occupiedQuadrants & (1 << ((0 - get_current_rotation()) & 3))) != 0; - W = (occupiedQuadrants & (1 << ((1 - get_current_rotation()) & 3))) != 0; - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_COLLISION_N, N); - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_COLLISION_E, E); - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_COLLISION_S, S); - WidgetSetCheckboxValue(*w, WIDX_SCENERY_CHECK_COLLISION_W, W); - break; - } - case TileElementType::Entrance: - w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].top = GBBT(propertiesAnchor, 1); - w->widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].bottom = GBBB(propertiesAnchor, 1); - WidgetSetEnabled( - *w, WIDX_ENTRANCE_BUTTON_MAKE_USABLE, - tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE); - break; - case TileElementType::Wall: - { - bool canBeSloped = false; - bool hasAnimation = false; - const auto wallEntry = tileElement->AsWall()->GetEntry(); - if (wallEntry != nullptr) - { - canBeSloped = !(wallEntry->flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE); - hasAnimation = wallEntry->flags & WALL_SCENERY_IS_DOOR; } - w->widgets[WIDX_WALL_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - w->widgets[WIDX_WALL_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - w->widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_WALL_DROPDOWN_SLOPE].top = GBBT(propertiesAnchor, 1) + 3; - w->widgets[WIDX_WALL_DROPDOWN_SLOPE].bottom = GBBB(propertiesAnchor, 1) - 3; - w->widgets[WIDX_WALL_DROPDOWN_SLOPE].text = WallSlopeStringIds[tileElement->AsWall()->GetSlope()]; - w->widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].top = GBBT(propertiesAnchor, 1) + 4; - w->widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].bottom = GBBB(propertiesAnchor, 1) - 4; - w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top = GBBT(propertiesAnchor, 2) + 3; - w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].bottom = GBBB(propertiesAnchor, 2) - 3; - w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].top = GBBT(propertiesAnchor, 2) + 4; - w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].bottom = GBBB(propertiesAnchor, 2) - 4; - w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].top = GBBT(propertiesAnchor, 2) + 4; - w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].bottom = GBBB(propertiesAnchor, 2) - 4; + case WIDX_BUTTON_ROTATE: + RotateElement(windowTileInspectorSelectedIndex); + break; - // Wall slope dropdown - WidgetSetEnabled(*w, WIDX_WALL_DROPDOWN_SLOPE, canBeSloped); - widget_invalidate(*w, WIDX_WALL_DROPDOWN_SLOPE); - WidgetSetEnabled(*w, WIDX_WALL_DROPDOWN_SLOPE_BUTTON, canBeSloped); - widget_invalidate(*w, WIDX_WALL_DROPDOWN_SLOPE_BUTTON); - // Wall animation frame spinner - WidgetSetEnabled(*w, WIDX_WALL_SPINNER_ANIMATION_FRAME, hasAnimation); - WidgetSetEnabled(*w, WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE, hasAnimation); - WidgetSetEnabled(*w, WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE, hasAnimation); - break; + case WIDX_BUTTON_SORT: + SortElements(); + break; + + case WIDX_BUTTON_COPY: + CopyElement(); + break; + + case WIDX_BUTTON_PASTE: + PasteElement(); + break; + + case WIDX_BUTTON_MOVE_UP: + SwapElements(windowTileInspectorSelectedIndex, windowTileInspectorSelectedIndex + 1); + break; + + case WIDX_BUTTON_MOVE_DOWN: + SwapElements(windowTileInspectorSelectedIndex - 1, windowTileInspectorSelectedIndex); + break; } - case TileElementType::LargeScenery: - w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - break; - case TileElementType::Banner: - w->widgets[WIDX_BANNER_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; - w->widgets[WIDX_BANNER_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; - w->widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; - w->widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; - w->widgets[WIDX_BANNER_CHECK_BLOCK_NE].top = GBBT(propertiesAnchor, 1); - w->widgets[WIDX_BANNER_CHECK_BLOCK_NE].bottom = GBBB(propertiesAnchor, 1); - w->widgets[WIDX_BANNER_CHECK_BLOCK_SE].top = GBBT(propertiesAnchor, 2); - w->widgets[WIDX_BANNER_CHECK_BLOCK_SE].bottom = GBBB(propertiesAnchor, 2); - w->widgets[WIDX_BANNER_CHECK_BLOCK_SW].top = GBBT(propertiesAnchor, 2); - w->widgets[WIDX_BANNER_CHECK_BLOCK_SW].bottom = GBBB(propertiesAnchor, 2); - w->widgets[WIDX_BANNER_CHECK_BLOCK_NW].top = GBBT(propertiesAnchor, 1); - w->widgets[WIDX_BANNER_CHECK_BLOCK_NW].bottom = GBBB(propertiesAnchor, 1); - WidgetSetCheckboxValue( - *w, WIDX_BANNER_CHECK_BLOCK_NE, - !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((0 - get_current_rotation()) & 3)))); - WidgetSetCheckboxValue( - *w, WIDX_BANNER_CHECK_BLOCK_SE, - !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((1 - get_current_rotation()) & 3)))); - WidgetSetCheckboxValue( - *w, WIDX_BANNER_CHECK_BLOCK_SW, - !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((2 - get_current_rotation()) & 3)))); - WidgetSetCheckboxValue( - *w, WIDX_BANNER_CHECK_BLOCK_NW, - !(tileElement->AsBanner()->GetAllowedEdges() & (1 << ((3 - get_current_rotation()) & 3)))); - break; - default: - break; // Nothing. - } -} -static void WindowTileInspectorPaint(rct_window* w, rct_drawpixelinfo* dpi) -{ - WindowDrawWidgets(*w, dpi); + // Only element-specific widgets from now on + if (tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) + return; - ScreenCoordsXY screenCoords(w->windowPos.x, w->windowPos.y); + TileElement* const tileElement = GetSelectedElement(); - // Draw coordinates - gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(5, 24), "X:", { w->colours[1] }); - gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(74, 24), "Y:", { w->colours[1] }); - if (windowTileInspectorTileSelected) - { - auto tileCoords = TileCoordsXY{ windowTileInspectorToolMap }; - auto ft = Formatter(); - ft.Add(tileCoords.x); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 43, 24 }, STR_FORMAT_INTEGER, ft, { w->colours[1], TextAlignment::RIGHT }); - ft = Formatter(); - ft.Add(tileCoords.y); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 113, 24 }, STR_FORMAT_INTEGER, ft, { w->colours[1], TextAlignment::RIGHT }); - } - else - { - gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(43 - 7, 24), "-", { w->colours[1] }); - gfx_draw_string(dpi, screenCoords + ScreenCoordsXY(113 - 7, 24), "-", { w->colours[1] }); + // Update selection, can be nullptr. + OpenRCT2::TileInspector::SetSelectedElement(tileElement); + + if (tileElement == nullptr) + return; + + // Page widgets + switch (tileElement->GetType()) + { + case TileElementType::Surface: + switch (widgetIndex) + { + case WIDX_SURFACE_BUTTON_REMOVE_FENCES: + SurfaceShowParkFences(false); + break; + + case WIDX_SURFACE_BUTTON_RESTORE_FENCES: + SurfaceShowParkFences(true); + break; + + case WIDX_SURFACE_CHECK_CORNER_N: + case WIDX_SURFACE_CHECK_CORNER_E: + case WIDX_SURFACE_CHECK_CORNER_S: + case WIDX_SURFACE_CHECK_CORNER_W: + SurfaceToggleCorner(((widgetIndex - WIDX_SURFACE_CHECK_CORNER_N) + 2 - get_current_rotation()) & 3); + break; + + case WIDX_SURFACE_CHECK_DIAGONAL: + SurfaceToggleDiagonal(); + break; + } // switch widgetindex + break; + case TileElementType::Path: + switch (widgetIndex) + { + case WIDX_PATH_CHECK_SLOPED: + PathSetSloped(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsSloped()); + break; + + case WIDX_PATH_CHECK_BROKEN: + PathSetBroken(windowTileInspectorSelectedIndex, !tileElement->AsPath()->IsBroken()); + break; + + case WIDX_PATH_CHECK_EDGE_E: + case WIDX_PATH_CHECK_EDGE_S: + case WIDX_PATH_CHECK_EDGE_W: + case WIDX_PATH_CHECK_EDGE_N: + { + // 0 = east/right, 1 = south/bottom, 2 = west/left, 3 = north/top + const int32_t eswn = (widgetIndex - WIDX_PATH_CHECK_EDGE_E) / 2; + // Transform to world orientation + const int32_t index = (eswn - get_current_rotation()) & 3; + PathToggleEdge( + windowTileInspectorSelectedIndex, + index + 4); // The corners are stored in the 4 most significant bits, hence the + 4 + break; + } + + case WIDX_PATH_CHECK_EDGE_NE: + case WIDX_PATH_CHECK_EDGE_SE: + case WIDX_PATH_CHECK_EDGE_SW: + case WIDX_PATH_CHECK_EDGE_NW: + { + // 0 = NE, 1 = SE, 2 = SW, 3 = NW + const int32_t neseswnw = (widgetIndex - WIDX_PATH_CHECK_EDGE_NE) / 2; + // Transform to world orientation + const int32_t index = (neseswnw - get_current_rotation()) & 3; + PathToggleEdge(windowTileInspectorSelectedIndex, index); + break; + } + } // switch widget index + break; + + case TileElementType::Track: + switch (widgetIndex) + { + case WIDX_TRACK_CHECK_APPLY_TO_ALL: + _applyToAll ^= 1; + InvalidateWidget(widgetIndex); + break; + + case WIDX_TRACK_CHECK_CHAIN_LIFT: + { + bool entireTrackBlock = IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL); + bool newLift = !tileElement->AsTrack()->HasChain(); + TrackBlockSetLift(windowTileInspectorSelectedIndex, entireTrackBlock, newLift); + break; + } + + case WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED: + TrackSetBlockBrake(windowTileInspectorSelectedIndex, !tileElement->AsTrack()->BlockBrakeClosed()); + break; + + case WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE: + TrackSetIndestructible(windowTileInspectorSelectedIndex, !tileElement->AsTrack()->IsIndestructible()); + break; + } // switch widget index + break; + + case TileElementType::SmallScenery: + switch (widgetIndex) + { + case WIDX_SCENERY_CHECK_QUARTER_N: + case WIDX_SCENERY_CHECK_QUARTER_E: + case WIDX_SCENERY_CHECK_QUARTER_S: + case WIDX_SCENERY_CHECK_QUARTER_W: + QuarterTileSet(windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_QUARTER_N); + break; + + case WIDX_SCENERY_CHECK_COLLISION_N: + case WIDX_SCENERY_CHECK_COLLISION_E: + case WIDX_SCENERY_CHECK_COLLISION_S: + case WIDX_SCENERY_CHECK_COLLISION_W: + ToggleQuadrantCollosion(windowTileInspectorSelectedIndex, widgetIndex - WIDX_SCENERY_CHECK_COLLISION_N); + break; + } // switch widget index + break; + + case TileElementType::Entrance: + switch (widgetIndex) + { + case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: + EntranceMakeUsable(windowTileInspectorSelectedIndex); + break; + } // switch widget index + break; + + case TileElementType::Banner: + switch (widgetIndex) + { + case WIDX_BANNER_CHECK_BLOCK_NE: + case WIDX_BANNER_CHECK_BLOCK_SE: + case WIDX_BANNER_CHECK_BLOCK_SW: + case WIDX_BANNER_CHECK_BLOCK_NW: + BannerToggleBlock(windowTileInspectorSelectedIndex, widgetIndex - WIDX_BANNER_CHECK_BLOCK_NE); + break; + } // switch widget index + break; + + case TileElementType::LargeScenery: + case TileElementType::Wall: + default: + break; + } } - if (windowTileInspectorSelectedIndex != -1) + void OnClose() override { - // X and Y of first element in detail box - screenCoords = w->windowPos - + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_GROUPBOX_DETAILS].top + 14 }; + OpenRCT2::TileInspector::SetSelectedElement(nullptr); + } - // Get map element - TileElement* const tileElement = WindowTileInspectorGetSelectedElement(w); + void OnResize() override + { + if (width < min_width) + { + Invalidate(); + width = min_width; + } + if (height < min_height) + { + Invalidate(); + height = min_height; + } + } + + void OnMouseDown(WidgetIndex widgetIndex) override + { + switch (widgetIndex) + { + case WIDX_SPINNER_X_INCREASE: + windowTileInspectorTile.x = std::min(windowTileInspectorTile.x + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); + _toolMap.x = std::min(_toolMap.x + 32, MAXIMUM_TILE_START_XY); + LoadTile(nullptr); + break; + + case WIDX_SPINNER_X_DECREASE: + windowTileInspectorTile.x = std::max(windowTileInspectorTile.x - 1, 0); + _toolMap.x = std::max(_toolMap.x - 32, 0); + LoadTile(nullptr); + break; + + case WIDX_SPINNER_Y_INCREASE: + windowTileInspectorTile.y = std::min(windowTileInspectorTile.y + 1, MAXIMUM_MAP_SIZE_TECHNICAL - 1); + _toolMap.y = std::min(_toolMap.y + 32, MAXIMUM_TILE_START_XY); + LoadTile(nullptr); + break; + + case WIDX_SPINNER_Y_DECREASE: + windowTileInspectorTile.y = std::max(windowTileInspectorTile.y - 1, 0); + _toolMap.y = std::max(_toolMap.y - 32, 0); + LoadTile(nullptr); + break; + } // switch widget index + + // Only element-specific widgets from now on + if (tileInspectorPage == TileInspectorPage::Default || windowTileInspectorSelectedIndex == -1) + return; + + const TileElement* tileElement = GetSelectedElement(); if (tileElement == nullptr) return; switch (tileElement->GetType()) { case TileElementType::Surface: - { - // Details - // Terrain texture name - StringId terrainNameId = STR_EMPTY; - auto surfaceStyle = tileElement->AsSurface()->GetSurfaceStyleObject(); - if (surfaceStyle != nullptr) + switch (widgetIndex) { - terrainNameId = surfaceStyle->NameStringId; - } - auto ft = Formatter(); - ft.Add(terrainNameId); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_TERAIN, ft, { w->colours[1] }); + case WIDX_SURFACE_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Edge texture name - StringId terrainEdgeNameId = STR_EMPTY; - auto edgeStyle = tileElement->AsSurface()->GetEdgeStyleObject(); - if (edgeStyle != nullptr) - { - terrainEdgeNameId = edgeStyle->NameStringId; - } - ft = Formatter(); - ft.Add(terrainEdgeNameId); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SURFACE_EDGE, ft, { w->colours[1] }); - - // Land ownership - StringId landOwnership; - if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) - landOwnership = STR_LAND_OWNED; - else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_AVAILABLE) - landOwnership = STR_LAND_SALE; - else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) - landOwnership = STR_CONSTRUCTION_RIGHTS_OWNED; - else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) - landOwnership = STR_CONSTRUCTION_RIGHTS_SALE; - else - landOwnership = STR_TILE_INSPECTOR_LAND_NOT_OWNED_AND_NOT_AVAILABLE; - ft = Formatter(); - ft.Add(landOwnership); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SURFACE_OWNERSHIP, ft, { w->colours[1] }); - - // Water level - ft = Formatter(); - ft.Add(tileElement->AsSurface()->GetWaterHeight()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_SURFACE_WATER_LEVEL, ft, { w->colours[1] }); - - // Properties - // Raise / lower label - screenCoords = w->windowPos - + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_SURFACE_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); - - // Raised corners - screenCoords = w->windowPos - + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_SURFACE_CHECK_CORNER_E].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_CORNERS, {}, { w->colours[1] }); + case WIDX_SURFACE_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } case TileElementType::Path: - { - // Details - auto pathEl = tileElement->AsPath(); - auto footpathObj = pathEl->GetLegacyPathEntry(); - if (footpathObj == nullptr) + switch (widgetIndex) { - // Surface name - auto surfaceObj = pathEl->GetSurfaceEntry(); - if (surfaceObj != nullptr) - { - auto ft = Formatter(); - ft.Add(surfaceObj->NameStringId); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_FOOTPATH_SURFACE_NAME, ft, { COLOUR_WHITE }); - } + case WIDX_PATH_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Railings name - auto railingsObj = pathEl->GetRailingsEntry(); - if (railingsObj != nullptr) - { - auto ft = Formatter(); - ft.Add(railingsObj->NameStringId); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_FOOTPATH_RAILINGS_NAME, ft, - { COLOUR_WHITE }); - } - } - else - { - // Legacy path name - auto footpathEntry = reinterpret_cast(footpathObj->GetLegacyData()); - auto ft = Formatter(); - ft.Add(footpathEntry->string_idx); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_NAME, ft, { COLOUR_WHITE }); - } - - // Path addition - if (tileElement->AsPath()->HasAddition()) - { - const auto pathAdditionType = tileElement->AsPath()->GetAdditionEntryIndex(); - const auto* pathBitEntry = get_footpath_item_entry(pathAdditionType); - StringId additionNameId = pathBitEntry != nullptr ? pathBitEntry->name - : static_cast(STR_UNKNOWN_OBJECT_TYPE); - auto ft = Formatter(); - ft.Add(additionNameId); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS, ft, - { COLOUR_WHITE }); - } - else - { - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, {}, - { COLOUR_WHITE }); - } - - // Properties - // Raise / lower label - screenCoords = w->windowPos - + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_PATH_SPINNER_HEIGHT].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_PATH_SPINNER_HEIGHT].left + 3; - auto ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); - - // Path connections - screenCoords = w->windowPos - + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_PATH_CHECK_EDGE_W].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_PATH_CONNECTED_EDGES, {}, { w->colours[1] }); + case WIDX_PATH_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } case TileElementType::Track: - { - auto trackElement = tileElement->AsTrack(); - RideId rideId = trackElement->GetRideIndex(); - auto ride = get_ride(rideId); - - // Ride ID - auto ft = Formatter(); - ft.Add(rideId); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_TRACK_RIDE_ID, ft, { w->colours[1] }); - - // Ride name - if (ride != nullptr) + switch (widgetIndex) { - ft = Formatter(); - ride->FormatNameTo(ft); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_TRACK_RIDE_NAME, ft, { w->colours[1] }); - } + case WIDX_TRACK_SPINNER_HEIGHT_INCREASE: + if (IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL)) + TrackBlockHeightOffset(windowTileInspectorSelectedIndex, 1); + else + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Ride type. Individual pieces may be of a different ride type from the ride it belongs to. - const auto& rtd = GetRideTypeDescriptor(trackElement->GetRideType()); - ft = Formatter(); - ft.Add(rtd.Naming.Name); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_TRACK_RIDE_TYPE, ft, { w->colours[1] }); - - // Track - ft = Formatter(); - ft.Add(trackElement->GetTrackType()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_TRACK_PIECE_ID, ft, { w->colours[1] }); - - ft = Formatter(); - ft.Add(trackElement->GetSequenceIndex()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 44 }, STR_TILE_INSPECTOR_TRACK_SEQUENCE, ft, { w->colours[1] }); - if (trackElement->IsStation()) - { - auto stationIndex = trackElement->GetStationIndex(); - ft = Formatter(); - ft.Add(STR_COMMA16); - ft.Add(stationIndex.ToUnderlying()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { w->colours[1] }); - } - else - { - const char* stationNone = "-"; - ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(stationNone); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { w->colours[1] }); - } - - ft = Formatter(); - ft.Add(ColourSchemeNames[trackElement->GetColourScheme()]); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 66 }, STR_TILE_INSPECTOR_COLOUR_SCHEME, ft, { w->colours[1] }); - - // Properties - // Raise / lower label - screenCoords.y = w->windowPos.y + w->widgets[WIDX_TRACK_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_TRACK_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); + case WIDX_TRACK_SPINNER_HEIGHT_DECREASE: + if (IsWidgetPressed(WIDX_TRACK_CHECK_APPLY_TO_ALL)) + TrackBlockHeightOffset(windowTileInspectorSelectedIndex, -1); + else + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } case TileElementType::SmallScenery: - { - // Details - // Age - auto ft = Formatter(); - ft.Add(tileElement->AsSmallScenery()->GetAge()); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_AGE, ft, { w->colours[1] }); - - // Quadrant value - const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); - if (sceneryEntry != nullptr && !(sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))) + switch (widgetIndex) { - int16_t quadrant = tileElement->AsSmallScenery()->GetSceneryQuadrant(); - static constexpr StringId _quadrantStringIdx[] = { - STR_TILE_INSPECTOR_SCENERY_QUADRANT_SW, - STR_TILE_INSPECTOR_SCENERY_QUADRANT_NW, - STR_TILE_INSPECTOR_SCENERY_QUADRANT_NE, - STR_TILE_INSPECTOR_SCENERY_QUADRANT_SE, - }; - ft = Formatter(); - ft.Add(_quadrantStringIdx[quadrant]); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SCENERY_QUADRANT, ft, - { w->colours[1] }); - } + case WIDX_SCENERY_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - // Scenery ID - ft = Formatter(); - ft.Add(tileElement->AsSmallScenery()->GetEntryIndex()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SCENERY_ENTRY_IDX, ft, { w->colours[1] }); - - // Properties - // Raise / Lower - screenCoords.y = w->windowPos.y + w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_SCENERY_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); - - // Quarter tile - screenCoords = w->windowPos - + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, - w->widgets[WIDX_SCENERY_CHECK_QUARTER_E].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_QUADRANT_LABEL, {}, { w->colours[1] }); - - // Collision - screenCoords.y = w->windowPos.y + w->widgets[WIDX_SCENERY_CHECK_COLLISION_E].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_COLLISSION, {}, { w->colours[1] }); + case WIDX_SCENERY_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index break; - } case TileElementType::Entrance: - { - // Details - // Entrance type - auto ft = Formatter(); - ft.Add(EntranceTypeStringIds[tileElement->AsEntrance()->GetEntranceType()]); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRANCE_TYPE, ft, { w->colours[1] }); + switch (widgetIndex) + { + case WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + case WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + + case WIDX_ENTRANCE_BUTTON_MAKE_USABLE: + EntranceMakeUsable(windowTileInspectorSelectedIndex); + break; + } // switch widget index + break; + + case TileElementType::Wall: + switch (widgetIndex) { - // TODO: Make this work with Left/Right park entrance parts - ft = Formatter(); - ft.Add(park_entrance_get_index({ windowTileInspectorToolMap, tileElement->GetBaseZ() })); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft, - { w->colours[1] }); - } - else - { - ft = Formatter(); - ft.Add(tileElement->AsEntrance()->GetStationIndex().ToUnderlying()); - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + case WIDX_WALL_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_WALL_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + + case WIDX_WALL_DROPDOWN_SLOPE_BUTTON: { - // Ride entrance ID - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft, - { w->colours[1] }); + rct_widget* widget = &widgets[widgetIndex]; + // Use dropdown instead of dropdown button + widget--; + // Fill dropdown list + gDropdownItems[0].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[1].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[2].Format = STR_DROPDOWN_MENU_LABEL; + gDropdownItems[0].Args = STR_TILE_INSPECTOR_WALL_FLAT; + gDropdownItems[1].Args = STR_TILE_INSPECTOR_WALL_SLOPED_LEFT; + gDropdownItems[2].Args = STR_TILE_INSPECTOR_WALL_SLOPED_RIGHT; + WindowDropdownShowTextCustomWidth( + { windowPos.x + widget->left, windowPos.y + widget->top }, widget->height() + 1, colours[1], 0, + Dropdown::Flag::StayOpen, 3, widget->width() - 3); + + // Set current value as checked + Dropdown::SetChecked(tileElement->AsWall()->GetSlope(), true); + break; + } + + case WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE: + WallAnimationFrameOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE: + WallAnimationFrameOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index + break; + + case TileElementType::LargeScenery: + switch (widgetIndex) + { + case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index + break; + + case TileElementType::Banner: + switch (widgetIndex) + { + case WIDX_BANNER_SPINNER_HEIGHT_INCREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, 1); + break; + + case WIDX_BANNER_SPINNER_HEIGHT_DECREASE: + BaseHeightOffset(windowTileInspectorSelectedIndex, -1); + break; + } // switch widget index + break; + + default: + break; + } + } + + void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override + { + if (dropdownIndex == -1) + return; + // Get selected element + TileElement* const tileElement = GetSelectedElement(); + if (tileInspectorPage == TileInspectorPage::Wall) + { + openrct2_assert(tileElement->GetType() == TileElementType::Wall, "Element is not a wall"); + if (widgetIndex == WIDX_WALL_DROPDOWN_SLOPE_BUTTON) + WallSetSlope(windowTileInspectorSelectedIndex, dropdownIndex); + } + } + + void OnToolUpdate(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + map_invalidate_selection_rect(); + gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; + + CoordsXY mapCoords; + TileElement* clickedElement = nullptr; + bool mouseOnViewport = false; + if (InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z)) + { + auto info = get_map_coordinates_from_pos(screenCoords, ViewportInteractionFlags); + clickedElement = info.Element; + mapCoords = info.Loc; + } + // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor + if (clickedElement == nullptr) + { + auto mouseCoords = screen_pos_to_map_pos(screenCoords, nullptr); + if (mouseCoords.has_value()) + { + mouseOnViewport = true; + mapCoords = mouseCoords.value(); + } + } + if (mouseOnViewport) + gMapSelectPositionA = gMapSelectPositionB = mapCoords; + else if (_tileSelected) + gMapSelectPositionA = gMapSelectPositionB = _toolMap; + else + gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; + + gMapSelectType = MAP_SELECT_TYPE_FULL; + map_invalidate_selection_rect(); + } + + void OnToolDown(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + UpdateSelectedTile(screenCoords); + } + + void OnToolDrag(WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords) override + { + UpdateSelectedTile(screenCoords); + } + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override + { + return ScreenSize(WW - 30, windowTileInspectorElementCount * SCROLLABLE_ROW_HEIGHT); + } + + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + // There is nothing to interact with when no tile is selected + if (!_tileSelected) + return; + + // Because the list items are displayed in reverse order, subtract the calculated index from the amount of elements + const int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; + const ScreenRect checkboxColumnRect{ { 2, 0 }, { 15, screenCoords.y } }; + if (index >= 0 && checkboxColumnRect.Contains(screenCoords)) + { // Checkbox was clicked + ToggleInvisibility(index); + } + else + { + SelectElementFromList(index); + } + } + + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override + { + int16_t index = windowTileInspectorElementCount - (screenCoords.y - 1) / SCROLLABLE_ROW_HEIGHT - 1; + if (index < 0 || index >= windowTileInspectorElementCount) + _highlightedIndex = -1; + else + _highlightedIndex = index; + InvalidateWidget(WIDX_LIST); + } + + void OnDraw(rct_drawpixelinfo& dpi) override + { + DrawWidgets(dpi); + ScreenCoordsXY screenCoords(windowPos.x, windowPos.y); + + // Draw coordinates + gfx_draw_string(&dpi, screenCoords + ScreenCoordsXY(5, 24), "X:", { colours[1] }); + gfx_draw_string(&dpi, screenCoords + ScreenCoordsXY(74, 24), "Y:", { colours[1] }); + if (_tileSelected) + { + auto tileCoords = TileCoordsXY{ _toolMap }; + auto ft = Formatter(); + ft.Add(tileCoords.x); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 43, 24 }, STR_FORMAT_INTEGER, ft, { colours[1], TextAlignment::RIGHT }); + ft = Formatter(); + ft.Add(tileCoords.y); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 113, 24 }, STR_FORMAT_INTEGER, ft, { colours[1], TextAlignment::RIGHT }); + } + else + { + gfx_draw_string(&dpi, screenCoords + ScreenCoordsXY(43 - 7, 24), "-", { colours[1] }); + gfx_draw_string(&dpi, screenCoords + ScreenCoordsXY(113 - 7, 24), "-", { colours[1] }); + } + + if (windowTileInspectorSelectedIndex != -1) + { + // X and Y of first element in detail box + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_GROUPBOX_DETAILS].top + 14 }; + + // Get map element + TileElement* const tileElement = GetSelectedElement(); + if (tileElement == nullptr) + return; + + switch (tileElement->GetType()) + { + case TileElementType::Surface: + { + // Details + // Terrain texture name + StringId terrainNameId = STR_EMPTY; + auto surfaceStyle = tileElement->AsSurface()->GetSurfaceStyleObject(); + if (surfaceStyle != nullptr) + terrainNameId = surfaceStyle->NameStringId; + auto ft = Formatter(); + ft.Add(terrainNameId); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_TERAIN, ft, { colours[1] }); + + // Edge texture name + StringId terrainEdgeNameId = STR_EMPTY; + auto edgeStyle = tileElement->AsSurface()->GetEdgeStyleObject(); + if (edgeStyle != nullptr) + terrainEdgeNameId = edgeStyle->NameStringId; + ft = Formatter(); + ft.Add(terrainEdgeNameId); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SURFACE_EDGE, ft, { colours[1] }); + + // Land ownership + StringId landOwnership; + if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_OWNED) + landOwnership = STR_LAND_OWNED; + else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_AVAILABLE) + landOwnership = STR_LAND_SALE; + else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) + landOwnership = STR_CONSTRUCTION_RIGHTS_OWNED; + else if (tileElement->AsSurface()->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) + landOwnership = STR_CONSTRUCTION_RIGHTS_SALE; + else + landOwnership = STR_TILE_INSPECTOR_LAND_NOT_OWNED_AND_NOT_AVAILABLE; + + ft = Formatter(); + ft.Add(landOwnership); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SURFACE_OWNERSHIP, ft, { colours[1] }); + + // Water level + ft = Formatter(); + ft.Add(tileElement->AsSurface()->GetWaterHeight()); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_SURFACE_WATER_LEVEL, ft, + { colours[1] }); + + // Properties + // Raise / lower label + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_SURFACE_SPINNER_HEIGHT].top }; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_SURFACE_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Raised corners + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_SURFACE_CHECK_CORNER_E].top }; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_SURFACE_CORNERS, {}, { colours[1] }); + break; + } + case TileElementType::Path: + { + // Details + auto pathEl = tileElement->AsPath(); + auto footpathObj = pathEl->GetLegacyPathEntry(); + if (footpathObj == nullptr) + { + // Surface name + auto surfaceObj = pathEl->GetSurfaceEntry(); + if (surfaceObj != nullptr) + { + auto ft = Formatter(); + ft.Add(surfaceObj->NameStringId); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_FOOTPATH_SURFACE_NAME, ft, { COLOUR_WHITE }); + } + + // Railings name + auto railingsObj = pathEl->GetRailingsEntry(); + if (railingsObj != nullptr) + { + auto ft = Formatter(); + ft.Add(railingsObj->NameStringId); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_FOOTPATH_RAILINGS_NAME, ft, + { COLOUR_WHITE }); + } } else { - // Ride exit ID - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_EXIT_ID, ft, - { w->colours[1] }); + // Legacy path name + auto footpathEntry = reinterpret_cast(footpathObj->GetLegacyData()); + auto ft = Formatter(); + ft.Add(footpathEntry->string_idx); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_PATH_NAME, ft, { COLOUR_WHITE }); } + + // Path addition + if (tileElement->AsPath()->HasAddition()) + { + const auto pathAdditionType = tileElement->AsPath()->GetAdditionEntryIndex(); + const auto* pathBitEntry = get_footpath_item_entry(pathAdditionType); + StringId additionNameId = pathBitEntry != nullptr ? pathBitEntry->name + : static_cast(STR_UNKNOWN_OBJECT_TYPE); + auto ft = Formatter(); + ft.Add(additionNameId); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS, ft, + { COLOUR_WHITE }); + } + else + { + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 2 * 11 }, STR_TILE_INSPECTOR_PATH_ADDITIONS_NONE, {}, + { COLOUR_WHITE }); + } + + // Properties + // Raise / lower label + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_PATH_SPINNER_HEIGHT].top }; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_PATH_SPINNER_HEIGHT].left + 3; + auto ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Path connections + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_PATH_CHECK_EDGE_W].top }; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_PATH_CONNECTED_EDGES, {}, { colours[1] }); + break; } - if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) - { - // Entrance part - ft = Formatter(); - ft.Add(ParkEntrancePartStringIds[tileElement->AsEntrance()->GetSequenceIndex()]); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_PART, ft, { w->colours[1] }); - } - else + case TileElementType::Track: { + auto trackElement = tileElement->AsTrack(); + RideId id = trackElement->GetRideIndex(); + auto rideTile = get_ride(id); + // Ride ID + auto ft = Formatter(); + ft.Add(id); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_TRACK_RIDE_ID, ft, { colours[1] }); + + // Ride name + if (rideTile != nullptr) + { + ft = Formatter(); + rideTile->FormatNameTo(ft); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_TRACK_RIDE_NAME, ft, + { colours[1] }); + } + + // Ride type. Individual pieces may be of a different ride type from the ride it belongs to. + const auto& rtd = GetRideTypeDescriptor(trackElement->GetRideType()); ft = Formatter(); - ft.Add(tileElement->AsEntrance()->GetRideIndex()); + ft.Add(rtd.Naming.Name); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_RIDE_ID, ft, - { w->colours[1] }); - // Station index - auto stationIndex = tileElement->AsEntrance()->GetStationIndex(); + &dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_TRACK_RIDE_TYPE, ft, { colours[1] }); + + // Track ft = Formatter(); - ft.Add(STR_COMMA16); - ft.Add(stationIndex.ToUnderlying()); + ft.Add(trackElement->GetTrackType()); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { w->colours[1] }); - } + &dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_TRACK_PIECE_ID, ft, { colours[1] }); - // Properties - // Raise / Lower - screenCoords.y = w->windowPos.y + w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); - break; - } - - case TileElementType::Wall: - { - // Details - // Type - auto ft = Formatter(); - ft.Add(tileElement->AsWall()->GetEntryIndex()); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_TYPE, ft, { w->colours[1] }); - - // Banner info - auto banner = tileElement->AsWall()->GetBanner(); - if (banner != nullptr) - { ft = Formatter(); - banner->FormatTextTo(ft); + ft.Add(trackElement->GetSequenceIndex()); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, - { w->colours[1] }); - } - else - { + &dpi, screenCoords + ScreenCoordsXY{ 0, 44 }, STR_TILE_INSPECTOR_TRACK_SEQUENCE, ft, { colours[1] }); + if (trackElement->IsStation()) + { + auto stationIndex = trackElement->GetStationIndex(); + ft = Formatter(); + ft.Add(STR_COMMA16); + ft.Add(stationIndex.ToUnderlying()); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { colours[1] }); + } + else + { + const char* stationNone = "-"; + ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(stationNone); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 55 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { colours[1] }); + } + + ft = Formatter(); + ft.Add(ColourSchemeNames[trackElement->GetColourScheme()]); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, - { w->colours[1] }); + &dpi, screenCoords + ScreenCoordsXY{ 0, 66 }, STR_TILE_INSPECTOR_COLOUR_SCHEME, ft, { colours[1] }); + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_TRACK_SPINNER_HEIGHT].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_TRACK_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + break; } - // Properties - // Raise / lower label - screenCoords.y = w->windowPos.y + w->widgets[WIDX_WALL_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_WALL_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); - - // Slope label - screenCoords = w->windowPos - + ScreenCoordsXY{ w->widgets[WIDX_GROUPBOX_DETAILS].left + 7, w->widgets[WIDX_WALL_DROPDOWN_SLOPE].top }; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_SLOPE, {}, { w->colours[1] }); - - // Animation frame label - screenCoords.y = w->windowPos.y + w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME, {}, { w->colours[1] }); - - // Current animation frame - colour_t colour = w->colours[1]; - if (WidgetIsDisabled(*w, WIDX_WALL_SPINNER_ANIMATION_FRAME)) + case TileElementType::SmallScenery: { - colour = w->colours[0] | COLOUR_FLAG_INSET; + // Details + // Age + auto ft = Formatter(); + ft.Add(tileElement->AsSmallScenery()->GetAge()); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_AGE, ft, { colours[1] }); + + // Quadrant value + const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); + if (sceneryEntry != nullptr && !(sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))) + { + int16_t quadrant = tileElement->AsSmallScenery()->GetSceneryQuadrant(); + static constexpr StringId _quadrantStringIdx[] = { + STR_TILE_INSPECTOR_SCENERY_QUADRANT_SW, + STR_TILE_INSPECTOR_SCENERY_QUADRANT_NW, + STR_TILE_INSPECTOR_SCENERY_QUADRANT_NE, + STR_TILE_INSPECTOR_SCENERY_QUADRANT_SE, + }; + ft = Formatter(); + ft.Add(_quadrantStringIdx[quadrant]); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_SCENERY_QUADRANT, ft, + { colours[1] }); + } + + // Scenery ID + ft = Formatter(); + ft.Add(tileElement->AsSmallScenery()->GetEntryIndex()); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_SCENERY_ENTRY_IDX, ft, { colours[1] }); + + // Properties + // Raise / Lower + screenCoords.y = windowPos.y + widgets[WIDX_SCENERY_SPINNER_HEIGHT].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_SCENERY_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Quarter tile + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_SCENERY_CHECK_QUARTER_E].top }; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_SCENERY_QUADRANT_LABEL, {}, { colours[1] }); + + // Collision + screenCoords.y = windowPos.y + widgets[WIDX_SCENERY_CHECK_COLLISION_E].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_COLLISSION, {}, { colours[1] }); + break; } - screenCoords.x = w->windowPos.x + w->widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].left + 3; - ft = Formatter(); - ft.Add(tileElement->AsWall()->GetAnimationFrame()); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colour }); - break; - } - case TileElementType::LargeScenery: - { - // Details - // Type - auto sceneryElement = tileElement->AsLargeScenery(); - ObjectEntryIndex largeSceneryType = sceneryElement->GetEntryIndex(); - auto ft = Formatter(); - ft.Add(largeSceneryType); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_LARGE_SCENERY_TYPE, ft, { w->colours[1] }); - - // Part ID - ft = Formatter(); - ft.Add(sceneryElement->GetSequenceIndex()); - DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_LARGE_SCENERY_PIECE_ID, ft, - { w->colours[1] }); - - // Banner info - auto* largeSceneryEntry = get_large_scenery_entry(largeSceneryType); - if (largeSceneryEntry != nullptr && largeSceneryEntry->scrolling_mode != SCROLLING_MODE_NONE) + case TileElementType::Entrance: { - auto banner = sceneryElement->GetBanner(); + // Details + // Entrance type + auto ft = Formatter(); + ft.Add(EntranceTypeStringIds[tileElement->AsEntrance()->GetEntranceType()]); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_ENTRANCE_TYPE, ft, { colours[1] }); + + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + { + // TODO: Make this work with Left/Right park entrance parts + ft = Formatter(); + ft.Add(park_entrance_get_index({ _toolMap, tileElement->GetBaseZ() })); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft, + { colours[1] }); + } + else + { + ft = Formatter(); + ft.Add(tileElement->AsEntrance()->GetStationIndex().ToUnderlying()); + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + { + // Ride entrance ID + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_ENTRANCE_ID, ft, + { colours[1] }); + } + else + { + // Ride exit ID + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRANCE_EXIT_ID, ft, + { colours[1] }); + } + } + + if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE) + { + // Entrance part + ft = Formatter(); + ft.Add(ParkEntrancePartStringIds[tileElement->AsEntrance()->GetSequenceIndex()]); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_PART, ft, { colours[1] }); + } + else + { + // Ride ID + ft = Formatter(); + ft.Add(tileElement->AsEntrance()->GetRideIndex()); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRANCE_RIDE_ID, ft, + { colours[1] }); + // Station index + auto stationIndex = tileElement->AsEntrance()->GetStationIndex(); + ft = Formatter(); + ft.Add(STR_COMMA16); + ft.Add(stationIndex.ToUnderlying()); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 33 }, STR_TILE_INSPECTOR_STATION_INDEX, ft, { colours[1] }); + } + + // Properties + // Raise / Lower + screenCoords.y = windowPos.y + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + break; + } + + case TileElementType::Wall: + { + // Details + // Type + auto ft = Formatter(); + ft.Add(tileElement->AsWall()->GetEntryIndex()); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_WALL_TYPE, ft, { colours[1] }); + + // Banner info + auto banner = tileElement->AsWall()->GetBanner(); if (banner != nullptr) { ft = Formatter(); banner->FormatTextTo(ft); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, - { w->colours[1] }); + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, + { colours[1] }); } + else + { + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, + { colours[1] }); + } + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_WALL_SPINNER_HEIGHT].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_WALL_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Slope label + screenCoords = windowPos + + ScreenCoordsXY{ widgets[WIDX_GROUPBOX_DETAILS].left + 7, widgets[WIDX_WALL_DROPDOWN_SLOPE].top }; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_WALL_SLOPE, {}, { colours[1] }); + + // Animation frame label + screenCoords.y = windowPos.y + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_WALL_ANIMATION_FRAME, {}, { colours[1] }); + + // Current animation frame + colour_t colour = colours[1]; + if (IsWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME)) + { + colour = colours[0] | COLOUR_FLAG_INSET; + } + screenCoords.x = windowPos.x + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].left + 3; + ft = Formatter(); + ft.Add(tileElement->AsWall()->GetAnimationFrame()); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colour }); + break; } - else + + case TileElementType::LargeScenery: { + // Details + // Type + auto sceneryElement = tileElement->AsLargeScenery(); + ObjectEntryIndex largeSceneryType = sceneryElement->GetEntryIndex(); + auto ft = Formatter(); + ft.Add(largeSceneryType); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_LARGE_SCENERY_TYPE, ft, { colours[1] }); + + // Part ID + ft = Formatter(); + ft.Add(sceneryElement->GetSequenceIndex()); DrawTextBasic( - dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, - { w->colours[1] }); + &dpi, screenCoords + ScreenCoordsXY{ 0, 11 }, STR_TILE_INSPECTOR_LARGE_SCENERY_PIECE_ID, ft, + { colours[1] }); + + // Banner info + auto* largeSceneryEntry = get_large_scenery_entry(largeSceneryType); + if (largeSceneryEntry != nullptr && largeSceneryEntry->scrolling_mode != SCROLLING_MODE_NONE) + { + auto banner = sceneryElement->GetBanner(); + if (banner != nullptr) + { + ft = Formatter(); + banner->FormatTextTo(ft); + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, + { colours[1] }); + } + } + else + { + DrawTextBasic( + &dpi, screenCoords + ScreenCoordsXY{ 0, 22 }, STR_TILE_INSPECTOR_ENTRY_BANNER_NONE, {}, + { colours[1] }); + } + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].left + 3; + ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + break; } - // Properties - // Raise / lower label - screenCoords.y = w->windowPos.y + w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].left + 3; - ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); - break; - } - - case TileElementType::Banner: - { - // Details - // Banner info - auto banner = tileElement->AsBanner()->GetBanner(); - if (banner != nullptr) + case TileElementType::Banner: { - Formatter ft; - banner->FormatTextTo(ft); - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, { w->colours[1] }); + // Details + // Banner info + auto banner = tileElement->AsBanner()->GetBanner(); + if (banner != nullptr) + { + Formatter ft; + banner->FormatTextTo(ft); + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_ENTRY_BANNER_TEXT, ft, { colours[1] }); + } + + // Properties + // Raise / lower label + screenCoords.y = windowPos.y + widgets[WIDX_BANNER_SPINNER_HEIGHT].top; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { colours[1] }); + + // Current base height + screenCoords.x = windowPos.x + widgets[WIDX_BANNER_SPINNER_HEIGHT].left + 3; + auto ft = Formatter(); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords, STR_FORMAT_INTEGER, ft, { colours[1] }); + + // Blocked paths + screenCoords.y += 28; + screenCoords.x = windowPos.x + widgets[WIDX_GROUPBOX_DETAILS].left + 7; + DrawTextBasic(&dpi, screenCoords, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, {}, { colours[1] }); + break; } - // Properties - // Raise / lower label - screenCoords.y = w->windowPos.y + w->widgets[WIDX_BANNER_SPINNER_HEIGHT].top; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BASE_HEIGHT_FULL, {}, { w->colours[1] }); - - // Current base height - screenCoords.x = w->windowPos.x + w->widgets[WIDX_BANNER_SPINNER_HEIGHT].left + 3; - auto ft = Formatter(); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords, STR_FORMAT_INTEGER, ft, { w->colours[1] }); - - // Blocked paths - screenCoords.y += 28; - screenCoords.x = w->windowPos.x + w->widgets[WIDX_GROUPBOX_DETAILS].left + 7; - DrawTextBasic(dpi, screenCoords, STR_TILE_INSPECTOR_BANNER_BLOCKED_PATHS, {}, { w->colours[1] }); - break; - } - default: - { - break; + default: + break; } } } -} -static void WindowTileInspectorScrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) -{ - const int32_t listWidth = w->widgets[WIDX_LIST].width(); - gfx_fill_rect( - dpi, { { dpi->x, dpi->y }, { dpi->x + dpi->width - 1, dpi->y + dpi->height - 1 } }, - ColourMapA[w->colours[1]].mid_light); - - // Show usage hint when nothing is selected - if (!windowTileInspectorTileSelected) + void OnScrollDraw(int32_t scrollIndex, rct_drawpixelinfo& dpi) override { - auto& listWidget = w->widgets[WIDX_LIST]; - auto centrePos = ScreenCoordsXY{ listWidget.width() / 2, - (listWidget.height() - font_get_line_height(FontSpriteBase::MEDIUM)) / 2 }; - auto ft = Formatter{}; - auto textPaint = TextPaint{ w->colours[1], TextAlignment::CENTRE }; - DrawTextWrapped(dpi, centrePos, listWidth, STR_TILE_INSPECTOR_SELECT_TILE_HINT, ft, textPaint); - return; - } + const int32_t listWidth = widgets[WIDX_LIST].width(); + gfx_fill_rect( + &dpi, { { dpi.x, dpi.y }, { dpi.x + dpi.width - 1, dpi.y + dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); - ScreenCoordsXY screenCoords{}; - screenCoords.y = SCROLLABLE_ROW_HEIGHT * (windowTileInspectorElementCount - 1); - int32_t i = 0; - char buffer[256]; - - const TileElement* tileElement = map_get_first_element_at(windowTileInspectorToolMap); - - do - { - if (tileElement == nullptr) - break; - - const bool selectedRow = i == windowTileInspectorSelectedIndex; - const bool hoveredRow = i == windowTileInspectorHighlightedIndex; - const char* typeName = ""; - - // Draw row background colour - auto fillRectangle = ScreenRect{ { 0, screenCoords.y }, { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; - if (selectedRow) + // Show usage hint when nothing is selected + if (!_tileSelected) { - gfx_fill_rect(dpi, fillRectangle, ColourMapA[w->colours[1]].mid_dark); + auto& listWidget = widgets[WIDX_LIST]; + auto centrePos = ScreenCoordsXY{ listWidget.width() / 2, + (listWidget.height() - font_get_line_height(FontSpriteBase::MEDIUM)) / 2 }; + auto ft = Formatter{}; + auto textPaint = TextPaint{ colours[1], TextAlignment::CENTRE }; + DrawTextWrapped(&dpi, centrePos, listWidth, STR_TILE_INSPECTOR_SELECT_TILE_HINT, ft, textPaint); + return; } - else if (hoveredRow) - { - gfx_fill_rect(dpi, fillRectangle, ColourMapA[w->colours[1]].mid_dark | 0x1000000); - } - else if (((windowTileInspectorElementCount - i) & 1) == 0) + + ScreenCoordsXY screenCoords{}; + screenCoords.y = SCROLLABLE_ROW_HEIGHT * (windowTileInspectorElementCount - 1); + int32_t i = 0; + char buffer[256]; + + const TileElement* tileElement = map_get_first_element_at(_toolMap); + + do { + if (tileElement == nullptr) + break; + + const bool selectedRow = i == windowTileInspectorSelectedIndex; + const bool hoveredRow = i == _highlightedIndex; + const char* typeName = ""; + + // Draw row background colour + auto fillRectangle = ScreenRect{ { 0, screenCoords.y }, { listWidth, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 } }; + if (selectedRow) + gfx_fill_rect(&dpi, fillRectangle, ColourMapA[colours[1]].mid_dark); + else if (hoveredRow) + gfx_fill_rect(&dpi, fillRectangle, ColourMapA[colours[1]].mid_dark | 0x1000000); // Zebra stripes - gfx_fill_rect(dpi, fillRectangle, ColourMapA[w->colours[1]].light | 0x1000000); - } + else if (((windowTileInspectorElementCount - i) & 1) == 0) + gfx_fill_rect(&dpi, fillRectangle, ColourMapA[colours[1]].light | 0x1000000); - const StringId stringFormat = (selectedRow || hoveredRow) ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; - auto checkboxFormatter = Formatter(); - checkboxFormatter.Add(STR_STRING); - checkboxFormatter.Add(CheckBoxMarkString); + const StringId stringFormat = (selectedRow || hoveredRow) ? STR_WHITE_STRING : STR_WINDOW_COLOUR_2_STRINGID; + auto checkboxFormatter = Formatter(); + checkboxFormatter.Add(STR_STRING); + checkboxFormatter.Add(CheckBoxMarkString); - // Draw checkbox and check if visible - gfx_fill_rect_inset(dpi, { { 2, screenCoords.y }, { 15, screenCoords.y + 11 } }, w->colours[1], INSET_RECT_F_E0); - if (!tileElement->IsInvisible()) + // Draw checkbox and check if visible + gfx_fill_rect_inset(&dpi, { { 2, screenCoords.y }, { 15, screenCoords.y + 11 } }, colours[1], INSET_RECT_F_E0); + if (!tileElement->IsInvisible()) + { + auto eyeFormatter = Formatter(); + eyeFormatter.Add(STR_STRING); + eyeFormatter.Add(EyeString); + DrawTextBasic(&dpi, screenCoords + ScreenCoordsXY{ 2, 1 }, stringFormat, eyeFormatter); + } + + const auto type = tileElement->GetType(); + switch (type) + { + case TileElementType::Surface: + typeName = language_get_string(STR_TILE_INSPECTOR_SURFACE); + break; + + case TileElementType::Path: + typeName = tileElement->AsPath()->IsQueue() ? language_get_string(STR_QUEUE_LINE_MAP_TIP) + : language_get_string(STR_FOOTPATH_MAP_TIP); + break; + + case TileElementType::Track: + typeName = language_get_string(STR_RIDE_COMPONENT_TRACK_CAPITALISED); + break; + + case TileElementType::SmallScenery: + { + const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); + snprintf( + buffer, sizeof(buffer), "%s (%s)", language_get_string(STR_OBJECT_SELECTION_SMALL_SCENERY), + sceneryEntry != nullptr ? language_get_string(sceneryEntry->name) : ""); + typeName = buffer; + break; + } + + case TileElementType::Entrance: + typeName = language_get_string(STR_RIDE_CONSTRUCTION_ENTRANCE); + break; + + case TileElementType::Wall: + { + const auto* entry = tileElement->AsWall()->GetEntry(); + snprintf( + buffer, sizeof(buffer), "%s (%s)", language_get_string(STR_TILE_INSPECTOR_WALL), + entry != nullptr ? language_get_string(entry->name) : ""); + typeName = buffer; + break; + } + + case TileElementType::LargeScenery: + typeName = language_get_string(STR_OBJECT_SELECTION_LARGE_SCENERY); + break; + + case TileElementType::Banner: + snprintf( + buffer, sizeof(buffer), "%s (%u)", language_get_string(STR_BANNER_WINDOW_TITLE), + tileElement->AsBanner()->GetIndex().ToUnderlying()); + typeName = buffer; + break; + + default: + snprintf(buffer, sizeof(buffer), "%s (%d)", language_get_string(STR_UNKNOWN_OBJECT_TYPE), EnumValue(type)); + typeName = buffer; + } + + const int32_t clearanceHeight = tileElement->clearance_height; + const bool ghost = tileElement->IsGhost(); + const bool last = tileElement->IsLastForTile(); + + // Element name + auto ft = Formatter(); + ft.Add(STR_STRING); + ft.Add(typeName); + DrawTextEllipsised( + &dpi, screenCoords + ScreenCoordsXY{ TypeColumnXY.x, 0 }, TypeColumnSize.width, stringFormat, ft); + + // Base height + ft = Formatter(); + ft.Add(STR_FORMAT_INTEGER); + ft.Add(tileElement->base_height); + DrawTextBasic(&dpi, screenCoords + ScreenCoordsXY{ BaseHeightColumnXY.x, 0 }, stringFormat, ft); + + // Clearance height + ft = Formatter(); + ft.Add(STR_FORMAT_INTEGER); + ft.Add(clearanceHeight); + DrawTextBasic(&dpi, screenCoords + ScreenCoordsXY{ ClearanceHeightColumnXY.x, 0 }, stringFormat, ft); + + // Direction + ft = Formatter(); + ft.Add(STR_FORMAT_INTEGER); + ft.Add(tileElement->GetDirection()); + DrawTextBasic(&dpi, screenCoords + ScreenCoordsXY{ DirectionColumnXY.x, 0 }, stringFormat, ft); + + // Checkmarks for ghost and last for tile + if (ghost) + DrawTextBasic(&dpi, screenCoords + ScreenCoordsXY{ GhostFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); + if (last) + DrawTextBasic(&dpi, screenCoords + ScreenCoordsXY{ LastFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); + + screenCoords.y -= SCROLLABLE_ROW_HEIGHT; + i++; + } while (!(tileElement++)->IsLastForTile()); + } + + void ClearClipboard() + { + _elementCopied = false; + } + +private: + void SetPage(const TileInspectorPage p) + { + Invalidate(); + // subtract current page height, then add new page height + if (tileInspectorPage != TileInspectorPage::Default) { - auto eyeFormatter = Formatter(); - eyeFormatter.Add(STR_STRING); - eyeFormatter.Add(EyeString); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ 2, 1 }, stringFormat, eyeFormatter); + auto index = EnumValue(tileInspectorPage) - 1; + height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + min_height -= PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + } + if (p != TileInspectorPage::Default) + { + auto index = EnumValue(p) - 1; + height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + min_height += PageGroupBoxSettings[index].details_top_offset - GROUPBOX_PADDING - 3; + } + tileInspectorPage = p; + auto pageIndex = EnumValue(p); + widgets = PageWidgets[pageIndex]; + hold_down_widgets = PageHoldDownWidgets[pageIndex]; + disabled_widgets = PageDisabledWidgets[pageIndex]; + pressed_widgets = 0; + Invalidate(); + } + + void UpdateSelectedTile(const ScreenCoordsXY& screenCoords) + { + const bool ctrlIsHeldDown = InputTestPlaceObjectModifier(PLACE_OBJECT_MODIFIER_COPY_Z); + // Mouse hasn't moved + if (screenCoords.x == _toolMouseX && screenCoords.y == _toolMouseY && _toolCtrlDown == ctrlIsHeldDown) + return; + + _toolMouseX = screenCoords.x; + _toolMouseY = screenCoords.y; + _toolCtrlDown = ctrlIsHeldDown; + + CoordsXY mapCoords{}; + TileElement* clickedElement = nullptr; + if (ctrlIsHeldDown) + { + auto info = get_map_coordinates_from_pos(screenCoords, ViewportInteractionFlags); + clickedElement = info.Element; + mapCoords = info.Loc; } - const auto type = tileElement->GetType(); - switch (type) + // Even if Ctrl was pressed, fall back to normal selection when there was nothing under the cursor + if (clickedElement == nullptr) + { + auto mouseCoords = screen_pos_to_map_pos(screenCoords, nullptr); + if (!mouseCoords.has_value()) + return; + + mapCoords = mouseCoords.value(); + // Tile is already selected + if (_tileSelected && mapCoords.x == _toolMap.x && mapCoords.y == _toolMap.y) + return; + } + + _tileSelected = true; + _toolMap = mapCoords; + windowTileInspectorTile = TileCoordsXY(mapCoords); + OpenRCT2::TileInspector::SetSelectedElement(clickedElement); + LoadTile(clickedElement); + } + + void SelectElementFromList(int32_t index) + { + if (index < 0 || index >= windowTileInspectorElementCount) + { + windowTileInspectorSelectedIndex = -1; + OpenRCT2::TileInspector::SetSelectedElement(nullptr); + } + else + { + windowTileInspectorSelectedIndex = index; + const TileElement* const tileElement = GetSelectedElement(); + OpenRCT2::TileInspector::SetSelectedElement(tileElement); + } + Invalidate(); + } + + void LoadTile(TileElement* elementToSelect) + { + windowTileInspectorSelectedIndex = -1; + scrolls[0].v_top = 0; + + TileElement* element = map_get_first_element_at(_toolMap); + int16_t numItems = 0; + do + { + if (element == nullptr) + break; + if (element == elementToSelect) + windowTileInspectorSelectedIndex = numItems; + + numItems++; + } while (!(element++)->IsLastForTile()); + windowTileInspectorElementCount = numItems; + Invalidate(); + } + + void RemoveElement(int32_t elementIndex) + { + openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyRemove, elementIndex); + GameActions::Execute(&modifyTile); + } + + void RotateElement(int32_t elementIndex) + { + openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyRotate, elementIndex); + GameActions::Execute(&modifyTile); + } + + // Swap element with its parent + void SwapElements(int16_t first, int16_t second) + { + bool firstInRange = first >= 0 && first < windowTileInspectorElementCount; + bool secondInRange = second >= 0 && second < windowTileInspectorElementCount; + // This might happen if two people are modifying the same tile. + if (!firstInRange || !secondInRange) + return; + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnySwap, first, second); + GameActions::Execute(&modifyTile); + } + + void SortElements() + { + openrct2_assert(_tileSelected, "No tile selected"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnySort); + GameActions::Execute(&modifyTile); + } + + void CopyElement() + { + // Copy value, in case the element gets moved + _copiedElement = *GetSelectedElement(); + _elementCopied = true; + Invalidate(); + } + + void PasteElement() + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyPaste, 0, 0, _copiedElement); + GameActions::Execute(&modifyTile); + } + + void BaseHeightOffset(int16_t elementIndex, int8_t heightOffset) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyBaseHeightOffset, elementIndex, heightOffset); + GameActions::Execute(&modifyTile); + } + + void SurfaceShowParkFences(bool showFences) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceShowParkFences, showFences); + GameActions::Execute(&modifyTile); + } + + void SurfaceToggleCorner(int32_t cornerIndex) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceToggleCorner, cornerIndex); + GameActions::Execute(&modifyTile); + } + + void SurfaceToggleDiagonal() + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::SurfaceToggleDiagonal); + GameActions::Execute(&modifyTile); + } + + void PathSetSloped(int32_t elementIndex, bool sloped) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathSetSlope, elementIndex, sloped); + GameActions::Execute(&modifyTile); + } + + void PathSetBroken(int32_t elementIndex, bool broken) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathSetBroken, elementIndex, broken); + GameActions::Execute(&modifyTile); + } + + void PathToggleEdge(int32_t elementIndex, int32_t cornerIndex) + { + openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + openrct2_assert(cornerIndex >= 0 && cornerIndex < 8, "cornerIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::PathToggleEdge, elementIndex, cornerIndex); + GameActions::Execute(&modifyTile); + } + + void EntranceMakeUsable(int32_t elementIndex) + { + Guard::ArgumentInRange(elementIndex, 0, windowTileInspectorElementCount - 1); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::EntranceMakeUsable, elementIndex); + GameActions::Execute(&modifyTile); + } + + void WallSetSlope(int32_t elementIndex, int32_t slopeValue) + { + // Make sure only the correct bits are set + openrct2_assert((slopeValue & 3) == slopeValue, "slopeValue doesn't match its mask"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::WallSetSlope, elementIndex, slopeValue); + GameActions::Execute(&modifyTile); + } + + void WallAnimationFrameOffset(int16_t elementIndex, int8_t animationFrameOffset) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::WallSetAnimationFrame, elementIndex, animationFrameOffset); + GameActions::Execute(&modifyTile); + } + + void TrackBlockHeightOffset(int32_t elementIndex, int8_t heightOffset) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackBaseHeightOffset, elementIndex, heightOffset); + GameActions::Execute(&modifyTile); + } + + void TrackBlockSetLift(int32_t elementIndex, bool entireTrackBlock, bool chain) + { + auto modifyTile = TileModifyAction( + _toolMap, entireTrackBlock ? TileModifyType::TrackSetChainBlock : TileModifyType::TrackSetChain, elementIndex, + chain); + GameActions::Execute(&modifyTile); + } + + void TrackSetBlockBrake(int32_t elementIndex, bool blockBrake) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackSetBlockBrake, elementIndex, blockBrake); + GameActions::Execute(&modifyTile); + } + + void TrackSetIndestructible(int32_t elementIndex, bool isIndestructible) + { + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::TrackSetIndestructible, elementIndex, isIndestructible); + GameActions::Execute(&modifyTile); + } + + void QuarterTileSet(int32_t elementIndex, const int32_t quarterIndex) + { + // quarterIndex is widget index relative to WIDX_SCENERY_CHECK_QUARTER_N, so a value from 0-3 + openrct2_assert(quarterIndex >= 0 && quarterIndex < 4, "quarterIndex out of range"); + auto modifyTile = TileModifyAction( + _toolMap, TileModifyType::ScenerySetQuarterLocation, elementIndex, (quarterIndex - get_current_rotation()) & 3); + GameActions::Execute(&modifyTile); + } + + // ToggleQuadrantCollision? + void ToggleQuadrantCollosion(int32_t elementIndex, const int32_t quadrantIndex) + { + auto modifyTile = TileModifyAction( + _toolMap, TileModifyType::ScenerySetQuarterCollision, elementIndex, + (quadrantIndex + 2 - get_current_rotation()) & 3); + GameActions::Execute(&modifyTile); + } + + void BannerToggleBlock(int32_t elementIndex, int32_t edgeIndex) + { + openrct2_assert(edgeIndex >= 0 && edgeIndex < 4, "edgeIndex out of range"); + // Make edgeIndex abstract + edgeIndex = (edgeIndex - get_current_rotation()) & 3; + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::BannerToggleBlockingEdge, elementIndex, edgeIndex); + GameActions::Execute(&modifyTile); + } + + void ToggleInvisibility(int32_t elementIndex) + { + openrct2_assert(elementIndex >= 0 && elementIndex < windowTileInspectorElementCount, "elementIndex out of range"); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyToggleInvisilibity, elementIndex); + GameActions::Execute(&modifyTile); + } + + TileElement* GetSelectedElement() + { + openrct2_assert( + windowTileInspectorSelectedIndex >= 0 && windowTileInspectorSelectedIndex < windowTileInspectorElementCount, + "Selected list item out of range"); + return map_get_first_element_at(_toolMap) + windowTileInspectorSelectedIndex; + } + + void OnPrepareDraw() override + { + // Set the correct page automatically + TileInspectorPage p = TileInspectorPage::Default; + if (windowTileInspectorSelectedIndex != -1) + { + const auto element = GetSelectedElement(); + switch (element->GetType()) + { + default: + case TileElementType::Surface: + p = TileInspectorPage::Surface; + break; + + case TileElementType::Path: + p = TileInspectorPage::Path; + break; + + case TileElementType::Track: + p = TileInspectorPage::Track; + break; + + case TileElementType::SmallScenery: + p = TileInspectorPage::Scenery; + break; + + case TileElementType::Entrance: + p = TileInspectorPage::Entrance; + break; + + case TileElementType::Wall: + p = TileInspectorPage::Wall; + break; + + case TileElementType::LargeScenery: + p = TileInspectorPage::LargeScenery; + break; + + case TileElementType::Banner: + p = TileInspectorPage::Banner; + break; + } + } + + if (tileInspectorPage != p) + { + SetPage(p); + Invalidate(); + } + // X and Y spinners + SetWidgetDisabled(WIDX_SPINNER_X_INCREASE, !(_tileSelected && ((_toolMap.x / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); + SetWidgetDisabled(WIDX_SPINNER_X_DECREASE, !(_tileSelected && ((_toolMap.x / 32) > 0))); + SetWidgetDisabled(WIDX_SPINNER_Y_INCREASE, !(_tileSelected && ((_toolMap.y / 32) < MAXIMUM_MAP_SIZE_TECHNICAL - 1))); + SetWidgetDisabled(WIDX_SPINNER_Y_DECREASE, !(_tileSelected && ((_toolMap.y / 32) > 0))); + + // Sort buttons + SetWidgetDisabled(WIDX_BUTTON_SORT, !(_tileSelected && windowTileInspectorElementCount > 1)); + + // Move Up button + SetWidgetDisabled( + WIDX_BUTTON_MOVE_UP, + !(windowTileInspectorSelectedIndex != -1 + && windowTileInspectorSelectedIndex < windowTileInspectorElementCount - 1)); + InvalidateWidget(WIDX_BUTTON_MOVE_UP); + + // Move Down button + SetWidgetDisabled(WIDX_BUTTON_MOVE_DOWN, !(windowTileInspectorSelectedIndex > 0)); + InvalidateWidget(WIDX_BUTTON_MOVE_DOWN); + + // Copy button + SetWidgetDisabled(WIDX_BUTTON_COPY, !(windowTileInspectorSelectedIndex >= 0)); + InvalidateWidget(WIDX_BUTTON_COPY); + + // Paste button + SetWidgetDisabled(WIDX_BUTTON_PASTE, !(_tileSelected && _elementCopied)); + InvalidateWidget(WIDX_BUTTON_PASTE); + + widgets[WIDX_BACKGROUND].bottom = height - 1; + + if (tileInspectorPage == TileInspectorPage::Default) + { + widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Empty; + widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Empty; + widgets[WIDX_LIST].bottom = height - PADDING_BOTTOM; + } + else + { + widgets[WIDX_GROUPBOX_DETAILS].type = WindowWidgetType::Groupbox; + widgets[WIDX_GROUPBOX_PROPERTIES].type = WindowWidgetType::Groupbox; + auto pageIndex = EnumValue(tileInspectorPage) - 1; + widgets[WIDX_GROUPBOX_DETAILS].text = PageGroupBoxSettings[pageIndex].string_id; + widgets[WIDX_GROUPBOX_DETAILS].top = height - PageGroupBoxSettings[pageIndex].details_top_offset; + widgets[WIDX_GROUPBOX_DETAILS].bottom = height - PageGroupBoxSettings[pageIndex].details_bottom_offset; + widgets[WIDX_GROUPBOX_PROPERTIES].top = height - PageGroupBoxSettings[pageIndex].properties_top_offset; + widgets[WIDX_GROUPBOX_PROPERTIES].bottom = height - PageGroupBoxSettings[pageIndex].properties_bottom_offset; + widgets[WIDX_LIST].bottom = widgets[WIDX_GROUPBOX_DETAILS].top - GROUPBOX_PADDING; + } + + // The default page doesn't need further invalidation + if (tileInspectorPage == TileInspectorPage::Default) + return; + + // Using a switch, because I don't think giving each page their own callbacks is + // needed here, as only the mouseup and invalidate functions are different. + const int32_t propertiesAnchor = widgets[WIDX_GROUPBOX_PROPERTIES].top; + const TileElement* const tileElement = GetSelectedElement(); + if (tileElement == nullptr) + return; + + switch (tileElement->GetType()) { case TileElementType::Surface: - typeName = language_get_string(STR_TILE_INSPECTOR_SURFACE); + widgets[WIDX_SURFACE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_SURFACE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SURFACE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_SURFACE_BUTTON_REMOVE_FENCES].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_SURFACE_BUTTON_RESTORE_FENCES].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_SURFACE_CHECK_CORNER_N].top = GBBT(propertiesAnchor, 2) + 7 * 0; + widgets[WIDX_SURFACE_CHECK_CORNER_N].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_N].top + 13; + widgets[WIDX_SURFACE_CHECK_CORNER_E].top = GBBT(propertiesAnchor, 2) + 7 * 1; + widgets[WIDX_SURFACE_CHECK_CORNER_E].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_E].top + 13; + widgets[WIDX_SURFACE_CHECK_CORNER_S].top = GBBT(propertiesAnchor, 2) + 7 * 2; + widgets[WIDX_SURFACE_CHECK_CORNER_S].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_S].top + 13; + widgets[WIDX_SURFACE_CHECK_CORNER_W].top = GBBT(propertiesAnchor, 2) + 7 * 1; + widgets[WIDX_SURFACE_CHECK_CORNER_W].bottom = widgets[WIDX_SURFACE_CHECK_CORNER_W].top + 13; + widgets[WIDX_SURFACE_CHECK_DIAGONAL].top = GBBT(propertiesAnchor, 3) + 7 * 1; + widgets[WIDX_SURFACE_CHECK_DIAGONAL].bottom = widgets[WIDX_SURFACE_CHECK_DIAGONAL].top + 13; + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_N, + tileElement->AsSurface()->GetSlope() & (1 << ((2 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_E, + tileElement->AsSurface()->GetSlope() & (1 << ((3 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_S, + tileElement->AsSurface()->GetSlope() & (1 << ((0 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_CORNER_W, + tileElement->AsSurface()->GetSlope() & (1 << ((1 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_SURFACE_CHECK_DIAGONAL, tileElement->AsSurface()->GetSlope() & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT); break; + case TileElementType::Path: - typeName = tileElement->AsPath()->IsQueue() ? language_get_string(STR_QUEUE_LINE_MAP_TIP) - : language_get_string(STR_FOOTPATH_MAP_TIP); + widgets[WIDX_PATH_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_PATH_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_PATH_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_PATH_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_PATH_CHECK_BROKEN].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_PATH_CHECK_BROKEN].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_PATH_CHECK_SLOPED].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_PATH_CHECK_SLOPED].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_PATH_CHECK_EDGE_N].top = GBBT(propertiesAnchor, 3) + 7 * 0; + widgets[WIDX_PATH_CHECK_EDGE_N].bottom = widgets[WIDX_PATH_CHECK_EDGE_N].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_NE].top = GBBT(propertiesAnchor, 3) + 7 * 1; + widgets[WIDX_PATH_CHECK_EDGE_NE].bottom = widgets[WIDX_PATH_CHECK_EDGE_NE].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_E].top = GBBT(propertiesAnchor, 3) + 7 * 2; + widgets[WIDX_PATH_CHECK_EDGE_E].bottom = widgets[WIDX_PATH_CHECK_EDGE_E].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_SE].top = GBBT(propertiesAnchor, 3) + 7 * 3; + widgets[WIDX_PATH_CHECK_EDGE_SE].bottom = widgets[WIDX_PATH_CHECK_EDGE_SE].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_S].top = GBBT(propertiesAnchor, 3) + 7 * 4; + widgets[WIDX_PATH_CHECK_EDGE_S].bottom = widgets[WIDX_PATH_CHECK_EDGE_S].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_SW].top = GBBT(propertiesAnchor, 3) + 7 * 3; + widgets[WIDX_PATH_CHECK_EDGE_SW].bottom = widgets[WIDX_PATH_CHECK_EDGE_SW].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_W].top = GBBT(propertiesAnchor, 3) + 7 * 2; + widgets[WIDX_PATH_CHECK_EDGE_W].bottom = widgets[WIDX_PATH_CHECK_EDGE_W].top + 13; + widgets[WIDX_PATH_CHECK_EDGE_NW].top = GBBT(propertiesAnchor, 3) + 7 * 1; + widgets[WIDX_PATH_CHECK_EDGE_NW].bottom = widgets[WIDX_PATH_CHECK_EDGE_NW].top + 13; + SetCheckboxValue(WIDX_PATH_CHECK_SLOPED, tileElement->AsPath()->IsSloped()); + SetCheckboxValue(WIDX_PATH_CHECK_BROKEN, tileElement->AsPath()->IsBroken()); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_NE, tileElement->AsPath()->GetEdges() & (1 << ((0 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_SE, tileElement->AsPath()->GetEdges() & (1 << ((1 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_SW, tileElement->AsPath()->GetEdges() & (1 << ((2 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_NW, tileElement->AsPath()->GetEdges() & (1 << ((3 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_E, tileElement->AsPath()->GetCorners() & (1 << ((0 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_S, tileElement->AsPath()->GetCorners() & (1 << ((1 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_W, tileElement->AsPath()->GetCorners() & (1 << ((2 - get_current_rotation()) & 3))); + SetCheckboxValue( + WIDX_PATH_CHECK_EDGE_N, tileElement->AsPath()->GetCorners() & (1 << ((3 - get_current_rotation()) & 3))); break; + case TileElementType::Track: - typeName = language_get_string(STR_RIDE_COMPONENT_TRACK_CAPITALISED); + widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].top = GBBT(propertiesAnchor, 0); + widgets[WIDX_TRACK_CHECK_APPLY_TO_ALL].bottom = GBBB(propertiesAnchor, 0); + widgets[WIDX_TRACK_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 1) + 3; + widgets[WIDX_TRACK_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 1) - 3; + widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 1) + 4; + widgets[WIDX_TRACK_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 1) - 4; + widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 1) + 4; + widgets[WIDX_TRACK_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 1) - 4; + widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_TRACK_CHECK_CHAIN_LIFT].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].top = GBBT(propertiesAnchor, 3); + widgets[WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED].bottom = GBBB(propertiesAnchor, 3); + widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].top = GBBT(propertiesAnchor, 4); + widgets[WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE].bottom = GBBB(propertiesAnchor, 4); + SetCheckboxValue(WIDX_TRACK_CHECK_APPLY_TO_ALL, _applyToAll); + SetCheckboxValue(WIDX_TRACK_CHECK_CHAIN_LIFT, tileElement->AsTrack()->HasChain()); + SetCheckboxValue(WIDX_TRACK_CHECK_BLOCK_BRAKE_CLOSED, tileElement->AsTrack()->BlockBrakeClosed()); + SetCheckboxValue(WIDX_TRACK_CHECK_IS_INDESTRUCTIBLE, tileElement->AsTrack()->IsIndestructible()); break; + case TileElementType::SmallScenery: { - const auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); - snprintf( - buffer, sizeof(buffer), "%s (%s)", language_get_string(STR_OBJECT_SELECTION_SMALL_SCENERY), - sceneryEntry != nullptr ? language_get_string(sceneryEntry->name) : ""); - typeName = buffer; + // Raise / Lower + widgets[WIDX_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + + // Quadrant checkboxes + widgets[WIDX_SCENERY_CHECK_QUARTER_N].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 0; + widgets[WIDX_SCENERY_CHECK_QUARTER_N].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_N].top + 13; + widgets[WIDX_SCENERY_CHECK_QUARTER_E].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_QUARTER_E].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_E].top + 13; + widgets[WIDX_SCENERY_CHECK_QUARTER_S].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 2; + widgets[WIDX_SCENERY_CHECK_QUARTER_S].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_S].top + 13; + widgets[WIDX_SCENERY_CHECK_QUARTER_W].top = GBBT(propertiesAnchor, 1) - 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_QUARTER_W].bottom = widgets[WIDX_SCENERY_CHECK_QUARTER_W].top + 13; + // This gets the relative rotation, by subtracting the camera's rotation, and wrapping it between 0-3 inclusive + bool N = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((0 - get_current_rotation()) & 3); + bool E = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((1 - get_current_rotation()) & 3); + bool S = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((2 - get_current_rotation()) & 3); + bool W = tileElement->AsSmallScenery()->GetSceneryQuadrant() == ((3 - get_current_rotation()) & 3); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_N, N); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_E, E); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_S, S); + SetCheckboxValue(WIDX_SCENERY_CHECK_QUARTER_W, W); + + // Collision checkboxes + widgets[WIDX_SCENERY_CHECK_COLLISION_N].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 0; + widgets[WIDX_SCENERY_CHECK_COLLISION_N].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_N].top + 13; + widgets[WIDX_SCENERY_CHECK_COLLISION_E].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_COLLISION_E].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_E].top + 13; + widgets[WIDX_SCENERY_CHECK_COLLISION_S].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 2; + widgets[WIDX_SCENERY_CHECK_COLLISION_S].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_S].top + 13; + widgets[WIDX_SCENERY_CHECK_COLLISION_W].top = GBBT(propertiesAnchor, 2) + 5 + 7 * 1; + widgets[WIDX_SCENERY_CHECK_COLLISION_W].bottom = widgets[WIDX_SCENERY_CHECK_COLLISION_W].top + 13; + auto occupiedQuadrants = tileElement->GetOccupiedQuadrants(); + N = (occupiedQuadrants & (1 << ((2 - get_current_rotation()) & 3))) != 0; + E = (occupiedQuadrants & (1 << ((3 - get_current_rotation()) & 3))) != 0; + S = (occupiedQuadrants & (1 << ((0 - get_current_rotation()) & 3))) != 0; + W = (occupiedQuadrants & (1 << ((1 - get_current_rotation()) & 3))) != 0; + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_N, N); + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_E, E); + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_S, S); + SetCheckboxValue(WIDX_SCENERY_CHECK_COLLISION_W, W); break; } + case TileElementType::Entrance: - typeName = language_get_string(STR_RIDE_CONSTRUCTION_ENTRANCE); + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_ENTRANCE_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_ENTRANCE_BUTTON_MAKE_USABLE].bottom = GBBB(propertiesAnchor, 1); + SetWidgetDisabled( + WIDX_ENTRANCE_BUTTON_MAKE_USABLE, + !(tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)); break; + case TileElementType::Wall: { - const auto* entry = tileElement->AsWall()->GetEntry(); - snprintf( - buffer, sizeof(buffer), "%s (%s)", language_get_string(STR_TILE_INSPECTOR_WALL), - entry != nullptr ? language_get_string(entry->name) : ""); - typeName = buffer; + bool canBeSloped = false; + bool hasAnimation = false; + const auto wallEntry = tileElement->AsWall()->GetEntry(); + if (wallEntry != nullptr) + { + canBeSloped = !(wallEntry->flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE); + hasAnimation = wallEntry->flags & WALL_SCENERY_IS_DOOR; + } + + widgets[WIDX_WALL_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_WALL_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_WALL_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_WALL_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_WALL_DROPDOWN_SLOPE].top = GBBT(propertiesAnchor, 1) + 3; + widgets[WIDX_WALL_DROPDOWN_SLOPE].bottom = GBBB(propertiesAnchor, 1) - 3; + widgets[WIDX_WALL_DROPDOWN_SLOPE].text = WallSlopeStringIds[tileElement->AsWall()->GetSlope()]; + widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].top = GBBT(propertiesAnchor, 1) + 4; + widgets[WIDX_WALL_DROPDOWN_SLOPE_BUTTON].bottom = GBBB(propertiesAnchor, 1) - 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].top = GBBT(propertiesAnchor, 2) + 3; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME].bottom = GBBB(propertiesAnchor, 2) - 3; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].top = GBBT(propertiesAnchor, 2) + 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE].bottom = GBBB(propertiesAnchor, 2) - 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].top = GBBT(propertiesAnchor, 2) + 4; + widgets[WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE].bottom = GBBB(propertiesAnchor, 2) - 4; + + // Wall slope dropdown + SetWidgetDisabled(WIDX_WALL_DROPDOWN_SLOPE, !canBeSloped); + InvalidateWidget(WIDX_WALL_DROPDOWN_SLOPE); + SetWidgetDisabled(WIDX_WALL_DROPDOWN_SLOPE_BUTTON, !canBeSloped); + InvalidateWidget(WIDX_WALL_DROPDOWN_SLOPE_BUTTON); + // Wall animation frame spinner + SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME, !hasAnimation); + SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME_INCREASE, !hasAnimation); + SetWidgetDisabled(WIDX_WALL_SPINNER_ANIMATION_FRAME_DECREASE, !hasAnimation); break; } + case TileElementType::LargeScenery: - typeName = language_get_string(STR_OBJECT_SELECTION_LARGE_SCENERY); + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_LARGE_SCENERY_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; break; + case TileElementType::Banner: - snprintf( - buffer, sizeof(buffer), "%s (%u)", language_get_string(STR_BANNER_WINDOW_TITLE), - tileElement->AsBanner()->GetIndex().ToUnderlying()); - typeName = buffer; + widgets[WIDX_BANNER_SPINNER_HEIGHT].top = GBBT(propertiesAnchor, 0) + 3; + widgets[WIDX_BANNER_SPINNER_HEIGHT].bottom = GBBB(propertiesAnchor, 0) - 3; + widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_BANNER_SPINNER_HEIGHT_INCREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].top = GBBT(propertiesAnchor, 0) + 4; + widgets[WIDX_BANNER_SPINNER_HEIGHT_DECREASE].bottom = GBBB(propertiesAnchor, 0) - 4; + widgets[WIDX_BANNER_CHECK_BLOCK_NE].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_BANNER_CHECK_BLOCK_NE].bottom = GBBB(propertiesAnchor, 1); + widgets[WIDX_BANNER_CHECK_BLOCK_SE].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_SE].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_SW].top = GBBT(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_SW].bottom = GBBB(propertiesAnchor, 2); + widgets[WIDX_BANNER_CHECK_BLOCK_NW].top = GBBT(propertiesAnchor, 1); + widgets[WIDX_BANNER_CHECK_BLOCK_NW].bottom = GBBB(propertiesAnchor, 1); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_NE, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((0 - get_current_rotation()) & 3)))); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_SE, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((1 - get_current_rotation()) & 3)))); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_SW, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((2 - get_current_rotation()) & 3)))); + SetCheckboxValue( + WIDX_BANNER_CHECK_BLOCK_NW, + (tileElement->AsBanner()->GetAllowedEdges() & (1 << ((3 - get_current_rotation()) & 3)))); break; + default: - snprintf(buffer, sizeof(buffer), "%s (%d)", language_get_string(STR_UNKNOWN_OBJECT_TYPE), EnumValue(type)); - typeName = buffer; + break; // Nothing. } + } +}; - const int32_t clearanceHeight = tileElement->clearance_height; - const bool ghost = tileElement->IsGhost(); - const bool last = tileElement->IsLastForTile(); - - // Element name - auto ft = Formatter(); - ft.Add(STR_STRING); - ft.Add(typeName); - DrawTextEllipsised(dpi, screenCoords + ScreenCoordsXY{ TypeColumnXY.x, 0 }, TypeColumnSize.width, stringFormat, ft); - - // Base height - ft = Formatter(); - ft.Add(STR_FORMAT_INTEGER); - ft.Add(tileElement->base_height); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ BaseHeightColumnXY.x, 0 }, stringFormat, ft); - - // Clearance height - ft = Formatter(); - ft.Add(STR_FORMAT_INTEGER); - ft.Add(clearanceHeight); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ ClearanceHeightColumnXY.x, 0 }, stringFormat, ft); - - // Direction - ft = Formatter(); - ft.Add(STR_FORMAT_INTEGER); - ft.Add(tileElement->GetDirection()); - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ DirectionColumnXY.x, 0 }, stringFormat, ft); - - // Checkmarks for ghost and last for tile - if (ghost) - { - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ GhostFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); - } - if (last) - { - DrawTextBasic(dpi, screenCoords + ScreenCoordsXY{ LastFlagColumnXY.x, 0 }, stringFormat, checkboxFormatter); - } - - screenCoords.y -= SCROLLABLE_ROW_HEIGHT; - i++; - } while (!(tileElement++)->IsLastForTile()); +rct_window* WindowTileInspectorOpen() +{ + rct_window* window = window_bring_to_front_by_class(WindowClass::TileInspector); + if (window == nullptr) + window = WindowCreate(WindowClass::TileInspector, ScreenCoordsXY(0, 29), WW, WH, WF_RESIZABLE); + return window; +} + +void WindowTileInspectorClearClipboard() +{ + auto* window = window_find_by_class(WindowClass::TileInspector); + if (window != nullptr) + static_cast(window)->ClearClipboard(); }