From fea69cc86c431497fb62c1eb1de4f6a5669b5e06 Mon Sep 17 00:00:00 2001 From: duncanspumpkin Date: Thu, 30 Sep 2021 17:47:49 +0100 Subject: [PATCH] Backport scenery window from NSF Co-authored-by: IntelOrca Co-authored-by: Gymnasiast --- src/openrct2-ui/windows/Scenery.cpp | 1263 +++++++++++----------- src/openrct2-ui/windows/TopToolbar.cpp | 16 +- src/openrct2-ui/windows/Window.h | 4 +- src/openrct2/interface/Window.h | 12 +- src/openrct2/interface/Window_internal.h | 1 - src/openrct2/world/Scenery.cpp | 111 ++ src/openrct2/world/Scenery.h | 7 + src/openrct2/world/ScenerySelection.h | 18 +- 8 files changed, 769 insertions(+), 663 deletions(-) diff --git a/src/openrct2-ui/windows/Scenery.cpp b/src/openrct2-ui/windows/Scenery.cpp index d32e3b3679..26a447d234 100644 --- a/src/openrct2-ui/windows/Scenery.cpp +++ b/src/openrct2-ui/windows/Scenery.cpp @@ -31,42 +31,8 @@ constexpr int32_t WINDOW_SCENERY_WIDTH = 634; constexpr int32_t WINDOW_SCENERY_HEIGHT = 180; constexpr int32_t SCENERY_BUTTON_WIDTH = 66; constexpr int32_t SCENERY_BUTTON_HEIGHT = 80; -constexpr int32_t SCENERY_WINDOW_TABS = MAX_SCENERY_GROUP_OBJECTS + 1; // The + 1 is for the 'Miscellaneous' tab -constexpr uint16_t SCENERY_ENTRIES_PER_TAB = 1024; // clang-format off -enum { - WINDOW_SCENERY_TAB_1, - WINDOW_SCENERY_TAB_2, - WINDOW_SCENERY_TAB_3, - WINDOW_SCENERY_TAB_4, - WINDOW_SCENERY_TAB_5, - WINDOW_SCENERY_TAB_6, - WINDOW_SCENERY_TAB_7, - WINDOW_SCENERY_TAB_8, - WINDOW_SCENERY_TAB_9, - WINDOW_SCENERY_TAB_10, - WINDOW_SCENERY_TAB_11, - WINDOW_SCENERY_TAB_12, - WINDOW_SCENERY_TAB_13, - WINDOW_SCENERY_TAB_14, - WINDOW_SCENERY_TAB_15, - WINDOW_SCENERY_TAB_16, - WINDOW_SCENERY_TAB_17, - WINDOW_SCENERY_TAB_18, - WINDOW_SCENERY_TAB_19, - WINDOW_SCENERY_TAB_20 -}; - -ScenerySelection gWindowSceneryTabSelections[SCENERY_WINDOW_TABS]; -uint8_t gWindowSceneryActiveTabIndex; -uint8_t gWindowSceneryPaintEnabled; -uint8_t gWindowSceneryRotation; -colour_t gWindowSceneryPrimaryColour; -colour_t gWindowScenerySecondaryColour; -colour_t gWindowSceneryTertiaryColour; -bool gWindowSceneryEyedropperEnabled = false; - static void window_scenery_close(rct_window *w); static void window_scenery_mouseup(rct_window *w, rct_widgetindex widgetIndex); static void window_scenery_resize(rct_window *w); @@ -102,38 +68,19 @@ static rct_window_event_list window_scenery_events([](auto& events) enum WINDOW_SCENERY_LIST_WIDGET_IDX { - WIDX_SCENERY_BACKGROUND, // 1 - WIDX_SCENERY_TITLE, // 2 - WIDX_SCENERY_CLOSE, // 4 - WIDX_SCENERY_TAB_CONTENT_PANEL, // 8 - WIDX_SCENERY_TAB_1, // 10 - WIDX_SCENERY_TAB_2, // 20 - WIDX_SCENERY_TAB_3, // 40 - WIDX_SCENERY_TAB_4, // 80 - WIDX_SCENERY_TAB_5, // 100 - WIDX_SCENERY_TAB_6, // 200 - WIDX_SCENERY_TAB_7, // 400 - WIDX_SCENERY_TAB_8, // 800 - WIDX_SCENERY_TAB_9, // 1000 - WIDX_SCENERY_TAB_10, // 2000 - WIDX_SCENERY_TAB_11, // 4000 - WIDX_SCENERY_TAB_12, // 8000 - WIDX_SCENERY_TAB_13, // 10000 - WIDX_SCENERY_TAB_14, // 20000 - WIDX_SCENERY_TAB_15, // 40000 - WIDX_SCENERY_TAB_16, // 80000 - WIDX_SCENERY_TAB_17, // 100000 - WIDX_SCENERY_TAB_18, // 200000 - WIDX_SCENERY_TAB_19, // 400000 - WIDX_SCENERY_TAB_20, // 800000 - WIDX_SCENERY_LIST, // 1000000 - WIDX_SCENERY_ROTATE_OBJECTS_BUTTON, // 2000000 - WIDX_SCENERY_REPAINT_SCENERY_BUTTON, // 4000000 - WIDX_SCENERY_PRIMARY_COLOUR_BUTTON, // 8000000 - WIDX_SCENERY_SECONDARY_COLOUR_BUTTON, // 10000000 - WIDX_SCENERY_TERTIARY_COLOUR_BUTTON, // 20000000 - WIDX_SCENERY_EYEDROPPER_BUTTON, // 40000000 - WIDX_SCENERY_BUILD_CLUSTER_BUTTON, // 80000000 + WIDX_SCENERY_BACKGROUND, + WIDX_SCENERY_TITLE, + WIDX_SCENERY_CLOSE, + WIDX_SCENERY_TAB_CONTENT_PANEL, + WIDX_SCENERY_LIST, + WIDX_SCENERY_ROTATE_OBJECTS_BUTTON, + WIDX_SCENERY_REPAINT_SCENERY_BUTTON, + WIDX_SCENERY_PRIMARY_COLOUR_BUTTON, + WIDX_SCENERY_SECONDARY_COLOUR_BUTTON, + WIDX_SCENERY_TERTIARY_COLOUR_BUTTON, + WIDX_SCENERY_EYEDROPPER_BUTTON, + WIDX_SCENERY_BUILD_CLUSTER_BUTTON, + WIDX_SCENERY_TAB_1, }; validate_global_widx(WC_SCENERY, WIDX_SCENERY_TAB_1); @@ -143,26 +90,6 @@ validate_global_widx(WC_SCENERY, WIDX_SCENERY_EYEDROPPER_BUTTON); static rct_widget window_scenery_widgets[] = { WINDOW_SHIM(WINDOW_TITLE, WINDOW_SCENERY_WIDTH, WINDOW_SCENERY_HEIGHT), MakeWidget ({ 0, 43}, {634, 99}, WindowWidgetType::Resize, WindowColour::Secondary ), // 8 0x009DE2C8 - MakeTab ({ 3, 17}, STR_STRING_DEFINED_TOOLTIP ), // 10 0x009DE2D8 - MakeTab ({ 34, 17}, STR_STRING_DEFINED_TOOLTIP ), // 20 0x009DE2E8 - MakeTab ({ 65, 17}, STR_STRING_DEFINED_TOOLTIP ), // 40 0x009DE2F8 - MakeTab ({ 96, 17}, STR_STRING_DEFINED_TOOLTIP ), // 80 0x009DE308 - MakeTab ({127, 17}, STR_STRING_DEFINED_TOOLTIP ), // 100 0x009DE318 - MakeTab ({158, 17}, STR_STRING_DEFINED_TOOLTIP ), // 200 0x009DE328 - MakeTab ({189, 17}, STR_STRING_DEFINED_TOOLTIP ), // 400 0x009DE338 - MakeTab ({220, 17}, STR_STRING_DEFINED_TOOLTIP ), // 800 0x009DE348 - MakeTab ({251, 17}, STR_STRING_DEFINED_TOOLTIP ), // 1000 0x009DE358 - MakeTab ({282, 17}, STR_STRING_DEFINED_TOOLTIP ), // 2000 0x009DE368 - MakeTab ({313, 17}, STR_STRING_DEFINED_TOOLTIP ), // 4000 0x009DE378 - MakeTab ({344, 17}, STR_STRING_DEFINED_TOOLTIP ), // 8000 0x009DE388 - MakeTab ({375, 17}, STR_STRING_DEFINED_TOOLTIP ), // 10000 0x009DE398 - MakeTab ({406, 17}, STR_STRING_DEFINED_TOOLTIP ), // 20000 0x009DE3A8 - MakeTab ({437, 17}, STR_STRING_DEFINED_TOOLTIP ), // 40000 0x009DE3B8 - MakeTab ({468, 17}, STR_STRING_DEFINED_TOOLTIP ), // 80000 0x009DE3C8 - MakeTab ({468, 17}, STR_STRING_DEFINED_TOOLTIP ), // 100000 0x009DE3D8 - MakeTab ({468, 17}, STR_STRING_DEFINED_TOOLTIP ), // 200000 0x009DE3E8 - MakeTab ({468, 17}, STR_STRING_DEFINED_TOOLTIP ), // 400000 0x009DE3F8 - MakeRemapWidget({468, 17}, { 31, 27}, WindowWidgetType::Tab, WindowColour::Secondary, SPR_TAB_QUESTION, STR_STRING_DEFINED_TOOLTIP ), // 800000 0x009DE408 MakeWidget ({ 2, 47}, {607, 80}, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_VERTICAL ), // 1000000 0x009DE418 MakeWidget ({609, 44}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_ROTATE_ARROW, STR_ROTATE_OBJECTS_90 ), // 2000000 0x009DE428 MakeWidget ({609, 68}, { 24, 24}, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_PAINTBRUSH, STR_SCENERY_PAINTBRUSH_TIP ), // 4000000 0x009DE438 @@ -177,55 +104,195 @@ static rct_widget window_scenery_widgets[] = { void window_scenery_update_scroll(rct_window* w); -// rct2: 0x00F64F2C -static ScenerySelection window_scenery_tab_entries[SCENERY_WINDOW_TABS][SCENERY_ENTRIES_PER_TAB + 1]; - -/** - * Was part of 0x006DFA00 - * The same code repeated five times for every scenery entry type - */ -static void init_scenery_entry(const ScenerySelection& selection, ObjectEntryIndex sceneryTabId) +struct SceneryTabInfo { - Guard::ArgumentInRange(selection.EntryIndex, 0, WINDOW_SCENERY_TAB_SELECTION_UNDEFINED); - if (scenery_is_invented(selection) || gCheatsIgnoreResearchStatus) + ObjectEntryIndex SceneryGroupIndex = OBJECT_ENTRY_INDEX_NULL; + std::vector Entries; + + bool IsMisc() const { - if (sceneryTabId < SCENERY_WINDOW_TABS) + return SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL; + } + + bool Contains(ScenerySelection entry) const + { + auto it = std::find(Entries.begin(), Entries.end(), entry); + return it != Entries.end(); + } + + void AddEntry(ScenerySelection entry) + { + if (!Contains(entry)) { - for (int32_t i = 0; i < SCENERY_ENTRIES_PER_TAB; i++) + Entries.push_back(entry); + } + } + + const rct_scenery_group_entry* GetSceneryGroupEntry() const + { + return get_scenery_group_entry(SceneryGroupIndex); + } +}; + +std::vector gWindowSceneryTabSelections; +size_t gWindowSceneryActiveTabIndex; +uint8_t gWindowSceneryPaintEnabled; +uint8_t gWindowSceneryRotation; +colour_t gWindowSceneryPrimaryColour; +colour_t gWindowScenerySecondaryColour; +colour_t gWindowSceneryTertiaryColour; +bool gWindowSceneryEyedropperEnabled; + +static std::vector _tabEntries; +static std::vector _widgets; +static ScenerySelection _selectedScenery; +static int16_t _hoverCounter; + +static ScenerySelection GetSelectedScenery(size_t tabIndex) +{ + if (gWindowSceneryTabSelections.size() > tabIndex) + { + return gWindowSceneryTabSelections[tabIndex]; + } + return {}; +} + +static void SetSelectedScenery(size_t tabIndex, ScenerySelection value) +{ + if (gWindowSceneryTabSelections.size() <= tabIndex) + { + gWindowSceneryTabSelections.resize(tabIndex + 1); + } + gWindowSceneryTabSelections[tabIndex] = value; +} + +static SceneryTabInfo* GetSceneryTabInfoForGroup(ObjectEntryIndex sceneryGroupIndex) +{ + if (sceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) + { + return &_tabEntries[_tabEntries.size() - 1]; + } + + for (auto& tabEntry : _tabEntries) + { + if (tabEntry.SceneryGroupIndex == sceneryGroupIndex) + return &tabEntry; + } + + return nullptr; +} + +static std::optional window_scenery_find_tab_with_scenery(const ScenerySelection& scenery) +{ + for (size_t i = 0; i < _tabEntries.size(); i++) + { + const auto& tabInfo = _tabEntries[i]; + for (const auto& entry : tabInfo.Entries) + { + if (entry == scenery) { - if (window_scenery_tab_entries[sceneryTabId][i].IsUndefined()) - { - window_scenery_tab_entries[sceneryTabId][i] = selection; - window_scenery_tab_entries[sceneryTabId][i + 1].SetUndefined(); - return; - } + return i; + } + } + } + return {}; +} + +static void init_scenery_entry(const ScenerySelection& selection, ObjectEntryIndex sceneryGroupIndex) +{ + Guard::ArgumentInRange(selection.EntryIndex, 0, OBJECT_ENTRY_INDEX_NULL); + + if (IsSceneryAvailableToBuild(selection)) + { + // Get current tab + auto tabIndex = window_scenery_find_tab_with_scenery(selection); + + // Add scenery to primary group (usually trees or path additions) + if (sceneryGroupIndex != OBJECT_ENTRY_INDEX_NULL) + { + auto tabInfo = GetSceneryTabInfoForGroup(sceneryGroupIndex); + if (tabInfo != nullptr) + { + tabInfo->AddEntry(selection); + return; } } - for (int32_t i = 0; i < SCENERY_WINDOW_TABS - 1; i++) + // If scenery is no tab, add it to misc + if (!tabIndex) { - int32_t counter = 0; - - while (!window_scenery_tab_entries[i][counter].IsUndefined()) + auto tabInfo = GetSceneryTabInfoForGroup(OBJECT_ENTRY_INDEX_NULL); + if (tabInfo != nullptr) { - if (window_scenery_tab_entries[i][counter] == selection) - { - return; - } + tabInfo->AddEntry(selection); + } + } + } +} - counter++; +static void window_scenery_sort_tabs() +{ + std::sort(_tabEntries.begin(), _tabEntries.end(), [](const SceneryTabInfo& a, const SceneryTabInfo& b) { + if (a.SceneryGroupIndex == b.SceneryGroupIndex) + return false; + + if (a.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) + return false; + if (b.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) + return true; + + auto entryA = a.GetSceneryGroupEntry(); + auto entryB = b.GetSceneryGroupEntry(); + return entryA->priority < entryB->priority; + }); +} + +static void window_scenery_prepare_widgets(rct_window* w) +{ + _widgets.clear(); + for (const auto& widget : window_scenery_widgets) + { + _widgets.push_back(widget); + } + + // Remove WWT_LAST + auto lastWidget = _widgets.back(); + _widgets.pop_back(); + + // Add tabs + ScreenCoordsXY pos = { 3, 17 }; + for (const auto& tabInfo : _tabEntries) + { + auto widget = MakeTab(pos, STR_STRING_DEFINED_TOOLTIP); + pos.x += 31; + + if (tabInfo.SceneryGroupIndex == OBJECT_ENTRY_INDEX_NULL) + { + widget.image = SPR_TAB_QUESTION | IMAGE_TYPE_REMAP; + } + else + { + auto sceneryGroupEntry = get_scenery_group_entry(tabInfo.SceneryGroupIndex); + if (sceneryGroupEntry != nullptr) + { + widget.image = sceneryGroupEntry->image | IMAGE_TYPE_REMAP; } } - for (int32_t i = 0; i < SCENERY_ENTRIES_PER_TAB; i++) - { - if (window_scenery_tab_entries[SCENERY_WINDOW_TABS - 1][i].IsUndefined()) - { - window_scenery_tab_entries[SCENERY_WINDOW_TABS - 1][i] = selection; - window_scenery_tab_entries[SCENERY_WINDOW_TABS - 1][i + 1].SetUndefined(); - break; - } - } + _widgets.push_back(widget); + } + + _widgets.push_back(lastWidget); + + w->widgets = _widgets.data(); + + w->enabled_widgets = (1 << WIDX_SCENERY_CLOSE) | (1 << WIDX_SCENERY_ROTATE_OBJECTS_BUTTON) + | (1 << WIDX_SCENERY_PRIMARY_COLOUR_BUTTON) | (1 << WIDX_SCENERY_SECONDARY_COLOUR_BUTTON) + | (1 << WIDX_SCENERY_REPAINT_SCENERY_BUTTON) | (1 << WIDX_SCENERY_TERTIARY_COLOUR_BUTTON) + | (1 << WIDX_SCENERY_EYEDROPPER_BUTTON) | (1ULL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); + for (size_t i = 0; i < _tabEntries.size(); i++) + { + w->enabled_widgets |= (1ULL << (WIDX_SCENERY_TAB_1 + i)); } } @@ -233,158 +300,106 @@ static void init_scenery_entry(const ScenerySelection& selection, ObjectEntryInd * * rct2: 0x006DFA00 */ -void window_scenery_init() +static void window_scenery_init(rct_window* w) { - bool enabledScenerySets[SCENERY_WINDOW_TABS] = { false }; + _tabEntries.clear(); - for (int32_t scenerySetIndex = 0; scenerySetIndex < SCENERY_WINDOW_TABS; scenerySetIndex++) + auto maxTabs = 32; + for (ObjectEntryIndex scenerySetIndex = 0; scenerySetIndex < maxTabs - 1; scenerySetIndex++) { - window_scenery_tab_entries[scenerySetIndex][0].SetUndefined(); - if (scenerySetIndex == MAX_SCENERY_GROUP_OBJECTS) - continue; - - rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(scenerySetIndex); - if (sceneryGroupEntry == nullptr) - continue; - - int32_t sceneryTabEntryCount = 0; - for (int32_t i = 0; i < sceneryGroupEntry->entry_count; i++) + const auto* sceneryGroupEntry = get_scenery_group_entry(scenerySetIndex); + if (sceneryGroupEntry != nullptr) { - auto sceneryEntry = sceneryGroupEntry->scenery_entries[i]; - if (scenery_is_invented(sceneryEntry) || gCheatsIgnoreResearchStatus) + SceneryTabInfo tabInfo; + tabInfo.SceneryGroupIndex = scenerySetIndex; + for (size_t i = 0; i < sceneryGroupEntry->entry_count; i++) { - window_scenery_tab_entries[scenerySetIndex][sceneryTabEntryCount] = sceneryEntry; - window_scenery_tab_entries[scenerySetIndex][++sceneryTabEntryCount].SetUndefined(); + auto sceneryEntry = sceneryGroupEntry->scenery_entries[i]; + if (IsSceneryAvailableToBuild(sceneryEntry)) + { + tabInfo.Entries.push_back(sceneryEntry); + } } - else + if (tabInfo.Entries.size() > 0) { - enabledScenerySets[scenerySetIndex] = true; + _tabEntries.push_back(std::move(tabInfo)); } } } + // Add misc tab + _tabEntries.emplace_back(); + // small scenery for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_SMALL_SCENERY_OBJECTS; sceneryId++) { - auto* sceneryEntry = get_small_scenery_entry(sceneryId); - if (sceneryEntry == nullptr) - continue; - - init_scenery_entry({ SCENERY_TYPE_SMALL, sceneryId }, sceneryEntry->scenery_tab_id); + const auto* sceneryEntry = get_small_scenery_entry(sceneryId); + if (sceneryEntry != nullptr) + { + init_scenery_entry({ SCENERY_TYPE_SMALL, sceneryId }, sceneryEntry->scenery_tab_id); + } } // large scenery for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_LARGE_SCENERY_OBJECTS; sceneryId++) { - auto* sceneryEntry = get_large_scenery_entry(sceneryId); - if (sceneryEntry == nullptr) - continue; - - init_scenery_entry({ SCENERY_TYPE_LARGE, sceneryId }, sceneryEntry->scenery_tab_id); + const auto* sceneryEntry = get_large_scenery_entry(sceneryId); + if (sceneryEntry != nullptr) + { + init_scenery_entry({ SCENERY_TYPE_LARGE, sceneryId }, sceneryEntry->scenery_tab_id); + } } // walls for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_WALL_SCENERY_OBJECTS; sceneryId++) { - auto* sceneryEntry = get_wall_entry(sceneryId); - if (sceneryEntry == nullptr) - continue; - - init_scenery_entry({ SCENERY_TYPE_WALL, sceneryId }, sceneryEntry->scenery_tab_id); + const auto* sceneryEntry = get_wall_entry(sceneryId); + if (sceneryEntry != nullptr) + { + init_scenery_entry({ SCENERY_TYPE_WALL, sceneryId }, sceneryEntry->scenery_tab_id); + } } // banners for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_BANNER_OBJECTS; sceneryId++) { - auto* bannerEntry = get_banner_entry(sceneryId); - if (bannerEntry == nullptr) - continue; - - init_scenery_entry({ SCENERY_TYPE_BANNER, sceneryId }, bannerEntry->scenery_tab_id); + const auto* sceneryEntry = get_banner_entry(sceneryId); + if (sceneryEntry != nullptr) + { + init_scenery_entry({ SCENERY_TYPE_BANNER, sceneryId }, sceneryEntry->scenery_tab_id); + } } // path bits for (ObjectEntryIndex sceneryId = 0; sceneryId < MAX_PATH_ADDITION_OBJECTS; sceneryId++) { - auto* pathBitEntry = get_footpath_item_entry(sceneryId); - if (pathBitEntry == nullptr) - continue; - - init_scenery_entry({ SCENERY_TYPE_PATH_ITEM, sceneryId }, pathBitEntry->scenery_tab_id); - } - - for (rct_widgetindex widgetIndex = WIDX_SCENERY_TAB_1; widgetIndex < WIDX_SCENERY_LIST; widgetIndex++) - window_scenery_widgets[widgetIndex].type = WindowWidgetType::Empty; - - uint8_t tabIndexes[SCENERY_WINDOW_TABS]; - uint8_t order[SCENERY_WINDOW_TABS]; - int32_t usedValues = 0; - - for (int32_t scenerySetId = 0; scenerySetId < MAX_SCENERY_GROUP_OBJECTS; scenerySetId++) - { - rct_scenery_group_entry* sceneryEntry = get_scenery_group_entry(scenerySetId); - if (sceneryEntry == nullptr) - continue; - - tabIndexes[usedValues] = scenerySetId; - order[usedValues] = sceneryEntry->priority; - - usedValues++; - } - - while (true) - { - bool finished = true; - for (int32_t i = 1; i < usedValues; i++) + const auto* sceneryEntry = get_footpath_item_entry(sceneryId); + if (sceneryEntry != nullptr) { - if (order[i - 1] > order[i]) - { - uint8_t tmp = tabIndexes[i - 1]; - tabIndexes[i - 1] = tabIndexes[i]; - tabIndexes[i] = tmp; - tmp = order[i - 1]; - order[i - 1] = order[i]; - order[i] = tmp; - finished = false; - } + init_scenery_entry({ SCENERY_TYPE_PATH_ITEM, sceneryId }, sceneryEntry->scenery_tab_id); } - - if (finished) - break; } - tabIndexes[usedValues] = SCENERY_WINDOW_TABS - 1; - usedValues++; - - uint16_t left = 3; - for (int32_t i = 0; i < usedValues; i++) + // Remove misc tab if empty + if (_tabEntries.back().Entries.size() == 0) { - uint32_t tabIndex = tabIndexes[i]; - rct_widget* tabWidget = &window_scenery_widgets[tabIndex + WIDX_SCENERY_TAB_1]; - - if (left != 3 || tabIndex != SCENERY_WINDOW_TABS - 1) - { - if (window_scenery_tab_entries[tabIndex][0].IsUndefined()) - continue; - - if (enabledScenerySets[tabIndex]) - continue; - } - - tabWidget->type = WindowWidgetType::Tab; - tabWidget->left = left; - tabWidget->right = left + 0x1E; - left += 0x1F; - - if (tabIndex >= SCENERY_WINDOW_TABS - 1) - continue; - - tabWidget->image = get_scenery_group_entry(tabIndex)->image | IMAGE_TYPE_REMAP; + _tabEntries.pop_back(); } + window_scenery_sort_tabs(); + window_scenery_prepare_widgets(w); window_invalidate_by_class(WC_SCENERY); } +void window_scenery_init() +{ + auto w = window_find_by_class(WC_SCENERY); + if (w != nullptr) + { + window_scenery_init(w); + } +} + /** * * rct2: 0x006DFEE4 @@ -395,29 +410,11 @@ void window_scenery_set_default_placement_configuration() gWindowSceneryPrimaryColour = COLOUR_BORDEAUX_RED; gWindowScenerySecondaryColour = COLOUR_YELLOW; gWindowSceneryTertiaryColour = COLOUR_DARK_BROWN; + window_scenery_init(); - for (int32_t i = 0; i < SCENERY_WINDOW_TABS; i++) - gWindowSceneryTabSelections[i] = ScenerySelection::CreateUndefined(); - - for (int32_t i = 0; i < SCENERY_WINDOW_TABS; i++) - { - if (!window_scenery_tab_entries[i][0].IsUndefined()) - { - gWindowSceneryActiveTabIndex = i; - return; - } - } - - for (int32_t i = 0; i < 16; i++) - { - rct_widget* tabWidget = &window_scenery_widgets[WIDX_SCENERY_TAB_1 + i]; - if (tabWidget->type != WindowWidgetType::Empty) - { - gWindowSceneryActiveTabIndex = i; - return; - } - } + gWindowSceneryTabSelections.clear(); + gWindowSceneryActiveTabIndex = 0; } /** @@ -426,31 +423,16 @@ void window_scenery_set_default_placement_configuration() */ rct_window* window_scenery_open() { - rct_window* window; - // Check if window is already open - window = window_bring_to_front_by_class(WC_SCENERY); + auto window = window_bring_to_front_by_class(WC_SCENERY); if (window != nullptr) return window; - window_scenery_init(); - window = WindowCreate( ScreenCoordsXY(context_get_width() - WINDOW_SCENERY_WIDTH, 0x1D), WINDOW_SCENERY_WIDTH, WINDOW_SCENERY_HEIGHT, &window_scenery_events, WC_SCENERY, WF_NO_SCROLLING); - window->widgets = window_scenery_widgets; - window->enabled_widgets = (1ULL << WIDX_SCENERY_CLOSE) | (1ULL << WIDX_SCENERY_ROTATE_OBJECTS_BUTTON) - | (1ULL << WIDX_SCENERY_TAB_1) | (1ULL << WIDX_SCENERY_TAB_2) | (1ULL << WIDX_SCENERY_TAB_3) - | (1ULL << WIDX_SCENERY_TAB_4) | (1ULL << WIDX_SCENERY_TAB_5) | (1ULL << WIDX_SCENERY_TAB_6) - | (1ULL << WIDX_SCENERY_TAB_7) | (1ULL << WIDX_SCENERY_TAB_8) | (1ULL << WIDX_SCENERY_TAB_9) - | (1ULL << WIDX_SCENERY_TAB_10) | (1ULL << WIDX_SCENERY_TAB_11) | (1ULL << WIDX_SCENERY_TAB_12) - | (1ULL << WIDX_SCENERY_TAB_13) | (1ULL << WIDX_SCENERY_TAB_14) | (1ULL << WIDX_SCENERY_TAB_15) - | (1ULL << WIDX_SCENERY_TAB_16) | (1ULL << WIDX_SCENERY_TAB_17) | (1ULL << WIDX_SCENERY_TAB_18) - | (1ULL << WIDX_SCENERY_TAB_19) | (1ULL << WIDX_SCENERY_TAB_20) | (1ULL << WIDX_SCENERY_PRIMARY_COLOUR_BUTTON) - | (1ULL << WIDX_SCENERY_SECONDARY_COLOUR_BUTTON) | (1ULL << WIDX_SCENERY_REPAINT_SCENERY_BUTTON) - | (1ULL << WIDX_SCENERY_TERTIARY_COLOUR_BUTTON) | (1ULL << WIDX_SCENERY_EYEDROPPER_BUTTON) - | (1ULL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); + window_scenery_init(window); WindowInitScrollWidgets(window); window_scenery_update_scroll(window); @@ -458,8 +440,8 @@ rct_window* window_scenery_open() gWindowSceneryRotation = 3; gSceneryCtrlPressed = false; gSceneryShiftPressed = false; - window->scenery.SelectedScenery = ScenerySelection::CreateUndefined(); - window->scenery.hover_counter = 0; + _selectedScenery = {}; + _hoverCounter = 0; window_push_others_below(window); gSceneryGhostType = 0; gSceneryPlaceCost = MONEY32_UNDEFINED; @@ -475,7 +457,14 @@ rct_window* window_scenery_open() return window; } -/** +static int32_t window_scenery_get_num_columns(rct_window* w) +{ + const auto* listWidget = &w->widgets[WIDX_SCENERY_LIST]; + auto contentWidth = listWidget->width() - SCROLLBAR_WIDTH; + return contentWidth / SCENERY_BUTTON_WIDTH; +} + +/* * * rct2: 0x006E1A73 */ @@ -492,10 +481,23 @@ void window_scenery_close(rct_window* w) tool_cancel(); } -static int32_t count_rows(int32_t items) +template constexpr static T window_scenery_count_rows(rct_window* w, T items) { - int32_t rows = items / 9; + auto rows = items / window_scenery_get_num_columns(w); + return rows; +} +static size_t window_scenery_count_rows(rct_window* w) +{ + auto tabIndex = gWindowSceneryActiveTabIndex; + if (tabIndex >= _tabEntries.size()) + { + return 0; + } + + auto totalItems = _tabEntries[tabIndex].Entries.size(); + auto numColumns = window_scenery_get_num_columns(w); + auto rows = window_scenery_count_rows(w, totalItems + numColumns - 1); return rows; } @@ -506,46 +508,27 @@ struct scenery_item ScenerySelection scenerySelection; }; -static scenery_item window_scenery_count_rows_with_selected_item(int32_t tabIndex) +static scenery_item window_scenery_count_rows_with_selected_item(rct_window* w, size_t tabIndex) { - scenery_item sceneryItem = { 0, 0, ScenerySelection::CreateUndefined() }; - int32_t totalItems = 0; - ScenerySelection currentEntry = { 0, 0 }; - ScenerySelection scenerySelection = gWindowSceneryTabSelections[tabIndex]; - - for (totalItems = 0; totalItems < SCENERY_ENTRIES_PER_TAB + 1; ++totalItems) + scenery_item sceneryItem = { 0, 0, ScenerySelection() }; + const auto& scenerySelection = GetSelectedScenery(tabIndex); + const auto& tabInfo = _tabEntries[tabIndex]; + for (size_t i = 0; i < tabInfo.Entries.size(); i++) { - currentEntry = window_scenery_tab_entries[tabIndex][totalItems]; - if (currentEntry.IsUndefined()) - break; - + const auto& currentEntry = tabInfo.Entries[i]; if (currentEntry == scenerySelection) { - sceneryItem.selected_item = totalItems; + sceneryItem.selected_item = static_cast(i); sceneryItem.scenerySelection = scenerySelection; } } - sceneryItem.allRows = count_rows(totalItems + 8); + sceneryItem.allRows = static_cast(window_scenery_count_rows(w, tabInfo.Entries.size() + 8)); return sceneryItem; } -static int32_t window_scenery_count_rows() +static int32_t window_scenery_rows_height(size_t rows) { - int32_t tabIndex = gWindowSceneryActiveTabIndex; - int32_t totalItems = 0; - - while (!window_scenery_tab_entries[tabIndex][totalItems].IsUndefined()) - { - totalItems++; - } - - int32_t rows = count_rows(totalItems + 8); - return rows; -} - -static int32_t window_scenery_rows_height(int32_t rows) -{ - return rows * SCENERY_BUTTON_HEIGHT; + return static_cast(rows * SCENERY_BUTTON_HEIGHT); } /** @@ -608,20 +591,27 @@ static void window_scenery_mouseup(rct_window* w, rct_widgetindex widgetIndex) */ void window_scenery_update_scroll(rct_window* w) { - int32_t tabIndex = gWindowSceneryActiveTabIndex; - int32_t listHeight = w->height - 14 - window_scenery_widgets[WIDX_SCENERY_LIST].top - 1; + auto tabIndex = gWindowSceneryActiveTabIndex; + if (tabIndex >= _tabEntries.size()) + { + return; + } - scenery_item sceneryItem = window_scenery_count_rows_with_selected_item(tabIndex); + int32_t listHeight = w->height - 14 - w->widgets[WIDX_SCENERY_LIST].top - 1; + + scenery_item sceneryItem = window_scenery_count_rows_with_selected_item(w, tabIndex); w->scrolls[0].v_bottom = window_scenery_rows_height(sceneryItem.allRows) + 1; int32_t maxTop = std::max(0, w->scrolls[0].v_bottom - listHeight); - int32_t rowSelected = count_rows(sceneryItem.selected_item); + auto rowSelected = window_scenery_count_rows(w, sceneryItem.selected_item); if (sceneryItem.scenerySelection.IsUndefined()) { rowSelected = 0; - ScenerySelection scenery = window_scenery_tab_entries[tabIndex][0]; + auto& scenery = _tabEntries[tabIndex].Entries[0]; if (!scenery.IsUndefined()) - gWindowSceneryTabSelections[tabIndex] = scenery; + { + SetSelectedScenery(tabIndex, scenery); + } } w->scrolls[0].v_top = window_scenery_rows_height(rowSelected); @@ -690,7 +680,7 @@ static void window_scenery_mousedown(rct_window* w, rct_widgetindex widgetIndex, break; } - if (widgetIndex >= WIDX_SCENERY_TAB_1 && widgetIndex <= WIDX_SCENERY_TAB_20) + if (widgetIndex >= WIDX_SCENERY_TAB_1) { gWindowSceneryActiveTabIndex = widgetIndex - WIDX_SCENERY_TAB_1; w->Invalidate(); @@ -731,9 +721,9 @@ static void window_scenery_dropdown(rct_window* w, rct_widgetindex widgetIndex, */ static void window_scenery_periodic_update(rct_window* w) { - if (!w->scenery.SelectedScenery.IsUndefined()) + if (!_selectedScenery.IsUndefined()) { - w->scenery.SelectedScenery = ScenerySelection::CreateUndefined(); + _selectedScenery = {}; } } @@ -754,8 +744,8 @@ static void window_scenery_update(rct_window* w) rct_widgetindex widgetIndex = window_find_widget_from_point(w, state->position); if (widgetIndex >= WIDX_SCENERY_TAB_CONTENT_PANEL) { - w->scenery.hover_counter++; - if (w->scenery.hover_counter < 8) + _hoverCounter++; + if (_hoverCounter < 8) { if (input_get_state() != InputState::ScrollLeft) { @@ -767,10 +757,13 @@ static void window_scenery_update(rct_window* w) } else { - int32_t windowHeight = std::min(463, w->scrolls[0].v_bottom + 62); - if (context_get_height() < 600) - windowHeight = std::min(374, windowHeight); - windowHeight = std::max(WINDOW_SCENERY_HEIGHT, windowHeight); + const auto& listWidget = w->widgets[WIDX_SCENERY_LIST]; + auto nonListHeight = w->height - listWidget.height() + 2; + + auto numRows = static_cast(window_scenery_count_rows(w)); + auto maxContentHeight = numRows * SCENERY_BUTTON_HEIGHT; + auto maxWindowHeight = maxContentHeight + nonListHeight; + auto windowHeight = std::clamp(maxWindowHeight, WINDOW_SCENERY_HEIGHT, 463); w->min_width = WINDOW_SCENERY_WIDTH; w->max_width = WINDOW_SCENERY_WIDTH; @@ -782,7 +775,7 @@ static void window_scenery_update(rct_window* w) } else { - w->scenery.hover_counter = 0; + _hoverCounter = 0; if (input_get_state() != InputState::ScrollLeft) { w->min_width = WINDOW_SCENERY_WIDTH; @@ -810,9 +803,8 @@ static void window_scenery_update(rct_window* w) } else { - uint16_t tabIndex = gWindowSceneryActiveTabIndex; - ScenerySelection tabSelectedScenery = gWindowSceneryTabSelections[tabIndex]; - + auto tabIndex = gWindowSceneryActiveTabIndex; + auto tabSelectedScenery = GetSelectedScenery(tabIndex); if (!tabSelectedScenery.IsUndefined()) { if (tabSelectedScenery.SceneryType == SCENERY_TYPE_BANNER) @@ -845,26 +837,30 @@ static void window_scenery_update(rct_window* w) */ void window_scenery_scrollgetsize(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) { - int32_t rows = window_scenery_count_rows(); + auto rows = window_scenery_count_rows(w); *height = window_scenery_rows_height(rows); } -static ScenerySelection get_scenery_id_by_cursor_pos(const ScreenCoordsXY& screenCoords) +static ScenerySelection get_scenery_id_by_cursor_pos(rct_window* w, const ScreenCoordsXY& screenCoords) { - int32_t tabSceneryIndex = screenCoords.x / SCENERY_BUTTON_WIDTH + (screenCoords.y / SCENERY_BUTTON_HEIGHT) * 9; - uint8_t tabIndex = gWindowSceneryActiveTabIndex; + ScenerySelection scenery{}; - int32_t itemCounter = 0; - ScenerySelection scenery = ScenerySelection::CreateUndefined(); - while (itemCounter <= tabSceneryIndex) + auto numColumns = window_scenery_get_num_columns(w); + auto colIndex = screenCoords.x / SCENERY_BUTTON_WIDTH; + auto rowIndex = screenCoords.y / SCENERY_BUTTON_HEIGHT; + if (colIndex >= 0 && colIndex < numColumns && rowIndex >= 0) { - scenery = window_scenery_tab_entries[tabIndex][itemCounter]; - if (scenery.IsUndefined()) - return ScenerySelection::CreateUndefined(); - - itemCounter++; + auto tabSceneryIndex = static_cast((rowIndex * numColumns) + colIndex); + auto tabIndex = gWindowSceneryActiveTabIndex; + if (tabIndex < _tabEntries.size()) + { + auto& tabInfo = _tabEntries[tabIndex]; + if (tabSceneryIndex < tabInfo.Entries.size()) + { + return tabInfo.Entries[tabSceneryIndex]; + } + } } - return scenery; } @@ -874,17 +870,16 @@ static ScenerySelection get_scenery_id_by_cursor_pos(const ScreenCoordsXY& scree */ void window_scenery_scrollmousedown(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) { - ScenerySelection scenery = get_scenery_id_by_cursor_pos(screenCoords); + ScenerySelection scenery = get_scenery_id_by_cursor_pos(w, screenCoords); if (scenery.IsUndefined()) return; - uint8_t tabIndex = gWindowSceneryActiveTabIndex; - gWindowSceneryTabSelections[tabIndex] = scenery; + SetSelectedScenery(gWindowSceneryActiveTabIndex, scenery); gWindowSceneryPaintEnabled &= 0xFE; gWindowSceneryEyedropperEnabled = false; OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, w->windowPos.x + (w->width / 2)); - w->scenery.hover_counter = -16; + _hoverCounter = -16; gSceneryPlaceCost = MONEY32_UNDEFINED; w->Invalidate(); } @@ -895,10 +890,10 @@ void window_scenery_scrollmousedown(rct_window* w, int32_t scrollIndex, const Sc */ void window_scenery_scrollmouseover(rct_window* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) { - ScenerySelection scenery = get_scenery_id_by_cursor_pos(screenCoords); + ScenerySelection scenery = get_scenery_id_by_cursor_pos(w, screenCoords); if (!scenery.IsUndefined()) { - w->scenery.SelectedScenery = scenery; + _selectedScenery = scenery; w->Invalidate(); } } @@ -909,36 +904,29 @@ void window_scenery_scrollmouseover(rct_window* w, int32_t scrollIndex, const Sc */ OpenRCT2String window_scenery_tooltip(rct_window* w, const rct_widgetindex widgetIndex, const rct_string_id fallback) { - auto ft = Formatter(); - - switch (widgetIndex) + if (widgetIndex >= WIDX_SCENERY_TAB_1) { - case WIDX_SCENERY_TAB_1: - case WIDX_SCENERY_TAB_2: - case WIDX_SCENERY_TAB_3: - case WIDX_SCENERY_TAB_4: - case WIDX_SCENERY_TAB_5: - case WIDX_SCENERY_TAB_6: - case WIDX_SCENERY_TAB_7: - case WIDX_SCENERY_TAB_8: - case WIDX_SCENERY_TAB_9: - case WIDX_SCENERY_TAB_10: - case WIDX_SCENERY_TAB_11: - case WIDX_SCENERY_TAB_12: - case WIDX_SCENERY_TAB_13: - case WIDX_SCENERY_TAB_14: - case WIDX_SCENERY_TAB_15: - case WIDX_SCENERY_TAB_16: - case WIDX_SCENERY_TAB_17: - case WIDX_SCENERY_TAB_18: - case WIDX_SCENERY_TAB_19: - ft.Add(get_scenery_group_entry(widgetIndex - WIDX_SCENERY_TAB_1)->name); - break; - case WIDX_SCENERY_TAB_20: - ft.Add(STR_MISCELLANEOUS); - break; + auto tabIndex = static_cast(widgetIndex - WIDX_SCENERY_TAB_1); + if (_tabEntries.size() > tabIndex) + { + const auto& tabInfo = _tabEntries[tabIndex]; + if (tabInfo.IsMisc()) + { + auto ft = Formatter(); + ft.Add(STR_MISCELLANEOUS); + return { fallback, ft }; + } + + auto sceneryEntry = tabInfo.GetSceneryGroupEntry(); + if (sceneryEntry != nullptr) + { + auto ft = Formatter(); + ft.Add(sceneryEntry->name); + return { fallback, ft }; + } + } } - return { fallback, ft }; + return { STR_NONE, Formatter() }; } /** @@ -947,18 +935,19 @@ OpenRCT2String window_scenery_tooltip(rct_window* w, const rct_widgetindex widge */ void window_scenery_invalidate(rct_window* w) { - uint16_t tabIndex = gWindowSceneryActiveTabIndex; - uint32_t titleStringId = STR_MISCELLANEOUS; - if (tabIndex < SCENERY_WINDOW_TABS - 1) + // Set the window title + rct_string_id titleStringId = STR_MISCELLANEOUS; + auto tabIndex = gWindowSceneryActiveTabIndex; + if (tabIndex < _tabEntries.size()) { - rct_scenery_group_entry* sgEntry = get_scenery_group_entry(tabIndex); + const auto& tabInfo = _tabEntries[tabIndex]; + const auto* sgEntry = tabInfo.GetSceneryGroupEntry(); if (sgEntry != nullptr) { titleStringId = sgEntry->name; } } - - window_scenery_widgets[WIDX_SCENERY_TITLE].text = titleStringId; + w->widgets[WIDX_SCENERY_TITLE].text = titleStringId; w->pressed_widgets = 0; w->pressed_widgets |= 1ULL << (tabIndex + WIDX_SCENERY_TAB_1); @@ -969,55 +958,54 @@ void window_scenery_invalidate(rct_window* w) if (gWindowSceneryScatterEnabled) w->pressed_widgets |= (1ULL << WIDX_SCENERY_BUILD_CLUSTER_BUTTON); - window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; - window_scenery_widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::Empty; - window_scenery_widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::Empty; if (!(gWindowSceneryPaintEnabled & 1)) { - window_scenery_widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::FlatBtn; + w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].type = WindowWidgetType::FlatBtn; } - ScenerySelection tabSelectedScenery = gWindowSceneryTabSelections[tabIndex]; + auto tabSelectedScenery = GetSelectedScenery(tabIndex); if (!tabSelectedScenery.IsUndefined()) { if (tabSelectedScenery.SceneryType == SCENERY_TYPE_SMALL) { if (!(gWindowSceneryPaintEnabled & 1)) { - window_scenery_widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::FlatBtn; + w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].type = WindowWidgetType::FlatBtn; } auto* sceneryEntry = get_small_scenery_entry(tabSelectedScenery.EntryIndex); if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ROTATABLE)) { - window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn; + w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn; } } else if (tabSelectedScenery.SceneryType >= SCENERY_TYPE_LARGE) { - window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn; + w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::FlatBtn; } } - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowSceneryPrimaryColour) + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowSceneryPrimaryColour) | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN; - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1( - gWindowScenerySecondaryColour) + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowScenerySecondaryColour) | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN; - window_scenery_widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowSceneryTertiaryColour) + w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].image = SPRITE_ID_PALETTE_COLOUR_1(gWindowSceneryTertiaryColour) | IMAGE_TYPE_TRANSPARENT | SPR_PALETTE_BTN; - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; - window_scenery_widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; if (gWindowSceneryPaintEnabled & 1) { // repaint coloured scenery tool is on - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - window_scenery_widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; - window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type = WindowWidgetType::Empty; } else if (!tabSelectedScenery.IsUndefined()) { @@ -1026,7 +1014,7 @@ void window_scenery_invalidate(rct_window* w) auto* bannerEntry = get_banner_entry(tabSelectedScenery.EntryIndex); if (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR) { - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; } } else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_LARGE) @@ -1034,25 +1022,25 @@ void window_scenery_invalidate(rct_window* w) auto* sceneryEntry = get_large_scenery_entry(tabSelectedScenery.EntryIndex); if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; } else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL) { auto* wallEntry = get_wall_entry(tabSelectedScenery.EntryIndex); if (wallEntry->flags & (WALL_SCENERY_HAS_PRIMARY_COLOUR | WALL_SCENERY_HAS_GLASS)) { - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) { - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; if (wallEntry->flags2 & WALL_SCENERY_2_NO_SELECT_PRIMARY_COLOUR) - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::Empty; if (wallEntry->flags & WALL_SCENERY_HAS_TERNARY_COLOUR) - window_scenery_widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; } } } @@ -1062,39 +1050,116 @@ void window_scenery_invalidate(rct_window* w) if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR | SMALL_SCENERY_FLAG_HAS_GLASS)) { - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; } } } - window_scenery_widgets[WIDX_SCENERY_BACKGROUND].right = w->width - 1; - window_scenery_widgets[WIDX_SCENERY_BACKGROUND].bottom = w->height - 1; - window_scenery_widgets[WIDX_SCENERY_TAB_CONTENT_PANEL].right = w->width - 1; - window_scenery_widgets[WIDX_SCENERY_TAB_CONTENT_PANEL].bottom = w->height - 1; - window_scenery_widgets[WIDX_SCENERY_TITLE].right = w->width - 2; - window_scenery_widgets[WIDX_SCENERY_CLOSE].left = w->width - 13; - window_scenery_widgets[WIDX_SCENERY_CLOSE].right = window_scenery_widgets[WIDX_SCENERY_CLOSE].left + 10; - window_scenery_widgets[WIDX_SCENERY_LIST].right = w->width - 26; - window_scenery_widgets[WIDX_SCENERY_LIST].bottom = w->height - 14; + auto windowWidth = w->width; + if (_tabEntries.size() > 0) + { + auto lastTabIndex = _tabEntries.size() - 1; + const auto lastTabWidget = &w->widgets[WIDX_SCENERY_TAB_1 + lastTabIndex]; + windowWidth = std::max(windowWidth, lastTabWidget->right + 3); + } + w->min_width = windowWidth; + w->max_width = windowWidth; - window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].left = w->width - 25; - window_scenery_widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].left = w->width - 25; - window_scenery_widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].left = w->width - 25; - window_scenery_widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].left = w->width - 25; - window_scenery_widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].right = w->width - 2; - window_scenery_widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].right = w->width - 2; - window_scenery_widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].right = w->width - 2; - window_scenery_widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].right = w->width - 2; + w->widgets[WIDX_SCENERY_BACKGROUND].right = windowWidth - 1; + w->widgets[WIDX_SCENERY_BACKGROUND].bottom = w->height - 1; + w->widgets[WIDX_SCENERY_TAB_CONTENT_PANEL].right = windowWidth - 1; + w->widgets[WIDX_SCENERY_TAB_CONTENT_PANEL].bottom = w->height - 1; + w->widgets[WIDX_SCENERY_TITLE].right = windowWidth - 2; + w->widgets[WIDX_SCENERY_CLOSE].left = windowWidth - 13; + w->widgets[WIDX_SCENERY_CLOSE].right = w->widgets[WIDX_SCENERY_CLOSE].left + 10; + w->widgets[WIDX_SCENERY_LIST].right = windowWidth - 26; + w->widgets[WIDX_SCENERY_LIST].bottom = w->height - 14; - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].left = w->width - 19; - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].left = w->width - 19; - window_scenery_widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].left = w->width - 19; - window_scenery_widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].right = w->width - 8; - window_scenery_widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].right = w->width - 8; - window_scenery_widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].right = w->width - 8; + w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].left = windowWidth - 25; + w->widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].left = windowWidth - 25; + w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].left = windowWidth - 25; + w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].left = windowWidth - 25; + w->widgets[WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].right = windowWidth - 2; + w->widgets[WIDX_SCENERY_REPAINT_SCENERY_BUTTON].right = windowWidth - 2; + w->widgets[WIDX_SCENERY_EYEDROPPER_BUTTON].right = windowWidth - 2; + w->widgets[WIDX_SCENERY_BUILD_CLUSTER_BUTTON].right = windowWidth - 2; + + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].left = windowWidth - 19; + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].left = windowWidth - 19; + w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].left = windowWidth - 19; + w->widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].right = windowWidth - 8; + w->widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].right = windowWidth - 8; + w->widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].right = windowWidth - 8; +} + +static std::pair window_scenery_get_name_and_price(ScenerySelection selectedScenery) +{ + rct_string_id name = STR_UNKNOWN_OBJECT_TYPE; + money32 price = MONEY32_UNDEFINED; + if (selectedScenery.IsUndefined() && gSceneryPlaceCost != MONEY32_UNDEFINED) + { + price = gSceneryPlaceCost; + } + else + { + switch (selectedScenery.SceneryType) + { + case SCENERY_TYPE_SMALL: + { + auto* sceneryEntry = get_small_scenery_entry(selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price * 10; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_PATH_ITEM: + { + auto* sceneryEntry = get_footpath_item_entry(selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_WALL: + { + auto* sceneryEntry = get_wall_entry(selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_LARGE: + { + auto* sceneryEntry = get_large_scenery_entry(selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price * 10; + name = sceneryEntry->name; + } + break; + } + case SCENERY_TYPE_BANNER: + { + auto* sceneryEntry = get_banner_entry(selectedScenery.EntryIndex); + if (sceneryEntry != nullptr) + { + price = sceneryEntry->price; + name = sceneryEntry->name; + } + break; + } + } + } + return { name, price }; } /** @@ -1105,17 +1170,18 @@ void window_scenery_paint(rct_window* w, rct_drawpixelinfo* dpi) { WindowDrawWidgets(w, dpi); - uint16_t tabIndex = gWindowSceneryActiveTabIndex; - uint16_t selectedWidgetId = tabIndex + 4; - uint32_t imageId = ((w->colours[1] << 19) | window_scenery_widgets[selectedWidgetId].image) + 1ul; + auto tabIndex = gWindowSceneryActiveTabIndex; + if (tabIndex < _tabEntries.size()) + { + auto selectedWidgetId = static_cast(WIDX_SCENERY_TAB_1 + tabIndex); + uint32_t imageId = ((w->colours[1] << 19) | w->widgets[selectedWidgetId].image) + 1ul; - gfx_draw_sprite( - dpi, imageId, - w->windowPos - + ScreenCoordsXY{ window_scenery_widgets[selectedWidgetId].left, window_scenery_widgets[selectedWidgetId].top }, - selectedWidgetId); + gfx_draw_sprite( + dpi, imageId, w->windowPos + ScreenCoordsXY{ w->widgets[selectedWidgetId].left, w->widgets[selectedWidgetId].top }, + selectedWidgetId); + } - ScenerySelection selectedSceneryEntry = w->scenery.SelectedScenery; + auto selectedSceneryEntry = _selectedScenery; if (selectedSceneryEntry.IsUndefined()) { if (gWindowSceneryPaintEnabled & 1) // repaint coloured scenery tool is on @@ -1123,62 +1189,16 @@ void window_scenery_paint(rct_window* w, rct_drawpixelinfo* dpi) if (gWindowSceneryEyedropperEnabled) return; - selectedSceneryEntry = gWindowSceneryTabSelections[tabIndex]; - + selectedSceneryEntry = GetSelectedScenery(tabIndex); if (selectedSceneryEntry.IsUndefined()) return; } - uint32_t price = 0; - rct_string_id name = STR_UNKNOWN_OBJECT_TYPE; - switch (selectedSceneryEntry.SceneryType) - { - case SCENERY_TYPE_SMALL: - { - auto* sceneryEntry = get_small_scenery_entry(selectedSceneryEntry.EntryIndex); - price = sceneryEntry->price * 10; - name = sceneryEntry->name; - break; - } - case SCENERY_TYPE_PATH_ITEM: - { - auto* pathBitEntry = get_footpath_item_entry(selectedSceneryEntry.EntryIndex); - price = pathBitEntry->price; - name = pathBitEntry->name; - break; - } - case SCENERY_TYPE_WALL: - { - auto* wallEntry = get_wall_entry(selectedSceneryEntry.EntryIndex); - price = wallEntry->price; - name = wallEntry->name; - break; - } - case SCENERY_TYPE_LARGE: - { - auto* sceneryEntry = get_large_scenery_entry(selectedSceneryEntry.EntryIndex); - price = sceneryEntry->price * 10; - name = sceneryEntry->name; - break; - } - case SCENERY_TYPE_BANNER: - { - auto* bannerEntry = get_banner_entry(selectedSceneryEntry.EntryIndex); - price = bannerEntry->price; - name = bannerEntry->name; - break; - } - } - - if (w->scenery.SelectedScenery.IsUndefined() && gSceneryPlaceCost != MONEY32_UNDEFINED) - { - price = gSceneryPlaceCost; - } - - if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) + auto [name, price] = window_scenery_get_name_and_price(selectedSceneryEntry); + if (price != MONEY32_UNDEFINED && !(gParkFlags & PARK_FLAGS_NO_MONEY)) { auto ft = Formatter(); - ft.Add(price); + ft.Add(price); // -14 DrawTextBasic( @@ -1191,29 +1211,136 @@ void window_scenery_paint(rct_window* w, rct_drawpixelinfo* dpi) DrawTextEllipsised(dpi, { w->windowPos.x + 3, w->windowPos.y + w->height - 13 }, w->width - 19, STR_BLACK_STRING, ft); } -/** - * - * rct2: 0x006E15ED - */ +static void window_scenery_scrollpaint_item(rct_window* w, rct_drawpixelinfo* dpi, ScenerySelection scenerySelection) +{ + if (scenerySelection.SceneryType == SCENERY_TYPE_BANNER) + { + auto bannerEntry = get_banner_entry(scenerySelection.EntryIndex); + uint32_t imageId = bannerEntry->image + gWindowSceneryRotation * 2; + imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; + + gfx_draw_sprite(dpi, imageId, { 0x21, 0x28 }, w->colours[1]); + gfx_draw_sprite(dpi, imageId + 1, { 0x21, 0x28 }, w->colours[1]); + } + else if (scenerySelection.SceneryType == SCENERY_TYPE_LARGE) + { + auto sceneryEntry = get_large_scenery_entry(scenerySelection.EntryIndex); + uint32_t imageId = sceneryEntry->image + gWindowSceneryRotation; + imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; + imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; + + gfx_draw_sprite(dpi, imageId, { 0x21, 0 }, w->colours[1]); + } + else if (scenerySelection.SceneryType == SCENERY_TYPE_WALL) + { + auto wallEntry = get_wall_entry(scenerySelection.EntryIndex); + uint32_t imageId = wallEntry->image; + uint8_t tertiaryColour = w->colours[1]; + uint16_t spriteTop = (wallEntry->height * 2) + 0x32; + + if (wallEntry->flags & WALL_SCENERY_HAS_GLASS) + { + imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; + + if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) + { + imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; + } + gfx_draw_sprite(dpi, imageId, { 0x2F, spriteTop }, tertiaryColour); + + imageId = (wallEntry->image + 0x40000006) | (EnumValue(GlassPaletteIds[gWindowSceneryPrimaryColour]) << 19); + gfx_draw_sprite(dpi, imageId, { 0x2F, spriteTop }, tertiaryColour); + } + else + { + imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; + + if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) + { + imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; + + if (wallEntry->flags & WALL_SCENERY_HAS_TERNARY_COLOUR) + { + imageId &= 0xDFFFFFFF; + tertiaryColour = gWindowSceneryTertiaryColour; + } + } + gfx_draw_sprite(dpi, imageId, { 0x2F, spriteTop }, tertiaryColour); + + if (wallEntry->flags & WALL_SCENERY_IS_DOOR) + { + gfx_draw_sprite(dpi, imageId + 1, { 0x2F, spriteTop }, tertiaryColour); + } + } + } + else if (scenerySelection.SceneryType == SCENERY_TYPE_PATH_ITEM) + { + auto* pathBitEntry = get_footpath_item_entry(scenerySelection.EntryIndex); + uint32_t imageId = pathBitEntry->image; + + gfx_draw_sprite(dpi, imageId, { 0x0B, 0x10 }, w->colours[1]); + } + else + { + auto sceneryEntry = get_small_scenery_entry(scenerySelection.EntryIndex); + uint32_t imageId = sceneryEntry->image + gWindowSceneryRotation; + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) + { + imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) + { + imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; + } + } + + uint16_t spriteTop = (sceneryEntry->height / 4) + 0x2B; + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) + { + spriteTop -= 0x0C; + } + + gfx_draw_sprite(dpi, imageId, { 0x20, spriteTop }, w->colours[1]); + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_GLASS)) + { + imageId = ((sceneryEntry->image + gWindowSceneryRotation) + 0x40000004) + + (EnumValue(GlassPaletteIds[gWindowSceneryPrimaryColour]) << 19); + + gfx_draw_sprite(dpi, imageId, { 0x20, spriteTop }, w->colours[1]); + } + + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED_FG)) + { + imageId = (sceneryEntry->image + gWindowSceneryRotation) + 4; + gfx_draw_sprite(dpi, imageId, { 0x20, spriteTop }, w->colours[1]); + } + } +} + void window_scenery_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t scrollIndex) { gfx_clear(dpi, ColourMapA[w->colours[1]].mid_light); - uint8_t tabIndex = gWindowSceneryActiveTabIndex; + auto numColumns = window_scenery_get_num_columns(w); + auto tabIndex = gWindowSceneryActiveTabIndex; + if (tabIndex > _tabEntries.size()) + { + return; + } ScreenCoordsXY topLeft{ 0, 0 }; - for (int32_t sceneryTabItemIndex = 0; sceneryTabItemIndex < SCENERY_ENTRIES_PER_TAB + 1; ++sceneryTabItemIndex) + const auto& tabInfo = _tabEntries[tabIndex]; + for (size_t sceneryTabItemIndex = 0; sceneryTabItemIndex < tabInfo.Entries.size(); sceneryTabItemIndex++) { - ScenerySelection currentSceneryGlobal = window_scenery_tab_entries[tabIndex][sceneryTabItemIndex]; - if (currentSceneryGlobal.IsUndefined()) - break; - - ScenerySelection tabSelectedScenery = gWindowSceneryTabSelections[tabIndex]; - + const auto& currentSceneryGlobal = tabInfo.Entries[sceneryTabItemIndex]; + auto tabSelectedScenery = GetSelectedScenery(tabIndex); if (gWindowSceneryPaintEnabled == 1 || gWindowSceneryEyedropperEnabled) { - if (w->scenery.SelectedScenery == currentSceneryGlobal) + if (_selectedScenery == currentSceneryGlobal) { gfx_fill_rect_inset( dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, @@ -1228,7 +1355,7 @@ void window_scenery_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t s dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, w->colours[1], (INSET_RECT_FLAG_BORDER_INSET | INSET_RECT_FLAG_FILL_MID_LIGHT)); } - else if (w->scenery.SelectedScenery == currentSceneryGlobal) + else if (_selectedScenery == currentSceneryGlobal) { gfx_fill_rect_inset( dpi, { topLeft, topLeft + ScreenCoordsXY{ SCENERY_BUTTON_WIDTH - 1, SCENERY_BUTTON_HEIGHT - 1 } }, @@ -1240,116 +1367,11 @@ void window_scenery_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t s if (clip_drawpixelinfo( &clipdpi, dpi, topLeft + ScreenCoordsXY{ 1, 1 }, SCENERY_BUTTON_WIDTH - 2, SCENERY_BUTTON_HEIGHT - 2)) { - if (currentSceneryGlobal.SceneryType == SCENERY_TYPE_BANNER) - { - auto* bannerEntry = get_banner_entry(currentSceneryGlobal.EntryIndex); - uint32_t imageId = bannerEntry->image + gWindowSceneryRotation * 2; - imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; - - gfx_draw_sprite(&clipdpi, imageId, { 0x21, 0x28 }, w->colours[1]); - gfx_draw_sprite(&clipdpi, imageId + 1, { 0x21, 0x28 }, w->colours[1]); - } - else if (currentSceneryGlobal.SceneryType == SCENERY_TYPE_LARGE) - { - auto* sceneryEntry = get_large_scenery_entry(currentSceneryGlobal.EntryIndex); - uint32_t imageId = sceneryEntry->image + gWindowSceneryRotation; - imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; - imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; - - gfx_draw_sprite(&clipdpi, imageId, { 0x21, 0 }, w->colours[1]); - } - else if (currentSceneryGlobal.SceneryType == SCENERY_TYPE_WALL) - { - auto* wallEntry = get_wall_entry(currentSceneryGlobal.EntryIndex); - uint32_t imageId = wallEntry->image; - uint8_t tertiaryColour = w->colours[1]; - uint16_t spriteTop = (wallEntry->height * 2) + 0x32; - - if (wallEntry->flags & WALL_SCENERY_HAS_GLASS) - { - imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; - - if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) - { - imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; - } - gfx_draw_sprite(&clipdpi, imageId, { 0x2F, spriteTop }, tertiaryColour); - - imageId = (wallEntry->image + 0x40000006) | (EnumValue(GlassPaletteIds[gWindowSceneryPrimaryColour]) << 19); - gfx_draw_sprite(&clipdpi, imageId, { 0x2F, spriteTop }, tertiaryColour); - } - else - { - imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; - - if (wallEntry->flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) - { - imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; - - if (wallEntry->flags & WALL_SCENERY_HAS_TERNARY_COLOUR) - { - imageId &= 0xDFFFFFFF; - tertiaryColour = gWindowSceneryTertiaryColour; - } - } - gfx_draw_sprite(&clipdpi, imageId, { 0x2F, spriteTop }, tertiaryColour); - - if (wallEntry->flags & WALL_SCENERY_IS_DOOR) - { - gfx_draw_sprite(&clipdpi, imageId + 1, { 0x2F, spriteTop }, tertiaryColour); - } - } - } - else if (currentSceneryGlobal.SceneryType == SCENERY_TYPE_PATH_ITEM) - { - auto* pathBitEntry = get_footpath_item_entry(currentSceneryGlobal.EntryIndex); - uint32_t imageId = pathBitEntry->image; - - gfx_draw_sprite(&clipdpi, imageId, { 0x0B, 0x10 }, w->colours[1]); - } - else - { - auto* sceneryEntry = get_small_scenery_entry(currentSceneryGlobal.EntryIndex); - uint32_t imageId = sceneryEntry->image + gWindowSceneryRotation; - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) - { - imageId |= (gWindowSceneryPrimaryColour << 19) | IMAGE_TYPE_REMAP; - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) - { - imageId |= (gWindowScenerySecondaryColour << 24) | IMAGE_TYPE_REMAP_2_PLUS; - } - } - - uint16_t spriteTop = (sceneryEntry->height / 4) + 0x2B; - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) - && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) - { - spriteTop -= 0x0C; - } - - gfx_draw_sprite(&clipdpi, imageId, { 0x20, spriteTop }, w->colours[1]); - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_GLASS)) - { - imageId = ((sceneryEntry->image + gWindowSceneryRotation) + 0x40000004) - + (EnumValue(GlassPaletteIds[gWindowSceneryPrimaryColour]) << 19); - - gfx_draw_sprite(&clipdpi, imageId, { 0x20, spriteTop }, w->colours[1]); - } - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED_FG)) - { - imageId = (sceneryEntry->image + gWindowSceneryRotation) + 4; - gfx_draw_sprite(&clipdpi, imageId, { 0x20, spriteTop }, w->colours[1]); - } - } + window_scenery_scrollpaint_item(w, &clipdpi, currentSceneryGlobal); } topLeft.x += SCENERY_BUTTON_WIDTH; - if (topLeft.x >= 594) + if (topLeft.x >= numColumns * SCENERY_BUTTON_WIDTH) { topLeft.y += SCENERY_BUTTON_HEIGHT; topLeft.x = 0; @@ -1357,36 +1379,20 @@ void window_scenery_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi, int32_t s } } -static int32_t window_scenery_find_tab_with_scenery(const ScenerySelection& scenery) -{ - for (int32_t i = 0; i < SCENERY_WINDOW_TABS; i++) - { - for (int32_t j = 0; j < SCENERY_ENTRIES_PER_TAB; j++) - { - ScenerySelection entry = window_scenery_tab_entries[i][j]; - if (entry.IsUndefined()) - break; - if (entry == scenery) - return i; - } - } - return -1; -} - bool window_scenery_set_selected_item(const ScenerySelection& scenery) { bool result = false; rct_window* w = window_bring_to_front_by_class(WC_SCENERY); if (w != nullptr) { - int32_t tabIndex = window_scenery_find_tab_with_scenery(scenery); - if (tabIndex != -1) + auto tabIndex = window_scenery_find_tab_with_scenery(scenery); + if (tabIndex) { - gWindowSceneryActiveTabIndex = tabIndex; - gWindowSceneryTabSelections[tabIndex] = scenery; + gWindowSceneryActiveTabIndex = *tabIndex; + SetSelectedScenery(*tabIndex, scenery); OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::Click1, 0, context_get_width() / 2); - w->scenery.hover_counter = -16; + _hoverCounter = -16; gSceneryPlaceCost = MONEY32_UNDEFINED; w->Invalidate(); result = true; @@ -1398,8 +1404,5 @@ bool window_scenery_set_selected_item(const ScenerySelection& scenery) // Used after removing objects, in order to avoid crashes. void window_scenery_reset_selected_scenery_items() { - for (size_t i = 0; i < SCENERY_WINDOW_TABS; i++) - { - gWindowSceneryTabSelections[i].SetUndefined(); - } + gWindowSceneryTabSelections.clear(); } diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index 01e01ecda7..2bb0b19327 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -1725,13 +1725,11 @@ static void window_top_toolbar_scenery_tool_down(const ScreenCoordsXY& windowPos return; } - ScenerySelection selectedTab = gWindowSceneryTabSelections[gWindowSceneryActiveTabIndex]; + auto selectedTab = gWindowSceneryTabSelections.size() > gWindowSceneryActiveTabIndex + ? gWindowSceneryTabSelections[gWindowSceneryActiveTabIndex] + : ScenerySelection{}; uint8_t sceneryType = selectedTab.SceneryType; uint16_t selectedScenery = selectedTab.EntryIndex; - - if (selectedTab.IsUndefined()) - return; - CoordsXY gridPos; switch (sceneryType) @@ -2623,8 +2621,12 @@ static void top_toolbar_tool_update_scenery(const ScreenCoordsXY& screenPos) if (gWindowSceneryEyedropperEnabled) return; - ScenerySelection selection = gWindowSceneryTabSelections[gWindowSceneryActiveTabIndex]; - + if (gWindowSceneryActiveTabIndex >= gWindowSceneryTabSelections.size()) + { + scenery_remove_ghost_tool_placement(); + return; + } + const auto& selection = gWindowSceneryTabSelections[gWindowSceneryActiveTabIndex]; if (selection.IsUndefined()) { scenery_remove_ghost_tool_placement(); diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 408e5bda6c..b0fbad775e 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -23,8 +23,8 @@ struct Vehicle; enum class GuestListFilterType : int32_t; enum class ScatterToolDensity : uint8_t; -extern ScenerySelection gWindowSceneryTabSelections[]; -extern uint8_t gWindowSceneryActiveTabIndex; +extern std::vector gWindowSceneryTabSelections; +extern size_t gWindowSceneryActiveTabIndex; extern bool gWindowSceneryScatterEnabled; extern uint16_t gWindowSceneryScatterSize; extern ScatterToolDensity gWindowSceneryScatterDensity; diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index f6fbbba70c..ec92b3e0e3 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -309,12 +309,6 @@ struct ride_variables int32_t var_486; }; -struct scenery_variables -{ - ScenerySelection SelectedScenery; - int16_t hover_counter; -}; - struct track_list_variables { bool track_list_being_updated; @@ -525,9 +519,9 @@ enum #define WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE 29 #define WC_RIDE_CONSTRUCTION__WIDX_EXIT 30 #define WC_RIDE_CONSTRUCTION__WIDX_ROTATE 32 -#define WC_SCENERY__WIDX_SCENERY_TAB_1 4 -#define WC_SCENERY__WIDX_SCENERY_ROTATE_OBJECTS_BUTTON 25 -#define WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON 30 +#define WC_SCENERY__WIDX_SCENERY_TAB_1 12 +#define WC_SCENERY__WIDX_SCENERY_ROTATE_OBJECTS_BUTTON 5 +#define WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON 10 #define WC_PEEP__WIDX_PATROL 10 #define WC_PEEP__WIDX_ACTION_LBL 13 #define WC_PEEP__WIDX_PICKUP 14 diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index 56dab05199..3392bd0bda 100644 --- a/src/openrct2/interface/Window_internal.h +++ b/src/openrct2/interface/Window_internal.h @@ -63,7 +63,6 @@ struct rct_window news_variables news; map_variables map; ride_variables ride; - scenery_variables scenery; track_list_variables track_list; error_variables error; void* custom_info; diff --git a/src/openrct2/world/Scenery.cpp b/src/openrct2/world/Scenery.cpp index 1f61117c87..451398b638 100644 --- a/src/openrct2/world/Scenery.cpp +++ b/src/openrct2/world/Scenery.cpp @@ -26,6 +26,7 @@ #include "Climate.h" #include "Footpath.h" #include "Fountain.h" +#include "LargeScenery.h" #include "Map.h" #include "Park.h" #include "SmallScenery.h" @@ -52,6 +53,8 @@ int16_t gSceneryCtrlPressZ; money64 gClearSceneryCost; +static std::vector _restrictedScenery; + // rct2: 0x009A3E74 const CoordsXY SceneryQuadrantOffsets[] = { { 7, 7 }, { 7, 23 }, { 23, 23 }, { 23, 7 } }; @@ -280,3 +283,111 @@ int32_t wall_entry_get_door_sound(const WallSceneryEntry* wallEntry) { return (wallEntry->flags2 & WALL_SCENERY_2_DOOR_SOUND_MASK) >> WALL_SCENERY_2_DOOR_SOUND_SHIFT; } + +bool IsSceneryAvailableToBuild(ScenerySelection item) +{ + if (!gCheatsIgnoreResearchStatus) + { + if (!scenery_is_invented(item)) + { + return false; + } + } + + if (!gCheatsSandboxMode) + { + if (IsSceneryItemRestricted(item)) + { + return false; + } + } + + return true; +} + +static size_t GetMaxObjectsForSceneryType(uint8_t sceneryType) +{ + switch (sceneryType) + { + case SCENERY_TYPE_SMALL: + return MAX_SMALL_SCENERY_OBJECTS; + case SCENERY_TYPE_PATH_ITEM: + return MAX_PATH_ADDITION_OBJECTS; + case SCENERY_TYPE_WALL: + return MAX_WALL_SCENERY_OBJECTS; + case SCENERY_TYPE_LARGE: + return MAX_LARGE_SCENERY_OBJECTS; + case SCENERY_TYPE_BANNER: + return MAX_BANNER_OBJECTS; + default: + return 0; + } +} + +static SceneryEntryBase* GetSceneryEntry(ScenerySelection item) +{ + switch (item.SceneryType) + { + case SCENERY_TYPE_SMALL: + return get_small_scenery_entry(item.EntryIndex); + case SCENERY_TYPE_PATH_ITEM: + return get_footpath_item_entry(item.EntryIndex); + case SCENERY_TYPE_WALL: + return get_wall_entry(item.EntryIndex); + case SCENERY_TYPE_LARGE: + return get_large_scenery_entry(item.EntryIndex); + case SCENERY_TYPE_BANNER: + return get_banner_entry(item.EntryIndex); + default: + return nullptr; + } +} + +bool IsSceneryItemRestricted(ScenerySelection item) +{ + auto it = std::find(_restrictedScenery.begin(), _restrictedScenery.end(), item); + return it != _restrictedScenery.end(); +} + +void ClearRestrictedScenery() +{ + _restrictedScenery.clear(); +} + +std::vector& GetRestrictedScenery() +{ + return _restrictedScenery; +} + +void RestrictAllMiscScenery() +{ + std::vector nonMiscScenery; + for (ObjectEntryIndex i = 0; i < MAX_SCENERY_GROUP_OBJECTS; i++) + { + auto sgEntry = get_scenery_group_entry(i); + if (sgEntry != nullptr) + { + for (size_t j = 0; j < sgEntry->entry_count; j++) + { + nonMiscScenery.push_back(sgEntry->scenery_entries[j]); + } + } + } + for (uint8_t sceneryType = SCENERY_TYPE_SMALL; sceneryType < SCENERY_TYPE_COUNT; sceneryType++) + { + auto maxObjects = GetMaxObjectsForSceneryType(sceneryType); + for (ObjectEntryIndex i = 0; i < maxObjects; i++) + { + ScenerySelection sceneryItem = { sceneryType, i }; + auto sceneryEntry = GetSceneryEntry(sceneryItem); + if (sceneryEntry != nullptr) + { + auto it = std::find(nonMiscScenery.begin(), nonMiscScenery.end(), sceneryItem); + if (it == nonMiscScenery.end()) + { + _restrictedScenery.push_back(sceneryItem); + } + } + } + } +} diff --git a/src/openrct2/world/Scenery.h b/src/openrct2/world/Scenery.h index 03e1aca011..93ebebb187 100644 --- a/src/openrct2/world/Scenery.h +++ b/src/openrct2/world/Scenery.h @@ -294,3 +294,10 @@ PathBitEntry* get_footpath_item_entry(ObjectEntryIndex entryIndex); rct_scenery_group_entry* get_scenery_group_entry(ObjectEntryIndex entryIndex); int32_t wall_entry_get_door_sound(const WallSceneryEntry* wallEntry); + +bool IsSceneryAvailableToBuild(ScenerySelection item); + +bool IsSceneryItemRestricted(ScenerySelection item); +void ClearRestrictedScenery(); +void RestrictAllMiscScenery(); +std::vector& GetRestrictedScenery(); diff --git a/src/openrct2/world/ScenerySelection.h b/src/openrct2/world/ScenerySelection.h index 7c3a7a83b2..716298b596 100644 --- a/src/openrct2/world/ScenerySelection.h +++ b/src/openrct2/world/ScenerySelection.h @@ -18,26 +18,16 @@ constexpr auto WINDOW_SCENERY_TAB_SELECTION_UNDEFINED = std::numeric_limits