diff --git a/src/openrct2-ui/windows/EditorInventionsList.cpp b/src/openrct2-ui/windows/EditorInventionsList.cpp index ea7004781c0e..320502476b76 100644 --- a/src/openrct2-ui/windows/EditorInventionsList.cpp +++ b/src/openrct2-ui/windows/EditorInventionsList.cpp @@ -84,7 +84,7 @@ static void window_editor_inventions_list_drag_cursor(rct_window *w, rct_widgeti static void window_editor_inventions_list_drag_moved(rct_window* w, int32_t x, int32_t y); static void window_editor_inventions_list_drag_paint(rct_window *w, rct_drawpixelinfo *dpi); -static rct_string_id window_editor_inventions_list_prepare_name(const rct_research_item * researchItem, bool withGap); +static rct_string_id window_editor_inventions_list_prepare_name(const ResearchItem * researchItem, bool withGap); // 0x0098177C static rct_window_event_list window_editor_inventions_list_events = { @@ -152,7 +152,7 @@ static rct_window_event_list window_editor_inventions_list_drag_events = { #pragma endregion -static rct_research_item *_editorInventionsListDraggedItem; +static ResearchItem _editorInventionsListDraggedItem; static constexpr const rct_string_id EditorInventionsResearchCategories[] = { STR_RESEARCH_NEW_TRANSPORT_RIDES, @@ -165,8 +165,8 @@ static constexpr const rct_string_id EditorInventionsResearchCategories[] = { }; // clang-format on -static void window_editor_inventions_list_drag_open(rct_research_item* researchItem); -static void move_research_item(rct_research_item* beforeItem); +static void window_editor_inventions_list_drag_open(ResearchItem* researchItem); +static void move_research_item(ResearchItem* beforeItem, int32_t scrollIndex); /** * @@ -196,72 +196,47 @@ static void research_rides_setup() * * rct2: 0x006855E7 */ -static void move_research_item(rct_research_item* beforeItem) +static void move_research_item(ResearchItem* beforeItem, int32_t scrollIndex) { - rct_window* w; - rct_research_item *researchItem, draggedItem; - - if (_editorInventionsListDraggedItem + 1 == beforeItem) - return; - - // Back up the dragged item - draggedItem = *_editorInventionsListDraggedItem; - - // Remove dragged item from list - researchItem = _editorInventionsListDraggedItem; - do - { - *researchItem = *(researchItem + 1); - researchItem++; - } while (researchItem->rawValue != RESEARCHED_ITEMS_END_2); - // At end of this researchItem points to the end of the list - - if (beforeItem > _editorInventionsListDraggedItem) - beforeItem--; - - // Add dragged item to list - do - { - *researchItem = *(researchItem - 1); - researchItem--; - } while (researchItem != beforeItem); - - *researchItem = draggedItem; - - w = window_find_by_class(WC_EDITOR_INVENTION_LIST); + auto w = window_find_by_class(WC_EDITOR_INVENTION_LIST); if (w != nullptr) { w->research_item = nullptr; w->Invalidate(); } -} -/** - * - * rct2: 0x0068558E - */ -static rct_research_item* window_editor_inventions_list_get_item_from_scroll_y(int32_t scrollIndex, int32_t y) -{ - rct_research_item* researchItem; - - researchItem = gResearchItems; + research_remove(&_editorInventionsListDraggedItem); - if (scrollIndex != 0) + auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + if (beforeItem != nullptr) { - // Skip pre-researched items - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) + for (size_t i = 0; i < researchList.size(); i++) { + if (researchList[i].Equals(beforeItem)) + { + researchList.insert((researchList.begin() + i), _editorInventionsListDraggedItem); + return; + } } - researchItem++; } - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR && researchItem->rawValue != RESEARCHED_ITEMS_END; - researchItem++) + // Still not found? Append to end of list. + researchList.push_back(_editorInventionsListDraggedItem); +} + +/** + * + * rct2: 0x0068558E + */ +static ResearchItem* window_editor_inventions_list_get_item_from_scroll_y(int32_t scrollIndex, int32_t y) +{ + auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + for (auto& researchItem : researchList) { y -= SCROLLABLE_ROW_HEIGHT; if (y < 0) { - return researchItem; + return &researchItem; } } @@ -272,35 +247,21 @@ static rct_research_item* window_editor_inventions_list_get_item_from_scroll_y(i * * rct2: 0x006855BB */ -static rct_research_item* window_editor_inventions_list_get_item_from_scroll_y_include_seps(int32_t scrollIndex, int32_t y) +static ResearchItem* window_editor_inventions_list_get_item_from_scroll_y_include_seps(int32_t scrollIndex, int32_t y) { - rct_research_item* researchItem; - - researchItem = gResearchItems; - - if (scrollIndex != 0) - { - // Skip pre-researched items - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - researchItem++; - } - - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR && researchItem->rawValue != RESEARCHED_ITEMS_END; - researchItem++) + auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + for (auto& researchItem : researchList) { y -= SCROLLABLE_ROW_HEIGHT; if (y < 0) { - return researchItem; + return &researchItem; } } - - return researchItem; + return nullptr; } -static rct_research_item* get_research_item_at(int32_t x, int32_t y) +static ResearchItem* get_research_item_at(int32_t x, int32_t y, int32_t* outScrollId) { rct_window* w = window_find_by_class(WC_EDITOR_INVENTION_LIST); if (w != nullptr && w->x <= x && w->y < y && w->x + w->width > x && w->y + w->height > y) @@ -310,18 +271,19 @@ static rct_research_item* get_research_item_at(int32_t x, int32_t y) if (widgetIndex == WIDX_PRE_RESEARCHED_SCROLL || widgetIndex == WIDX_RESEARCH_ORDER_SCROLL) { gPressedWidget.widget_index = widgetIndex; - int32_t outX, outY, outScrollArea, outScrollId; - widget_scroll_get_part(w, widget, x, y, &outX, &outY, &outScrollArea, &outScrollId); + int32_t outX, outY, outScrollArea; + widget_scroll_get_part(w, widget, x, y, &outX, &outY, &outScrollArea, outScrollId); if (outScrollArea == SCROLL_PART_VIEW) { - outScrollId = outScrollId == 0 ? 0 : 1; + *outScrollId = *outScrollId == 0 ? 0 : 1; - int32_t scrollY = y - (w->y + widget->top) + w->scrolls[outScrollId].v_top + 5; - return window_editor_inventions_list_get_item_from_scroll_y_include_seps(outScrollId, scrollY); + int32_t scrollY = y - (w->y + widget->top) + w->scrolls[*outScrollId].v_top + 5; + return window_editor_inventions_list_get_item_from_scroll_y_include_seps(*outScrollId, scrollY); } } } + *outScrollId = -1; return nullptr; } @@ -348,7 +310,7 @@ rct_window* window_editor_inventions_list_open() w->var_4AE = 0; w->selected_tab = 0; w->research_item = nullptr; - _editorInventionsListDraggedItem = nullptr; + _editorInventionsListDraggedItem.rawValue = -1; w->min_width = WW; w->min_height = WH; @@ -427,13 +389,13 @@ static void window_editor_inventions_list_update(rct_window* w) window_event_invalidate_call(w); widget_invalidate(w, WIDX_TAB_1); - if (_editorInventionsListDraggedItem == nullptr) + if (_editorInventionsListDraggedItem.IsInventedEndMarker()) return; if (window_find_by_class(WC_EDITOR_INVENTION_LIST_DRAG) != nullptr) return; - _editorInventionsListDraggedItem = nullptr; + _editorInventionsListDraggedItem.rawValue = -1; w->Invalidate(); } @@ -443,22 +405,14 @@ static void window_editor_inventions_list_update(rct_window* w) */ static void window_editor_inventions_list_scrollgetheight(rct_window* w, int32_t scrollIndex, int32_t* width, int32_t* height) { - rct_research_item* researchItem; - *height = 0; - - // Count / skip pre-researched items - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - *height += SCROLLABLE_ROW_HEIGHT; - - if (scrollIndex == 1) + if (scrollIndex == 0) { - researchItem++; - - // Count non pre-researched items - *height = 0; - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) - *height += SCROLLABLE_ROW_HEIGHT; + *height += (int32_t)gResearchItemsInvented.size() * SCROLLABLE_ROW_HEIGHT; + } + else + { + *height += (int32_t)gResearchItemsUninvented.size() * SCROLLABLE_ROW_HEIGHT; } } @@ -468,14 +422,14 @@ static void window_editor_inventions_list_scrollgetheight(rct_window* w, int32_t */ static void window_editor_inventions_list_scrollmousedown(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) { - rct_research_item* researchItem; + ResearchItem* researchItem; researchItem = window_editor_inventions_list_get_item_from_scroll_y(scrollIndex, y); if (researchItem == nullptr) return; // Disallow picking up always-researched items - if (researchItem->rawValue < RESEARCHED_ITEMS_END_2 || research_item_is_always_researched(researchItem)) + if (research_item_is_always_researched(researchItem)) return; w->Invalidate(); @@ -488,7 +442,7 @@ static void window_editor_inventions_list_scrollmousedown(rct_window* w, int32_t */ static void window_editor_inventions_list_scrollmouseover(rct_window* w, int32_t scrollIndex, int32_t x, int32_t y) { - rct_research_item* researchItem; + ResearchItem* researchItem; researchItem = window_editor_inventions_list_get_item_from_scroll_y(scrollIndex, y); if (researchItem != w->research_item) @@ -511,7 +465,7 @@ static void window_editor_inventions_list_scrollmouseover(rct_window* w, int32_t static void window_editor_inventions_list_cursor( rct_window* w, rct_widgetindex widgetIndex, int32_t x, int32_t y, int32_t* cursorId) { - rct_research_item* researchItem; + ResearchItem* researchItem; int32_t scrollIndex; switch (widgetIndex) @@ -528,8 +482,7 @@ static void window_editor_inventions_list_cursor( // Use the open hand as cursor for items that can be picked up researchItem = window_editor_inventions_list_get_item_from_scroll_y(scrollIndex, y); - if (researchItem != nullptr && researchItem->rawValue >= RESEARCHED_ITEMS_END_2 - && !research_item_is_always_researched(researchItem)) + if (researchItem != nullptr && !research_item_is_always_researched(researchItem)) { *cursorId = CURSOR_HAND_OPEN; } @@ -589,7 +542,7 @@ static void window_editor_inventions_list_invalidate(rct_window* w) static void window_editor_inventions_list_paint(rct_window* w, rct_drawpixelinfo* dpi) { rct_widget* widget; - rct_research_item* researchItem; + ResearchItem* researchItem; rct_string_id stringId; int32_t x, y, width; @@ -616,8 +569,8 @@ static void window_editor_inventions_list_paint(rct_window* w, rct_drawpixelinfo dpi, w->x + widget->left + 1, w->y + widget->top + 1, w->x + widget->right - 1, w->y + widget->bottom - 1, ColourMapA[w->colours[1]].darkest); - researchItem = _editorInventionsListDraggedItem; - if (researchItem == nullptr) + researchItem = &_editorInventionsListDraggedItem; + if (researchItem->IsInventedEndMarker()) researchItem = w->research_item; // If the research item is null or a list separator. if (researchItem == nullptr || researchItem->rawValue < 0) @@ -676,36 +629,21 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix uint8_t paletteIndex = ColourMapA[w->colours[1]].mid_light; gfx_clear(dpi, paletteIndex); - rct_research_item* researchItem = gResearchItems; - int32_t researchItemEndMarker; - - if (scrollIndex == 1) - { - // Skip pre-researched items - for (; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - researchItem++; - researchItemEndMarker = RESEARCHED_ITEMS_END; - } - else - { - researchItemEndMarker = RESEARCHED_ITEMS_SEPARATOR; - } - int16_t boxWidth = (w->widgets[WIDX_RESEARCH_ORDER_SCROLL].right - w->widgets[WIDX_RESEARCH_ORDER_SCROLL].left); int16_t columnSplitOffset = boxWidth / 2; int32_t itemY = -SCROLLABLE_ROW_HEIGHT; - do + + const auto& researchList = scrollIndex == 0 ? gResearchItemsInvented : gResearchItemsUninvented; + for (const auto& researchItem : researchList) { itemY += SCROLLABLE_ROW_HEIGHT; if (itemY + SCROLLABLE_ROW_HEIGHT < dpi->y || itemY >= dpi->y + dpi->height) continue; - if (w->research_item == researchItem) + if (w->research_item == &researchItem) { int32_t top, bottom; - if (_editorInventionsListDraggedItem == nullptr) + if (_editorInventionsListDraggedItem.IsInventedEndMarker()) { // Highlight top = itemY; @@ -721,10 +659,7 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix gfx_filter_rect(dpi, 0, top, boxWidth, bottom, PALETTE_DARKEN_1); } - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR || researchItem->rawValue == RESEARCHED_ITEMS_END) - continue; - - if (researchItem == _editorInventionsListDraggedItem) + if (researchItem.Equals(&_editorInventionsListDraggedItem)) continue; utf8 groupNameBuffer[256], vehicleNameBuffer[256]; @@ -732,9 +667,9 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix utf8* vehicleNamePtr = vehicleNameBuffer; uint8_t colour; - if (research_item_is_always_researched(researchItem)) + if (research_item_is_always_researched(&researchItem)) { - if (w->research_item == researchItem && _editorInventionsListDraggedItem == nullptr) + if (w->research_item == &researchItem && _editorInventionsListDraggedItem.IsInventedEndMarker()) gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM_EXTRA_DARK; else gCurrentFontSpriteBase = FONT_SPRITE_BASE_MEDIUM_DARK; @@ -750,13 +685,13 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix vehicleNamePtr = utf8_write_codepoint(vehicleNamePtr, colour); } - rct_string_id itemNameId = research_item_get_name(researchItem); + rct_string_id itemNameId = research_item_get_name(&researchItem); - if (researchItem->type == RESEARCH_ENTRY_TYPE_RIDE - && !RideGroupManager::RideTypeIsIndependent(researchItem->baseRideType)) + if (researchItem.type == RESEARCH_ENTRY_TYPE_RIDE + && !RideGroupManager::RideTypeIsIndependent(researchItem.baseRideType)) { - const auto rideEntry = get_ride_entry(researchItem->entryIndex); - const rct_string_id rideGroupName = get_ride_naming(researchItem->baseRideType, rideEntry).name; + const auto rideEntry = get_ride_entry(researchItem.entryIndex); + const rct_string_id rideGroupName = get_ride_naming(researchItem.baseRideType, rideEntry).name; format_string( groupNamePtr, std::size(groupNameBuffer), STR_INVENTIONS_LIST_RIDE_AND_VEHICLE_NAME, (void*)&rideGroupName); format_string(vehicleNamePtr, std::size(vehicleNameBuffer), itemNameId, nullptr); @@ -777,7 +712,7 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix gfx_clip_string(vehicleNameBuffer, columnSplitOffset - 11); gfx_draw_string(dpi, vehicleNameBuffer, colour, columnSplitOffset + 1, itemY); } - } while (researchItem++->rawValue != researchItemEndMarker); + } } #pragma region Drag item @@ -786,14 +721,14 @@ static void window_editor_inventions_list_scrollpaint(rct_window* w, rct_drawpix * * rct2: 0x006852F4 */ -static void window_editor_inventions_list_drag_open(rct_research_item* researchItem) +static void window_editor_inventions_list_drag_open(ResearchItem* researchItem) { char buffer[256], *ptr; int32_t stringWidth; rct_window* w; window_close_by_class(WC_EDITOR_INVENTION_LIST_DRAG); - _editorInventionsListDraggedItem = researchItem; + _editorInventionsListDraggedItem = *researchItem; rct_string_id stringId = research_item_get_name(researchItem); ptr = buffer; @@ -833,7 +768,8 @@ static void window_editor_inventions_list_drag_cursor( rct_window* inventionListWindow = window_find_by_class(WC_EDITOR_INVENTION_LIST); if (inventionListWindow != nullptr) { - rct_research_item* researchItem = get_research_item_at(x, y); + int32_t scrollId; + ResearchItem* researchItem = get_research_item_at(x, y, &scrollId); if (researchItem != inventionListWindow->research_item) { inventionListWindow->Invalidate(); @@ -849,20 +785,23 @@ static void window_editor_inventions_list_drag_cursor( */ static void window_editor_inventions_list_drag_moved(rct_window* w, int32_t x, int32_t y) { - rct_research_item* researchItem; + ResearchItem* researchItem; + int32_t scrollId; // Skip always researched items, so that the dragged item gets placed underneath them do { - researchItem = get_research_item_at(x, y); + researchItem = get_research_item_at(x, y, &scrollId); y += LIST_ROW_HEIGHT; - } while (researchItem != nullptr && researchItem->rawValue >= 0 && research_item_is_always_researched(researchItem)); + } while (researchItem != nullptr && research_item_is_always_researched(researchItem)); - if (researchItem != nullptr) - move_research_item(researchItem); + if (scrollId != -1) + { + move_research_item(researchItem, scrollId); + } window_close(w); - _editorInventionsListDraggedItem = nullptr; + _editorInventionsListDraggedItem.rawValue = -1; window_invalidate_by_class(WC_EDITOR_INVENTION_LIST); } @@ -877,11 +816,11 @@ static void window_editor_inventions_list_drag_paint(rct_window* w, rct_drawpixe x = w->x; y = w->y + 2; - drawString = window_editor_inventions_list_prepare_name(_editorInventionsListDraggedItem, true); + drawString = window_editor_inventions_list_prepare_name(&_editorInventionsListDraggedItem, true); gfx_draw_string_left(dpi, drawString, gCommonFormatArgs, COLOUR_BLACK | COLOUR_FLAG_OUTLINE, x, y); } -static rct_string_id window_editor_inventions_list_prepare_name(const rct_research_item* researchItem, bool withGap) +static rct_string_id window_editor_inventions_list_prepare_name(const ResearchItem* researchItem, bool withGap) { rct_string_id drawString; rct_string_id stringId = research_item_get_name(researchItem); diff --git a/src/openrct2/EditorObjectSelectionSession.cpp b/src/openrct2/EditorObjectSelectionSession.cpp index a8bc5285577a..54a30733578e 100644 --- a/src/openrct2/EditorObjectSelectionSession.cpp +++ b/src/openrct2/EditorObjectSelectionSession.cpp @@ -292,7 +292,7 @@ static void remove_selected_objects_from_research(const rct_object_entry* instal for (auto rideType : rideEntry->ride_type) { - rct_research_item tmp = {}; + ResearchItem tmp = {}; tmp.type = RESEARCH_ENTRY_TYPE_RIDE; tmp.entryIndex = entry_index; tmp.baseRideType = rideType; @@ -301,7 +301,7 @@ static void remove_selected_objects_from_research(const rct_object_entry* instal } else if (entry_type == OBJECT_TYPE_SCENERY_GROUP) { - rct_research_item tmp = {}; + ResearchItem tmp = {}; tmp.type = RESEARCH_ENTRY_TYPE_SCENERY; tmp.entryIndex = entry_index; research_remove(&tmp); diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index 1bac5b17c1f9..5297737acbda 100644 --- a/src/openrct2/interface/Window_internal.h +++ b/src/openrct2/interface/Window_internal.h @@ -14,7 +14,7 @@ #include #include -struct rct_research_item; +struct ResearchItem; struct rct_object_entry; /** @@ -79,7 +79,7 @@ struct rct_window { // 0x494 uint32_t highlighted_item; uint16_t ride_colour; - rct_research_item* research_item; + ResearchItem* research_item; rct_object_entry* object_entry; const scenario_index_entry* highlighted_scenario; struct diff --git a/src/openrct2/management/Research.cpp b/src/openrct2/management/Research.cpp index 581beed56e88..467f2a637351 100644 --- a/src/openrct2/management/Research.cpp +++ b/src/openrct2/management/Research.cpp @@ -46,13 +46,15 @@ uint8_t gResearchFundingLevel; uint8_t gResearchPriorities; uint16_t gResearchProgress; uint8_t gResearchProgressStage; -rct_research_item gResearchLastItem; +ResearchItem gResearchLastItem; uint8_t gResearchExpectedMonth; uint8_t gResearchExpectedDay; -rct_research_item gResearchNextItem; +ResearchItem gResearchNextItem; // 0x01358844[500] -rct_research_item gResearchItems[MAX_RESEARCH_ITEMS]; +ResearchItem gResearchItems[MAX_RESEARCH_ITEMS]; +std::vector gResearchItemsUninvented; +std::vector gResearchItemsInvented; // 0x00EE787C uint8_t gResearchUncompletedCategories; @@ -69,9 +71,8 @@ bool gSilentResearch = false; */ void research_reset_items() { - gResearchItems[0].rawValue = RESEARCHED_ITEMS_SEPARATOR; - gResearchItems[1].rawValue = RESEARCHED_ITEMS_END; - gResearchItems[2].rawValue = RESEARCHED_ITEMS_END_2; + gResearchItemsUninvented.clear(); + gResearchItemsInvented.clear(); } /** @@ -81,13 +82,10 @@ void research_reset_items() void research_update_uncompleted_types() { int32_t uncompletedResearchTypes = 0; - rct_research_item* researchItem = gResearchItems; - while (researchItem++->rawValue != RESEARCHED_ITEMS_SEPARATOR) - ; - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) + for (auto const& researchItem : gResearchItemsUninvented) { - uncompletedResearchTypes |= (1 << researchItem->category); + uncompletedResearchTypes |= (1 << researchItem.category); } gResearchUncompletedCategories = uncompletedResearchTypes; @@ -127,64 +125,62 @@ static void research_invalidate_related_windows() window_invalidate_by_class(WC_RESEARCH); } +static void research_mark_as_fully_completed() +{ + gResearchProgress = 0; + gResearchProgressStage = RESEARCH_STAGE_FINISHED_ALL; + research_invalidate_related_windows(); + // Reset funding to 0 if no more rides. + auto gameAction = ParkSetResearchFundingAction(gResearchPriorities, 0); + GameActions::Execute(&gameAction); +} + /** * * rct2: 0x00684BE5 */ static void research_next_design() { - rct_research_item *firstUnresearchedItem, *researchItem, tmp; - int32_t ignoreActiveResearchTypes; - - // Skip already researched items - firstUnresearchedItem = gResearchItems; - while (firstUnresearchedItem->rawValue != RESEARCHED_ITEMS_SEPARATOR) + if (gResearchItemsUninvented.empty()) { - firstUnresearchedItem++; + research_mark_as_fully_completed(); + return; } - ignoreActiveResearchTypes = 0; - researchItem = firstUnresearchedItem; + ResearchItem researchItem; + + bool ignoreActiveResearchTypes = false; + auto it = gResearchItemsUninvented.begin(); for (;;) { - researchItem++; - if (researchItem->rawValue == RESEARCHED_ITEMS_END) + researchItem = *it; + if (it == gResearchItemsUninvented.end()) { if (!ignoreActiveResearchTypes) { - ignoreActiveResearchTypes = 1; - researchItem = firstUnresearchedItem; + ignoreActiveResearchTypes = true; + it = gResearchItemsUninvented.begin(); continue; } else { - gResearchProgress = 0; - gResearchProgressStage = RESEARCH_STAGE_FINISHED_ALL; - research_invalidate_related_windows(); - // Reset funding to 0 if no more rides. - auto gameAction = ParkSetResearchFundingAction(gResearchPriorities, 0); - GameActions::Execute(&gameAction); + research_mark_as_fully_completed(); return; } } - else if (ignoreActiveResearchTypes || (gResearchPriorities & (1 << researchItem->category))) + else if (ignoreActiveResearchTypes || (gResearchPriorities & (1 << researchItem.category))) { break; } + it++; } - gResearchNextItem = *researchItem; + gResearchNextItem = researchItem; gResearchProgress = 0; gResearchProgressStage = RESEARCH_STAGE_DESIGNING; - // Bubble research item up until it is above the researched items separator - do - { - tmp = *researchItem; - *researchItem = *(researchItem - 1); - *(researchItem - 1) = tmp; - researchItem--; - } while ((researchItem + 1)->rawValue != RESEARCHED_ITEMS_SEPARATOR); + gResearchItemsUninvented.erase(it); + gResearchItemsInvented.push_back(researchItem); research_invalidate_related_windows(); } @@ -193,7 +189,7 @@ static void research_next_design() * * rct2: 0x006848D4 */ -void research_finish_item(rct_research_item* researchItem) +void research_finish_item(ResearchItem* researchItem) { gResearchLastItem = *researchItem; research_invalidate_related_windows(); @@ -229,15 +225,15 @@ void research_finish_item(rct_research_item* researchItem) ride_entry_set_invented(rideEntryIndex); bool seenRideEntry[MAX_RIDE_OBJECTS]{}; - - rct_research_item* researchItem2 = gResearchItems; - for (; researchItem2->rawValue != RESEARCHED_ITEMS_END; researchItem2++) + for (auto const& researchItem3 : gResearchItemsUninvented) { - if (researchItem2->rawValue != RESEARCHED_ITEMS_SEPARATOR && researchItem2->type == RESEARCH_ENTRY_TYPE_RIDE) - { - uint8_t index = researchItem2->entryIndex; - seenRideEntry[index] = true; - } + uint8_t index = researchItem3.entryIndex; + seenRideEntry[index] = true; + } + for (auto const& researchItem3 : gResearchItemsInvented) + { + uint8_t index = researchItem3.entryIndex; + seenRideEntry[index] = true; } // RCT2 made non-separated vehicles available at once, by removing all but one from research. @@ -386,54 +382,12 @@ void research_update() } } -void research_process_random_items() -{ - rct_research_item* research = gResearchItems; - for (; research->rawValue != RESEARCHED_ITEMS_END; research++) - { - } - - research++; - for (; research->rawValue != RESEARCHED_ITEMS_END_2; research += 2) - { - if (scenario_rand() & 1) - { - continue; - } - - rct_research_item* edx = nullptr; - rct_research_item* ebp = nullptr; - rct_research_item* inner_research = gResearchItems; - do - { - if (research->rawValue == inner_research->rawValue) - { - edx = inner_research; - } - if ((research + 1)->rawValue == inner_research->rawValue) - { - ebp = inner_research; - } - } while ((inner_research++)->rawValue != RESEARCHED_ITEMS_END); - assert(edx != nullptr); - edx->rawValue = research->rawValue; - assert(ebp != nullptr); - ebp->rawValue = (research + 1)->rawValue; - - uint8_t cat = edx->category; - edx->category = ebp->category; - ebp->category = cat; - } -} - /** * * rct2: 0x00684AC3 */ void research_reset_current_item() { - research_process_random_items(); - set_every_ride_type_not_invented(); set_every_ride_entry_not_invented(); @@ -441,9 +395,9 @@ void research_reset_current_item() set_all_scenery_items_invented(); set_all_scenery_groups_not_invented(); - for (rct_research_item* research = gResearchItems; research->rawValue != RESEARCHED_ITEMS_SEPARATOR; research++) + for (auto& researchItem : gResearchItemsInvented) { - research_finish_item(research); + research_finish_item(&researchItem); } gResearchLastItem.rawValue = RESEARCHED_ITEMS_SEPARATOR; @@ -455,29 +409,9 @@ void research_reset_current_item() * * rct2: 0x006857FA */ -static void research_insert_unresearched(int32_t rawValue, int32_t category) +static void research_insert_unresearched(int32_t rawValue, uint8_t category) { - rct_research_item *researchItem, *researchItem2; - - researchItem = gResearchItems; - do - { - if (researchItem->rawValue == RESEARCHED_ITEMS_END) - { - // Insert slot - researchItem2 = researchItem; - while (researchItem2->rawValue != RESEARCHED_ITEMS_END_2) - { - researchItem2++; - } - memmove(researchItem + 1, researchItem, (researchItem2 - researchItem + 1) * sizeof(rct_research_item)); - - // Place new item - researchItem->rawValue = rawValue; - researchItem->category = category; - break; - } - } while (rawValue != (researchItem++)->rawValue); + gResearchItemsUninvented.push_back({ rawValue, category }); } /** @@ -486,52 +420,37 @@ static void research_insert_unresearched(int32_t rawValue, int32_t category) */ static void research_insert_researched(int32_t rawValue, uint8_t category) { - rct_research_item *researchItem, *researchItem2; - - researchItem = gResearchItems; // First check to make sure that entry is not already accounted for - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) + ResearchItem item = { rawValue, category }; + if (item.Exists()) { - if ((researchItem->rawValue & 0xFFFFFF) == (rawValue & 0xFFFFFF)) - { - return; - } + return; } - researchItem = gResearchItems; - do - { - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR) - { - // Insert slot - researchItem2 = researchItem; - while (researchItem2->rawValue != RESEARCHED_ITEMS_END_2) - { - researchItem2++; - } - memmove(researchItem + 1, researchItem, (researchItem2 - researchItem + 1) * sizeof(rct_research_item)); - // Place new item - researchItem->rawValue = rawValue; - researchItem->category = category; - break; - } - } while (rawValue != (researchItem++)->rawValue); + gResearchItemsInvented.push_back(item); } /** * * rct2: 0x006857CF */ -void research_remove(rct_research_item* researchItem) +void research_remove(ResearchItem* researchItem) { - for (rct_research_item* researchItem2 = gResearchItems; researchItem2->rawValue != RESEARCHED_ITEMS_END; researchItem2++) + for (auto it = gResearchItemsUninvented.begin(); it != gResearchItemsUninvented.end(); it++) { - if (researchItem2->rawValue == researchItem->rawValue) + auto& researchItem2 = *it; + if (researchItem2.Equals(researchItem)) { - do - { - *researchItem2 = *(researchItem2 + 1); - } while (researchItem2++->rawValue != RESEARCHED_ITEMS_END_2); + gResearchItemsUninvented.erase(it); + return; + } + } + for (auto it = gResearchItemsInvented.begin(); it != gResearchItemsInvented.end(); it++) + { + auto& researchItem2 = *it; + if (researchItem2.Equals(researchItem)) + { + gResearchItemsInvented.erase(it); return; } } @@ -773,7 +692,7 @@ void set_every_ride_entry_not_invented() * * rct2: 0x0068563D */ -rct_string_id research_item_get_name(const rct_research_item* researchItem) +rct_string_id research_item_get_name(const ResearchItem* researchItem) { if (researchItem->type == RESEARCH_ENTRY_TYPE_RIDE) { @@ -821,54 +740,76 @@ rct_string_id research_get_friendly_base_ride_type_name(uint8_t trackType, rct_r * * rct2: 0x00685A79 * Do not use the research list outside of the inventions list window with the flags + * Clears flags like "always researched". */ void research_remove_flags() { - for (rct_research_item* research = gResearchItems; research->rawValue != RESEARCHED_ITEMS_END_2; research++) + for (auto& researchItem : gResearchItemsUninvented) { - // Clear the always researched flags. - if (research->rawValue > RESEARCHED_ITEMS_SEPARATOR) - { - research->flags = 0; - } + researchItem.flags = 0; + } + for (auto& researchItem : gResearchItemsInvented) + { + researchItem.flags = 0; } } void research_fix() { // Fix invalid research items - for (int32_t i = 0; i < MAX_RESEARCH_ITEMS; i++) + for (auto it = gResearchItemsInvented.begin(); it != gResearchItemsInvented.end();) { - rct_research_item* researchItem = &gResearchItems[i]; - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR) - continue; - if (researchItem->rawValue == RESEARCHED_ITEMS_END) + auto& researchItem = *it; + if (researchItem.type == RESEARCH_ENTRY_TYPE_RIDE) { - if (i == MAX_RESEARCH_ITEMS - 1) + rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex); + if (rideEntry == nullptr) { - (--researchItem)->rawValue = RESEARCHED_ITEMS_END; + it = gResearchItemsInvented.erase(it); + } + else + { + it++; } - (++researchItem)->rawValue = RESEARCHED_ITEMS_END_2; - break; } - if (researchItem->rawValue == RESEARCHED_ITEMS_END_2) - break; - if (researchItem->type == RESEARCH_ENTRY_TYPE_RIDE) + else + { + rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.rawValue); + if (sceneryGroupEntry == nullptr) + { + it = gResearchItemsInvented.erase(it); + } + else + { + it++; + } + } + } + for (auto it = gResearchItemsUninvented.begin(); it != gResearchItemsUninvented.end();) + { + auto& researchItem = *it; + if (researchItem.type == RESEARCH_ENTRY_TYPE_RIDE) { - rct_ride_entry* rideEntry = get_ride_entry(researchItem->entryIndex); + rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex); if (rideEntry == nullptr) { - research_remove(researchItem); - i--; + it = gResearchItemsUninvented.erase(it); + } + else + { + it++; } } else { - rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem->rawValue); + rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.rawValue); if (sceneryGroupEntry == nullptr) { - research_remove(researchItem); - i--; + it = gResearchItemsUninvented.erase(it); + } + else + { + it++; } } } @@ -914,51 +855,18 @@ void research_fix() void research_items_make_all_unresearched() { - rct_research_item *researchItem, *nextResearchItem, researchItemTemp; - - int32_t sorted; - do - { - sorted = 1; - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - if (research_item_is_always_researched(researchItem)) - continue; - - nextResearchItem = researchItem + 1; - if (nextResearchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR - || research_item_is_always_researched(nextResearchItem)) - { - // Bubble up always researched item or separator - researchItemTemp = *researchItem; - *researchItem = *nextResearchItem; - *nextResearchItem = researchItemTemp; - sorted = 0; - - if (researchItem->rawValue == RESEARCHED_ITEMS_SEPARATOR) - break; - } - } - } while (!sorted); + gResearchItemsUninvented.insert( + gResearchItemsUninvented.end(), std::make_move_iterator(gResearchItemsInvented.begin()), + std::make_move_iterator(gResearchItemsInvented.end())); + gResearchItemsInvented.clear(); } void research_items_make_all_researched() { - rct_research_item *researchItem, researchItemTemp; - - // Find separator - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - - // Move separator below all items - for (; (researchItem + 1)->rawValue != RESEARCHED_ITEMS_END; researchItem++) - { - // Swap separator with research item - researchItemTemp = *researchItem; - *researchItem = *(researchItem + 1); - *(researchItem + 1) = researchItemTemp; - } + gResearchItemsInvented.insert( + gResearchItemsInvented.end(), std::make_move_iterator(gResearchItemsUninvented.begin()), + std::make_move_iterator(gResearchItemsUninvented.end())); + gResearchItemsUninvented.clear(); } /** @@ -967,52 +875,41 @@ void research_items_make_all_researched() */ void research_items_shuffle() { - rct_research_item *researchItem, *researchOrderBase, researchItemTemp; - int32_t i, numNonResearchedItems; - - // Skip pre-researched items - for (researchItem = gResearchItems; researchItem->rawValue != RESEARCHED_ITEMS_SEPARATOR; researchItem++) - { - } - researchItem++; - researchOrderBase = researchItem; - - // Count non pre-researched items - numNonResearchedItems = 0; - for (; researchItem->rawValue != RESEARCHED_ITEMS_END; researchItem++) - numNonResearchedItems++; - - // Shuffle list - for (i = 0; i < numNonResearchedItems; i++) - { - int32_t ri = util_rand() % numNonResearchedItems; - if (ri == i) - continue; - - researchItemTemp = researchOrderBase[i]; - researchOrderBase[i] = researchOrderBase[ri]; - researchOrderBase[ri] = researchItemTemp; - } + std::shuffle(std::begin(gResearchItemsUninvented), std::end(gResearchItemsUninvented), std::default_random_engine{}); } -bool research_item_is_always_researched(rct_research_item* researchItem) +bool research_item_is_always_researched(const ResearchItem* researchItem) { return (researchItem->flags & (RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED | RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED)) != 0; } -bool rct_research_item::IsInventedEndMarker() const +bool ResearchItem::IsInventedEndMarker() const { return rawValue == RESEARCHED_ITEMS_SEPARATOR; } -bool rct_research_item::IsUninventedEndMarker() const +bool ResearchItem::Equals(const ResearchItem* otherItem) const { - return rawValue == RESEARCHED_ITEMS_END; + return (entryIndex == otherItem->entryIndex && baseRideType == otherItem->baseRideType && type == otherItem->type); } -bool rct_research_item::IsRandomEndMarker() const +bool ResearchItem::Exists() const { - return rawValue == RESEARCHED_ITEMS_END_2; + for (auto const& researchItem : gResearchItemsUninvented) + { + if (researchItem.Equals(this)) + { + return true; + } + } + for (auto const& researchItem : gResearchItemsInvented) + { + if (researchItem.Equals(this)) + { + return true; + } + } + return false; } diff --git a/src/openrct2/management/Research.h b/src/openrct2/management/Research.h index 339d8115be48..f222abbdf3b1 100644 --- a/src/openrct2/management/Research.h +++ b/src/openrct2/management/Research.h @@ -15,8 +15,7 @@ struct rct_ride_entry; -#pragma pack(push, 1) -struct rct_research_item +struct ResearchItem { // Bit 16 (0: scenery entry, 1: ride entry) union @@ -33,11 +32,9 @@ struct rct_research_item uint8_t category; bool IsInventedEndMarker() const; - bool IsRandomEndMarker() const; - bool IsUninventedEndMarker() const; + bool Equals(const ResearchItem* otherItem) const; + bool Exists() const; }; -assert_struct_size(rct_research_item, 5); -#pragma pack(pop) enum { @@ -100,10 +97,11 @@ extern uint16_t gResearchProgress; extern uint8_t gResearchProgressStage; extern uint8_t gResearchExpectedMonth; extern uint8_t gResearchExpectedDay; -extern rct_research_item gResearchLastItem; -extern rct_research_item gResearchNextItem; +extern ResearchItem gResearchLastItem; +extern ResearchItem gResearchNextItem; -extern rct_research_item gResearchItems[MAX_RESEARCH_ITEMS]; +extern std::vector gResearchItemsUninvented; +extern std::vector gResearchItemsInvented; extern uint8_t gResearchUncompletedCategories; extern bool gSilentResearch; @@ -113,11 +111,10 @@ void research_update(); void research_reset_current_item(); void research_populate_list_random(); void research_populate_list_researched(); -void research_process_random_items(); -void research_finish_item(rct_research_item* researchItem); +void research_finish_item(ResearchItem* researchItem); void research_insert(int32_t researched, int32_t rawValue, uint8_t category); -void research_remove(rct_research_item* researchItem); +void research_remove(ResearchItem* researchItem); void research_insert_ride_entry(uint8_t entryIndex, bool researched); void research_insert_scenery_group_entry(uint8_t entryIndex, bool researched); @@ -139,7 +136,7 @@ void set_every_ride_type_invented(); void set_every_ride_type_not_invented(); void set_every_ride_entry_invented(); void set_every_ride_entry_not_invented(); -rct_string_id research_item_get_name(const rct_research_item* researchItem); +rct_string_id research_item_get_name(const ResearchItem* researchItem); rct_string_id research_get_friendly_base_ride_type_name(uint8_t trackType, rct_ride_entry* rideEntry); void research_remove_flags(); void research_fix(); @@ -147,4 +144,4 @@ void research_fix(); void research_items_make_all_unresearched(); void research_items_make_all_researched(); void research_items_shuffle(); -bool research_item_is_always_researched(rct_research_item* researchItem); +bool research_item_is_always_researched(const ResearchItem* researchItem); diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index ec2ecd11182b..8cd788f202a2 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -2496,7 +2496,7 @@ class S4Importer final : public IParkImporter uint8_t researchItem = src->Assoc & 0x000000FF; uint8_t researchType = (src->Assoc & 0x00FF0000) >> 16; - rct_research_item tmpResearchItem = {}; + ResearchItem tmpResearchItem = {}; ConvertResearchEntry(&tmpResearchItem, researchItem, researchType); dst->Assoc = (uint32_t)tmpResearchItem.rawValue; } @@ -2540,7 +2540,7 @@ class S4Importer final : public IParkImporter gTotalRideValueForMoney = _s4.total_ride_value_for_money; } - void ConvertResearchEntry(rct_research_item* dst, uint8_t srcItem, uint8_t srcType) + void ConvertResearchEntry(ResearchItem* dst, uint8_t srcItem, uint8_t srcType) { dst->rawValue = RESEARCHED_ITEMS_SEPARATOR; if (srcType == RCT1_RESEARCH_TYPE_RIDE) diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp index c94e2c2933d4..c3f3e5c9c302 100644 --- a/src/openrct2/rct12/RCT12.cpp +++ b/src/openrct2/rct12/RCT12.cpp @@ -915,3 +915,18 @@ void RCT12BannerElement::SetAllowedEdges(uint8_t newEdges) flags &= ~0b00001111; flags |= (newEdges & 0b00001111); } + +bool RCT12ResearchItem::IsInventedEndMarker() const +{ + return rawValue == RCT12_RESEARCHED_ITEMS_SEPARATOR; +} + +bool RCT12ResearchItem::IsUninventedEndMarker() const +{ + return rawValue == RCT12_RESEARCHED_ITEMS_END; +} + +bool RCT12ResearchItem::IsRandomEndMarker() const +{ + return rawValue == RCT12_RESEARCHED_ITEMS_END_2; +} diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index 8e8c5821fb1a..4f2a8f7d6ae5 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -57,6 +57,13 @@ enum class RCT12TrackDesignVersion : uint8_t unknown }; +// Everything before this point has been researched +#define RCT12_RESEARCHED_ITEMS_SEPARATOR (-1) +// Everything before this point and after separator still requires research +#define RCT12_RESEARCHED_ITEMS_END (-2) +// Extra end of list entry. Leftover from RCT1. +#define RCT12_RESEARCHED_ITEMS_END_2 (-3) + #pragma pack(push, 1) /* Maze Element entry size: 0x04 */ @@ -658,6 +665,28 @@ struct RCT12MapAnimation }; assert_struct_size(RCT12MapAnimation, 6); +struct RCT12ResearchItem +{ + // Bit 16 (0: scenery entry, 1: ride entry) + union + { + int32_t rawValue; + struct + { + uint8_t entryIndex; + uint8_t baseRideType; + uint8_t type; // 0: scenery entry, 1: ride entry + uint8_t flags; + }; + }; + uint8_t category; + + bool IsInventedEndMarker() const; + bool IsRandomEndMarker() const; + bool IsUninventedEndMarker() const; +}; +assert_struct_size(RCT12ResearchItem, 5); + #pragma pack(pop) bool is_user_string_id(rct_string_id stringId); diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 18e825725e4c..e82e8653e12b 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -848,7 +848,18 @@ void S6Exporter::ExportResearchedSceneryItems() void S6Exporter::ExportResearchList() { - std::memcpy(_s6.research_items, gResearchItems, sizeof(_s6.research_items)); + size_t i = 0; + for (const auto& researchItem : gResearchItemsInvented) + { + _s6.research_items[i++] = RCT12ResearchItem{ researchItem.rawValue, researchItem.category }; + } + _s6.research_items[i++] = { RCT12_RESEARCHED_ITEMS_SEPARATOR, 0 }; + for (const auto& researchItem : gResearchItemsUninvented) + { + _s6.research_items[i++] = RCT12ResearchItem{ researchItem.rawValue, researchItem.category }; + } + _s6.research_items[i++] = { RCT12_RESEARCHED_ITEMS_END, 0 }; + _s6.research_items[i] = { RCT12_RESEARCHED_ITEMS_END_2, 0 }; } void S6Exporter::ExportMarketingCampaigns() diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 3c0081607473..40e8c7a9ac38 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -850,7 +850,25 @@ class S6Importer final : public IParkImporter void ImportResearchList() { - std::memcpy(gResearchItems, _s6.research_items, sizeof(_s6.research_items)); + bool invented = true; + for (size_t i = 0; i < sizeof(_s6.research_items); i++) + { + if (_s6.research_items[i].IsInventedEndMarker()) + { + invented = false; + continue; + } + else if (_s6.research_items[i].IsUninventedEndMarker() || _s6.research_items[i].IsRandomEndMarker()) + { + break; + } + + RCT12ResearchItem* ri = &_s6.research_items[i]; + if (invented) + gResearchItemsInvented.push_back(ResearchItem{ ri->rawValue, ri->category }); + else + gResearchItemsUninvented.push_back(ResearchItem{ ri->rawValue, ri->category }); + } } void ImportBanner(Banner* dst, const RCT12Banner* src) diff --git a/src/openrct2/scenario/Scenario.h b/src/openrct2/scenario/Scenario.h index ecb9918236f4..5b6c97483e72 100644 --- a/src/openrct2/scenario/Scenario.h +++ b/src/openrct2/scenario/Scenario.h @@ -238,7 +238,7 @@ struct rct_s6_data uint8_t last_entrance_style; uint8_t rct1_water_colour; uint8_t pad_01358842[2]; - rct_research_item research_items[MAX_RESEARCH_ITEMS]; + RCT12ResearchItem research_items[MAX_RESEARCH_ITEMS]; uint16_t map_base_z; char scenario_name[64]; char scenario_description[256];