From 5029f754edbcc92c23a27ac938bb88131cc451ac Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Wed, 17 Jun 2020 16:52:48 +0200 Subject: [PATCH] Fix #7324: Research window shows vehicle name instead of ride name --- data/language/en-GB.txt | 2 + distribution/changelog.txt | 1 + src/openrct2-ui/windows/Research.cpp | 52 ++++++++-- src/openrct2/localisation/StringIds.h | 3 + src/openrct2/management/Research.cpp | 136 ++++++++++++++++++++----- src/openrct2/management/Research.h | 32 +++--- src/openrct2/object/RideObject.cpp | 26 +---- src/openrct2/rct1/S4Importer.cpp | 1 + src/openrct2/rct2/S6Importer.cpp | 2 + src/openrct2/ride/RideGroupManager.cpp | 27 +++++ src/openrct2/ride/RideGroupManager.h | 2 + 11 files changed, 213 insertions(+), 71 deletions(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 5b57d654d6..fca3b5a2be 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3670,6 +3670,8 @@ STR_6372 :The specified path contains a RollerCoaster Tycoon 1 installation, STR_6373 :Toggle clearance checks STR_6374 :C STR_6375 :Unknown Ride +STR_6376 :{WINDOW_COLOUR_2}Ride vehicle:{NEWLINE}{BLACK}{STRINGID} for {STRINGID} +STR_6377 :{WINDOW_COLOUR_2}Type: {BLACK}{STRINGID} for {STRINGID} ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index acd8bf986b..2a7934aeeb 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -22,6 +22,7 @@ - Fix: [#5451] Guests scream on every descent, no matter how small. - Fix: [#6119] Advertising campaign for ride window not updated properly (original bug). - Fix: [#7006] Submarine Ride is in the wrong research group. +- Fix: [#7324] Research window shows vehicle name instead of ride name. - Fix: [#10634] Guests are unable to use uphill paths out of toilets. - Fix: [#10876] When removing a path, its guest entry point is not removed. - Fix: [#10876] There can be multiple peep spawns on the same location. diff --git a/src/openrct2-ui/windows/Research.cpp b/src/openrct2-ui/windows/Research.cpp index 267a6ac421..6abff4439b 100644 --- a/src/openrct2-ui/windows/Research.cpp +++ b/src/openrct2-ui/windows/Research.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -362,16 +363,33 @@ void window_research_development_page_paint(rct_window* w, rct_drawpixelinfo* dp else { // Research type - stringId = STR_RESEARCH_UNKNOWN; + rct_string_id strings[2] = { STR_RESEARCH_UNKNOWN, 0 }; + rct_string_id label = STR_RESEARCH_TYPE_LABEL; if (gResearchProgressStage != RESEARCH_STAGE_INITIAL_RESEARCH) { - stringId = ResearchCategoryNames[gResearchNextItem->category]; + strings[0] = ResearchCategoryNames[gResearchNextItem->category]; if (gResearchProgressStage != RESEARCH_STAGE_DESIGNING) { - stringId = gResearchNextItem->GetName(); + strings[0] = gResearchNextItem->GetName(); + if (gResearchNextItem->type == RESEARCH_ENTRY_TYPE_RIDE) + { + auto rtd = RideTypeDescriptors[gResearchNextItem->baseRideType]; + if (!rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + if (gResearchNextItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE) + { + strings[0] = rtd.Naming.Name; + } + else + { + strings[1] = rtd.Naming.Name; + label = STR_RESEARCH_TYPE_LABEL_VEHICLE; + } + } + } } } - gfx_draw_string_left_wrapped(dpi, &stringId, x, y, 296, STR_RESEARCH_TYPE_LABEL, COLOUR_BLACK); + gfx_draw_string_left_wrapped(dpi, &strings, x, y, 296, label, COLOUR_BLACK); y += 25; // Progress @@ -402,11 +420,31 @@ void window_research_development_page_paint(rct_window* w, rct_drawpixelinfo* dp rct_string_id lastDevelopmentFormat; if (gResearchLastItem.has_value()) { - stringId = gResearchLastItem->GetName(); + rct_string_id strings[2] = { gResearchLastItem->GetName(), 0 }; uint8_t type = gResearchLastItem->type; - lastDevelopmentFormat = (type == RESEARCH_ENTRY_TYPE_RIDE) ? STR_RESEARCH_RIDE_LABEL : STR_RESEARCH_SCENERY_LABEL; + if (type == RESEARCH_ENTRY_TYPE_SCENERY) + { + lastDevelopmentFormat = STR_RESEARCH_SCENERY_LABEL; + } + else + { + lastDevelopmentFormat = STR_RESEARCH_RIDE_LABEL; + auto rtd = RideTypeDescriptors[gResearchLastItem->baseRideType]; + if (!rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + if (gResearchLastItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE) + { + strings[0] = rtd.Naming.Name; + } + else + { + strings[1] = rtd.Naming.Name; + lastDevelopmentFormat = STR_RESEARCH_VEHICLE_LABEL; + } + } + } - gfx_draw_string_left_wrapped(dpi, &stringId, x, y, 266, lastDevelopmentFormat, COLOUR_BLACK); + gfx_draw_string_left_wrapped(dpi, &strings, x, y, 266, lastDevelopmentFormat, COLOUR_BLACK); } } diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 3b9fe75f0f..ed96a21580 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3911,6 +3911,9 @@ enum STR_UNKNOWN_RIDE = 6375, + STR_RESEARCH_VEHICLE_LABEL = 6376, + STR_RESEARCH_TYPE_LABEL_VEHICLE = 6377, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working /* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings }; diff --git a/src/openrct2/management/Research.cpp b/src/openrct2/management/Research.cpp index 2d437ab995..ac279738a7 100644 --- a/src/openrct2/management/Research.cpp +++ b/src/openrct2/management/Research.cpp @@ -201,21 +201,8 @@ void research_finish_item(ResearchItem* researchItem) if (rideEntry != nullptr && base_ride_type != RIDE_TYPE_NULL) { - bool ride_group_was_invented_before = false; - bool ride_type_was_invented_before = ride_type_is_invented(base_ride_type); rct_string_id availabilityString; - // Determine if the ride group this entry belongs to was invented before. - if (RideTypeDescriptors[base_ride_type].HasFlag(RIDE_TYPE_FLAG_HAS_RIDE_GROUPS)) - { - const RideGroup* rideGroup = RideGroupManager::GetRideGroup(base_ride_type, rideEntry); - - if (rideGroup->IsInvented()) - { - ride_group_was_invented_before = true; - } - } - ride_type_set_invented(base_ride_type); openrct2_assert(base_ride_type < RIDE_TYPE_COUNT, "Invalid base_ride_type = %d", base_ride_type); @@ -256,17 +243,10 @@ void research_finish_item(ResearchItem* researchItem) auto ft = Formatter::Common(); - // If a vehicle should be listed separately (maze, mini golf, flat rides, shops) - if (RideTypeDescriptors[base_ride_type].HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) - { - availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE; - ft.Add(rideEntry->naming.Name); - } - // If a vehicle is the first to be invented for its ride group, show the ride group name. - else if ( - !ride_type_was_invented_before - || (RideTypeDescriptors[base_ride_type].HasFlag(RIDE_TYPE_FLAG_HAS_RIDE_GROUPS) - && !ride_group_was_invented_before)) + // If a vehicle is the first to be invented for its ride type or group, show the ride type/group name. + // Independently listed vehicles (like all flat rides and shops) should always be announced as such. + if (RideTypeDescriptors[base_ride_type].HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY) + || researchItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE) { RideNaming naming = get_ride_naming(base_ride_type, rideEntry); availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE; @@ -740,11 +720,11 @@ void research_remove_flags() { for (auto& researchItem : gResearchItemsUninvented) { - researchItem.flags = 0; + researchItem.flags &= ~(RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED | RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED); } for (auto& researchItem : gResearchItemsInvented) { - researchItem.flags = 0; + researchItem.flags &= ~(RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED | RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED); } } @@ -910,3 +890,107 @@ bool ResearchItem::Exists() const } return false; } + +static std::bitset _seenRideType = {}; +static std::bitset _seenRideGroup = {}; + +static void research_update_first_of_type(ResearchItem* researchItem) +{ + if (researchItem->IsNull()) + return; + + if (researchItem->type != RESEARCH_ENTRY_TYPE_RIDE) + return; + + auto rideType = researchItem->baseRideType; + if (rideType >= RIDE_TYPE_COUNT) + return; + + const auto& rtd = RideTypeDescriptors[rideType]; + if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + { + researchItem->flags |= RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE; + return; + } + + if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_RIDE_GROUPS)) + { + if (!_seenRideType[rideType]) + researchItem->flags |= RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE; + + _seenRideType[rideType] = true; + } + else + { + const auto& entry = get_ride_entry(researchItem->entryIndex); + if (entry != nullptr) + { + auto rideGroupIndex = RideGroupManager::GetRideGroupIndex(rideType, entry); + assert(rideGroupIndex < MAX_RIDE_GROUPS_PER_RIDE_TYPE); + + if (!_seenRideGroup[rideType * rideGroupIndex]) + researchItem->flags |= RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE; + + _seenRideGroup[rideType * rideGroupIndex] = true; + } + } +} + +void research_determine_first_of_type() +{ + _seenRideType.reset(); + _seenRideGroup.reset(); + + for (auto& researchItem : gResearchItemsInvented) + { + if (researchItem.type != RESEARCH_ENTRY_TYPE_RIDE) + continue; + + auto rideType = researchItem.baseRideType; + if (rideType >= RIDE_TYPE_COUNT) + continue; + + const auto& rtd = RideTypeDescriptors[rideType]; + if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) + continue; + + // The next research item is also present in gResearchItemsInvented, even though it isn't invented yet(!) + if (gResearchNextItem.has_value() && !gResearchNextItem->IsNull() && researchItem.Equals(&gResearchNextItem.value())) + continue; + + if (!rtd.HasFlag(RIDE_TYPE_FLAG_HAS_RIDE_GROUPS)) + { + _seenRideType[rideType] = true; + log_error("Seen %d", rideType); + } + else + { + const auto& entry = get_ride_entry(researchItem.entryIndex); + if (entry != nullptr) + { + auto rideGroupIndex = RideGroupManager::GetRideGroupIndex(rideType, entry); + assert(rideGroupIndex < MAX_RIDE_GROUPS_PER_RIDE_TYPE); + _seenRideGroup[rideType * rideGroupIndex] = true; + log_error("Seen rgi %d ri %d", rideGroupIndex, rideType); + } + } + } + + if (gResearchLastItem.has_value()) + { + auto tmpVal = gResearchLastItem.value(); + research_update_first_of_type(&tmpVal); + gResearchLastItem = tmpVal; + } + if (gResearchNextItem.has_value()) + { + auto tmpVal = gResearchNextItem.value(); + research_update_first_of_type(&tmpVal); + gResearchNextItem = tmpVal; + } + + for (auto& researchItem : gResearchItemsUninvented) + { + research_update_first_of_type(&researchItem); + } +} diff --git a/src/openrct2/management/Research.h b/src/openrct2/management/Research.h index 2ff01d3211..384d6f6155 100644 --- a/src/openrct2/management/Research.h +++ b/src/openrct2/management/Research.h @@ -17,6 +17,19 @@ struct rct_ride_entry; +enum +{ + RESEARCH_ENTRY_TYPE_SCENERY = 0, + RESEARCH_ENTRY_TYPE_RIDE = 1, +}; + +enum +{ + RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE = (1 << 0), + RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED = (1 << 5), + RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED = (1 << 6), +}; + struct ResearchItem { union @@ -67,7 +80,7 @@ struct ResearchItem retItem.entryIndex = OpenRCT2EntryIndexToRCTEntryIndex(entryIndex); retItem.baseRideType = baseRideType; retItem.type = type; - retItem.flags = flags; + retItem.flags = (flags & ~RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE); retItem.category = category; } @@ -95,18 +108,6 @@ struct ResearchItem } }; -enum -{ - RESEARCH_ENTRY_TYPE_SCENERY = 0, - RESEARCH_ENTRY_TYPE_RIDE = 1, -}; - -enum -{ - RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED = (1 << 5), - RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED = (1 << 6), -}; - // Only used to mark as null nowadays. Deprecated. TODO: remove. #define RESEARCH_ITEM_NULL 0xFFFFFFFF @@ -192,3 +193,8 @@ void research_fix(); void research_items_make_all_unresearched(); void research_items_make_all_researched(); void research_items_shuffle(); +/** + * Determines if a newly invented ride entry should be listed as a new ride + * or as a new vehicle for a pre-existing ride. + */ +void research_determine_first_of_type(); diff --git a/src/openrct2/object/RideObject.cpp b/src/openrct2/object/RideObject.cpp index 4828f424ed..c746d6ff7a 100644 --- a/src/openrct2/object/RideObject.cpp +++ b/src/openrct2/object/RideObject.cpp @@ -399,31 +399,7 @@ void RideObject::SetRepositoryItem(ObjectRepositoryItem* item) const } item->RideInfo.RideFlags = 0; - - // Determines the ride group. Will fall back to 0 if there is none found. - uint8_t rideGroupIndex = 0; - - const RideGroup* rideGroup = RideGroupManager::GetRideGroup(firstRideType, &_legacyType); - - // If the ride group is nullptr, the track type does not have ride groups. - if (rideGroup != nullptr) - { - for (uint8_t i = rideGroupIndex + 1; i < MAX_RIDE_GROUPS_PER_RIDE_TYPE; i++) - { - const RideGroup* irg = RideGroupManager::RideGroupFind(firstRideType, i); - - if (irg != nullptr) - { - if (irg->Equals(rideGroup)) - { - rideGroupIndex = i; - break; - } - } - } - } - - item->RideInfo.RideGroupIndex = rideGroupIndex; + item->RideInfo.RideGroupIndex = RideGroupManager::GetRideGroupIndex(firstRideType, &_legacyType); } void RideObject::ReadLegacyVehicle( diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 45416519b3..165504f652 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -210,6 +210,7 @@ public: game_convert_news_items_to_utf8(); map_count_remaining_land_rights(); + research_determine_first_of_type(); } bool GetDetails(scenario_index_entry* dst) override diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index e70c265d34..48dfe382f2 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -487,6 +487,8 @@ public: OWNERSHIP_OWNED); // clang-format on } + + research_determine_first_of_type(); } void ImportRides() diff --git a/src/openrct2/ride/RideGroupManager.cpp b/src/openrct2/ride/RideGroupManager.cpp index 57ea49d0c8..f3c3d3595c 100644 --- a/src/openrct2/ride/RideGroupManager.cpp +++ b/src/openrct2/ride/RideGroupManager.cpp @@ -245,3 +245,30 @@ const RideGroup* RideGroupManager::RideGroupFind(const uint8_t rideType, const u return nullptr; } } + +uint8_t RideGroupManager::GetRideGroupIndex(const uint8_t rideType, const rct_ride_entry* rideEntry) +{ + uint8_t rideGroupIndex = 0; + + const RideGroup* rideGroup = RideGroupManager::GetRideGroup(rideType, rideEntry); + + // If the ride group is nullptr, the track type does not have ride groups. + if (rideGroup != nullptr) + { + for (uint8_t i = rideGroupIndex + 1; i < MAX_RIDE_GROUPS_PER_RIDE_TYPE; i++) + { + const RideGroup* irg = RideGroupManager::RideGroupFind(rideType, i); + + if (irg != nullptr) + { + if (irg->Equals(rideGroup)) + { + rideGroupIndex = i; + break; + } + } + } + } + + return rideGroupIndex; +} diff --git a/src/openrct2/ride/RideGroupManager.h b/src/openrct2/ride/RideGroupManager.h index 9b79cb3018..64b50872e0 100644 --- a/src/openrct2/ride/RideGroupManager.h +++ b/src/openrct2/ride/RideGroupManager.h @@ -32,6 +32,8 @@ class RideGroupManager { public: static const RideGroup* GetRideGroup(const uint8_t trackType, const rct_ride_entry* rideEntry); + /** Will fall back to 0 if there is none found. */ + static uint8_t GetRideGroupIndex(const uint8_t trackType, const rct_ride_entry* rideEntry); static const RideGroup* RideGroupFind(const uint8_t rideType, const uint8_t index); };