1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "Research.h"
11 
12 #include "../Game.h"
13 #include "../OpenRCT2.h"
14 #include "../actions/ParkSetResearchFundingAction.h"
15 #include "../config/Config.h"
16 #include "../core/Guard.hpp"
17 #include "../core/Memory.hpp"
18 #include "../interface/Window.h"
19 #include "../localisation/Date.h"
20 #include "../localisation/Localisation.h"
21 #include "../localisation/StringIds.h"
22 #include "../object/ObjectList.h"
23 #include "../rct1/RCT1.h"
24 #include "../ride/Ride.h"
25 #include "../ride/RideData.h"
26 #include "../ride/TrackData.h"
27 #include "../scenario/Scenario.h"
28 #include "../util/Util.h"
29 #include "../world/Park.h"
30 #include "../world/Scenery.h"
31 #include "Finance.h"
32 #include "NewsItem.h"
33 
34 #include <algorithm>
35 #include <iterator>
36 
37 static constexpr const int32_t _researchRate[] = {
38     0,
39     160,
40     250,
41     400,
42 };
43 
44 uint8_t gResearchFundingLevel;
45 uint8_t gResearchPriorities;
46 uint16_t gResearchProgress;
47 uint8_t gResearchProgressStage;
48 std::optional<ResearchItem> gResearchLastItem;
49 uint8_t gResearchExpectedMonth;
50 uint8_t gResearchExpectedDay;
51 std::optional<ResearchItem> gResearchNextItem;
52 
53 std::vector<ResearchItem> gResearchItemsUninvented;
54 std::vector<ResearchItem> gResearchItemsInvented;
55 
56 // 0x00EE787C
57 uint8_t gResearchUncompletedCategories;
58 
59 static bool _researchedRideTypes[RIDE_TYPE_COUNT];
60 static bool _researchedRideEntries[MAX_RIDE_OBJECTS];
61 static bool _researchedSceneryItems[SCENERY_TYPE_COUNT][UINT16_MAX];
62 
63 bool gSilentResearch = false;
64 
65 /**
66  *
67  *  rct2: 0x006671AD, part of 0x00667132
68  */
research_reset_items()69 void research_reset_items()
70 {
71     gResearchItemsUninvented.clear();
72     gResearchItemsInvented.clear();
73 }
74 
75 /**
76  *
77  *  rct2: 0x00684BAE
78  */
research_update_uncompleted_types()79 void research_update_uncompleted_types()
80 {
81     int32_t uncompletedResearchTypes = 0;
82 
83     for (auto const& researchItem : gResearchItemsUninvented)
84     {
85         uncompletedResearchTypes |= EnumToFlag(researchItem.category);
86     }
87 
88     gResearchUncompletedCategories = uncompletedResearchTypes;
89 }
90 
91 /**
92  *
93  *  rct2: 0x00684D2A
94  */
research_calculate_expected_date()95 static void research_calculate_expected_date()
96 {
97     if (gResearchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH || gResearchFundingLevel == RESEARCH_FUNDING_NONE)
98     {
99         gResearchExpectedDay = 255;
100     }
101     else
102     {
103         int32_t progressRemaining = gResearchProgressStage == RESEARCH_STAGE_COMPLETING_DESIGN ? 0x10000 : 0x20000;
104         progressRemaining -= gResearchProgress;
105         int32_t daysRemaining = (progressRemaining / _researchRate[gResearchFundingLevel]) * 128;
106 
107         int32_t expectedDay = gDateMonthTicks + (daysRemaining & 0xFFFF);
108         int32_t dayQuotient = expectedDay / 0x10000;
109         int32_t dayRemainder = expectedDay % 0x10000;
110 
111         int32_t expectedMonth = date_get_month(gDateMonthsElapsed + dayQuotient + (daysRemaining >> 16));
112         expectedDay = (dayRemainder * days_in_month[expectedMonth]) >> 16;
113 
114         gResearchExpectedDay = expectedDay;
115         gResearchExpectedMonth = expectedMonth;
116     }
117 }
118 
research_invalidate_related_windows()119 static void research_invalidate_related_windows()
120 {
121     window_invalidate_by_class(WC_CONSTRUCT_RIDE);
122     window_invalidate_by_class(WC_RESEARCH);
123 }
124 
research_mark_as_fully_completed()125 static void research_mark_as_fully_completed()
126 {
127     gResearchProgress = 0;
128     gResearchProgressStage = RESEARCH_STAGE_FINISHED_ALL;
129     research_invalidate_related_windows();
130     // Reset funding to 0 if no more rides.
131     auto gameAction = ParkSetResearchFundingAction(gResearchPriorities, 0);
132     GameActions::Execute(&gameAction);
133 }
134 
135 /**
136  *
137  *  rct2: 0x00684BE5
138  */
research_next_design()139 static void research_next_design()
140 {
141     if (gResearchItemsUninvented.empty())
142     {
143         research_mark_as_fully_completed();
144         return;
145     }
146 
147     ResearchItem researchItem;
148 
149     bool ignoreActiveResearchTypes = false;
150     auto it = gResearchItemsUninvented.begin();
151     for (;;)
152     {
153         researchItem = *it;
154         if (it == gResearchItemsUninvented.end())
155         {
156             if (!ignoreActiveResearchTypes)
157             {
158                 ignoreActiveResearchTypes = true;
159                 it = gResearchItemsUninvented.begin();
160                 continue;
161             }
162 
163             research_mark_as_fully_completed();
164             return;
165         }
166 
167         if (ignoreActiveResearchTypes || (gResearchPriorities & EnumToFlag(researchItem.category)))
168         {
169             break;
170         }
171         it++;
172     }
173 
174     gResearchNextItem = researchItem;
175     gResearchProgress = 0;
176     gResearchProgressStage = RESEARCH_STAGE_DESIGNING;
177 
178     gResearchItemsUninvented.erase(it);
179     gResearchItemsInvented.push_back(std::move(researchItem));
180 
181     research_invalidate_related_windows();
182 }
183 
184 /**
185  *
186  *  rct2: 0x006848D4
187  */
research_finish_item(ResearchItem * researchItem)188 void research_finish_item(ResearchItem* researchItem)
189 {
190     gResearchLastItem = *researchItem;
191     research_invalidate_related_windows();
192 
193     if (researchItem->type == Research::EntryType::Ride)
194     {
195         // Ride
196         uint32_t base_ride_type = researchItem->baseRideType;
197         ObjectEntryIndex rideEntryIndex = researchItem->entryIndex;
198         rct_ride_entry* rideEntry = get_ride_entry(rideEntryIndex);
199 
200         if (rideEntry != nullptr && base_ride_type != RIDE_TYPE_NULL)
201         {
202             if (!RideTypeIsValid(base_ride_type))
203             {
204                 log_warning("Invalid ride type: %d", base_ride_type);
205                 base_ride_type = ride_entry_get_first_non_null_ride_type(rideEntry);
206             }
207 
208             rct_string_id availabilityString;
209             ride_type_set_invented(base_ride_type);
210             ride_entry_set_invented(rideEntryIndex);
211 
212             bool seenRideEntry[MAX_RIDE_OBJECTS]{};
213             for (auto const& researchItem3 : gResearchItemsUninvented)
214             {
215                 ObjectEntryIndex index = researchItem3.entryIndex;
216                 seenRideEntry[index] = true;
217             }
218             for (auto const& researchItem3 : gResearchItemsInvented)
219             {
220                 ObjectEntryIndex index = researchItem3.entryIndex;
221                 seenRideEntry[index] = true;
222             }
223 
224             // RCT2 made non-separated vehicles available at once, by removing all but one from research.
225             // To ensure old files keep working, look for ride entries not in research, and make them available as well.
226             for (int32_t i = 0; i < MAX_RIDE_OBJECTS; i++)
227             {
228                 if (!seenRideEntry[i])
229                 {
230                     rct_ride_entry* rideEntry2 = get_ride_entry(i);
231                     if (rideEntry2 != nullptr)
232                     {
233                         for (uint8_t j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++)
234                         {
235                             if (rideEntry2->ride_type[j] == base_ride_type)
236                             {
237                                 ride_entry_set_invented(i);
238                                 break;
239                             }
240                         }
241                     }
242                 }
243             }
244 
245             Formatter ft;
246 
247             // If a vehicle is the first to be invented for its ride type, show the ride type/group name.
248             // Independently listed vehicles (like all flat rides and shops) should always be announced as such.
249             if (GetRideTypeDescriptor(base_ride_type).HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)
250                 || researchItem->flags & RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE)
251             {
252                 RideNaming naming = get_ride_naming(base_ride_type, rideEntry);
253                 availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE;
254                 ft.Add<rct_string_id>(naming.Name);
255             }
256             // If the vehicle should not be listed separately and it isn't the first to be invented for its ride group,
257             // report it as a new vehicle for the existing ride group.
258             else
259             {
260                 availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_VEHICLE_AVAILABLE;
261                 RideNaming baseRideNaming = get_ride_naming(base_ride_type, rideEntry);
262 
263                 ft.Add<rct_string_id>(baseRideNaming.Name);
264                 ft.Add<rct_string_id>(rideEntry->naming.Name);
265             }
266 
267             if (!gSilentResearch)
268             {
269                 if (gConfigNotifications.ride_researched)
270                 {
271                     News::AddItemToQueue(News::ItemType::Research, availabilityString, researchItem->rawValue, ft);
272                 }
273             }
274 
275             research_invalidate_related_windows();
276         }
277     }
278     else
279     {
280         // Scenery
281         rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem->entryIndex);
282         if (sceneryGroupEntry != nullptr)
283         {
284             scenery_group_set_invented(researchItem->entryIndex);
285 
286             Formatter ft;
287             ft.Add<rct_string_id>(sceneryGroupEntry->name);
288 
289             if (!gSilentResearch)
290             {
291                 if (gConfigNotifications.ride_researched)
292                 {
293                     News::AddItemToQueue(
294                         News::ItemType::Research, STR_NEWS_ITEM_RESEARCH_NEW_SCENERY_SET_AVAILABLE, researchItem->rawValue, ft);
295                 }
296             }
297 
298             research_invalidate_related_windows();
299             init_scenery();
300         }
301     }
302 }
303 
304 /**
305  *
306  *  rct2: 0x00684C7A
307  */
research_update()308 void research_update()
309 {
310     int32_t editorScreenFlags, researchLevel, currentResearchProgress;
311 
312     editorScreenFlags = SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_DESIGNER | SCREEN_FLAGS_TRACK_MANAGER;
313     if (gScreenFlags & editorScreenFlags)
314     {
315         return;
316     }
317 
318     if (gCurrentTicks % 32 != 0)
319     {
320         return;
321     }
322 
323     if ((gParkFlags & PARK_FLAGS_NO_MONEY) && gResearchFundingLevel == RESEARCH_FUNDING_NONE)
324     {
325         researchLevel = RESEARCH_FUNDING_NORMAL;
326     }
327     else
328     {
329         researchLevel = gResearchFundingLevel;
330     }
331 
332     currentResearchProgress = gResearchProgress;
333     currentResearchProgress += _researchRate[researchLevel];
334     if (currentResearchProgress <= 0xFFFF)
335     {
336         gResearchProgress = currentResearchProgress;
337     }
338     else
339     {
340         switch (gResearchProgressStage)
341         {
342             case RESEARCH_STAGE_INITIAL_RESEARCH:
343                 research_next_design();
344                 research_calculate_expected_date();
345                 break;
346             case RESEARCH_STAGE_DESIGNING:
347                 gResearchProgress = 0;
348                 gResearchProgressStage = RESEARCH_STAGE_COMPLETING_DESIGN;
349                 research_calculate_expected_date();
350                 research_invalidate_related_windows();
351                 break;
352             case RESEARCH_STAGE_COMPLETING_DESIGN:
353                 research_finish_item(&*gResearchNextItem);
354                 gResearchProgress = 0;
355                 gResearchProgressStage = RESEARCH_STAGE_INITIAL_RESEARCH;
356                 research_calculate_expected_date();
357                 research_update_uncompleted_types();
358                 research_invalidate_related_windows();
359                 break;
360             case RESEARCH_STAGE_FINISHED_ALL:
361                 gResearchFundingLevel = RESEARCH_FUNDING_NONE;
362                 break;
363         }
364     }
365 }
366 
367 /**
368  *
369  *  rct2: 0x00684AC3
370  */
research_reset_current_item()371 void research_reset_current_item()
372 {
373     set_every_ride_type_not_invented();
374     set_every_ride_entry_not_invented();
375 
376     // The following two instructions together make all items not tied to a scenery group available.
377     set_all_scenery_items_invented();
378     set_all_scenery_groups_not_invented();
379 
380     for (auto& researchItem : gResearchItemsInvented)
381     {
382         research_finish_item(&researchItem);
383     }
384 
385     gResearchLastItem = std::nullopt;
386     gResearchProgressStage = RESEARCH_STAGE_INITIAL_RESEARCH;
387     gResearchProgress = 0;
388 }
389 
390 /**
391  *
392  *  rct2: 0x006857FA
393  */
research_insert_unresearched(ResearchItem && item)394 static void research_insert_unresearched(ResearchItem&& item)
395 {
396     gResearchItemsUninvented.push_back(std::move(item));
397 }
398 
399 /**
400  *
401  *  rct2: 0x00685826
402  */
research_insert_researched(ResearchItem && item)403 static void research_insert_researched(ResearchItem&& item)
404 {
405     // First check to make sure that entry is not already accounted for
406     if (item.Exists())
407     {
408         return;
409     }
410 
411     gResearchItemsInvented.push_back(std::move(item));
412 }
413 
414 /**
415  *
416  *  rct2: 0x006857CF
417  */
ResearchRemove(const ResearchItem & researchItem)418 void ResearchRemove(const ResearchItem& researchItem)
419 {
420     for (auto it = gResearchItemsUninvented.begin(); it != gResearchItemsUninvented.end(); it++)
421     {
422         auto& researchItem2 = *it;
423         if (researchItem2 == researchItem)
424         {
425             gResearchItemsUninvented.erase(it);
426             return;
427         }
428     }
429     for (auto it = gResearchItemsInvented.begin(); it != gResearchItemsInvented.end(); it++)
430     {
431         auto& researchItem2 = *it;
432         if (researchItem2 == researchItem)
433         {
434             gResearchItemsInvented.erase(it);
435             return;
436         }
437     }
438 }
439 
research_insert(ResearchItem && item,bool researched)440 void research_insert(ResearchItem&& item, bool researched)
441 {
442     if (researched)
443     {
444         research_insert_researched(std::move(item));
445     }
446     else
447     {
448         research_insert_unresearched(std::move(item));
449     }
450 }
451 
452 /**
453  *
454  *  rct2: 0x00685675
455  */
research_populate_list_random()456 void research_populate_list_random()
457 {
458     research_reset_items();
459 
460     // Rides
461     for (int32_t i = 0; i < MAX_RIDE_OBJECTS; i++)
462     {
463         rct_ride_entry* rideEntry = get_ride_entry(i);
464         if (rideEntry == nullptr)
465         {
466             continue;
467         }
468 
469         int32_t researched = (scenario_rand() & 0xFF) > 128;
470         for (auto rideType : rideEntry->ride_type)
471         {
472             if (rideType != RIDE_TYPE_NULL)
473             {
474                 ResearchCategory category = GetRideTypeDescriptor(rideType).GetResearchCategory();
475                 research_insert_ride_entry(rideType, i, category, researched);
476             }
477         }
478     }
479 
480     // Scenery
481     for (uint32_t i = 0; i < MAX_SCENERY_GROUP_OBJECTS; i++)
482     {
483         rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(i);
484         if (sceneryGroupEntry == nullptr)
485         {
486             continue;
487         }
488 
489         int32_t researched = (scenario_rand() & 0xFF) > 85;
490         research_insert_scenery_group_entry(i, researched);
491     }
492 }
493 
research_insert_ride_entry(uint8_t rideType,ObjectEntryIndex entryIndex,ResearchCategory category,bool researched)494 bool research_insert_ride_entry(uint8_t rideType, ObjectEntryIndex entryIndex, ResearchCategory category, bool researched)
495 {
496     if (rideType != RIDE_TYPE_NULL && entryIndex != OBJECT_ENTRY_INDEX_NULL)
497     {
498         auto tmpItem = ResearchItem(Research::EntryType::Ride, entryIndex, rideType, category, 0);
499         research_insert(std::move(tmpItem), researched);
500         return true;
501     }
502 
503     return false;
504 }
505 
research_insert_ride_entry(ObjectEntryIndex entryIndex,bool researched)506 void research_insert_ride_entry(ObjectEntryIndex entryIndex, bool researched)
507 {
508     rct_ride_entry* rideEntry = get_ride_entry(entryIndex);
509     if (rideEntry == nullptr)
510         return;
511 
512     for (auto rideType : rideEntry->ride_type)
513     {
514         if (rideType != RIDE_TYPE_NULL)
515         {
516             ResearchCategory category = GetRideTypeDescriptor(rideType).GetResearchCategory();
517             research_insert_ride_entry(rideType, entryIndex, category, researched);
518         }
519     }
520 }
521 
research_insert_scenery_group_entry(ObjectEntryIndex entryIndex,bool researched)522 bool research_insert_scenery_group_entry(ObjectEntryIndex entryIndex, bool researched)
523 {
524     if (entryIndex != OBJECT_ENTRY_INDEX_NULL)
525     {
526         auto tmpItem = ResearchItem(
527             Research::EntryType::Scenery, entryIndex, RIDE_TYPE_NULL, ResearchCategory::SceneryGroup, 0);
528         research_insert(std::move(tmpItem), researched);
529         return true;
530     }
531     return false;
532 }
533 
ride_type_is_invented(uint32_t rideType)534 bool ride_type_is_invented(uint32_t rideType)
535 {
536     return RideTypeIsValid(rideType) ? _researchedRideTypes[rideType] : false;
537 }
538 
ride_entry_is_invented(int32_t rideEntryIndex)539 bool ride_entry_is_invented(int32_t rideEntryIndex)
540 {
541     return _researchedRideEntries[rideEntryIndex];
542 }
543 
ride_type_set_invented(uint32_t rideType)544 void ride_type_set_invented(uint32_t rideType)
545 {
546     if (RideTypeIsValid(rideType))
547     {
548         _researchedRideTypes[rideType] = true;
549     }
550 }
551 
ride_entry_set_invented(int32_t rideEntryIndex)552 void ride_entry_set_invented(int32_t rideEntryIndex)
553 {
554     _researchedRideEntries[rideEntryIndex] = true;
555 }
556 
scenery_is_invented(const ScenerySelection & sceneryItem)557 bool scenery_is_invented(const ScenerySelection& sceneryItem)
558 {
559     if (sceneryItem.SceneryType < SCENERY_TYPE_COUNT)
560     {
561         return _researchedSceneryItems[sceneryItem.SceneryType][sceneryItem.EntryIndex];
562     }
563 
564     log_warning("Invalid Scenery Type");
565     return false;
566 }
567 
scenery_set_invented(const ScenerySelection & sceneryItem)568 void scenery_set_invented(const ScenerySelection& sceneryItem)
569 {
570     if (sceneryItem.SceneryType < SCENERY_TYPE_COUNT)
571     {
572         _researchedSceneryItems[sceneryItem.SceneryType][sceneryItem.EntryIndex] = true;
573     }
574     else
575     {
576         log_warning("Invalid Scenery Type");
577     }
578 }
579 
scenery_set_not_invented(const ScenerySelection & sceneryItem)580 void scenery_set_not_invented(const ScenerySelection& sceneryItem)
581 {
582     if (sceneryItem.SceneryType < SCENERY_TYPE_COUNT)
583     {
584         _researchedSceneryItems[sceneryItem.SceneryType][sceneryItem.EntryIndex] = false;
585     }
586     else
587     {
588         log_warning("Invalid Scenery Type");
589     }
590 }
591 
scenery_group_is_invented(int32_t sgIndex)592 bool scenery_group_is_invented(int32_t sgIndex)
593 {
594     const auto sgEntry = get_scenery_group_entry(sgIndex);
595     if (sgEntry == nullptr || sgEntry->entry_count == 0)
596     {
597         return false;
598     }
599 
600     // All scenery is temporarily invented when in the scenario editor
601     if (gScreenFlags & SCREEN_FLAGS_EDITOR)
602     {
603         return true;
604     }
605 
606     if (gCheatsIgnoreResearchStatus)
607     {
608         return true;
609     }
610 
611     return std::none_of(
612         std::begin(gResearchItemsUninvented), std::end(gResearchItemsUninvented), [sgIndex](const ResearchItem& item) {
613             return item.type == Research::EntryType::Scenery && item.entryIndex == sgIndex;
614         });
615 }
616 
scenery_group_set_invented(int32_t sgIndex)617 void scenery_group_set_invented(int32_t sgIndex)
618 {
619     const auto sgEntry = get_scenery_group_entry(sgIndex);
620     if (sgEntry != nullptr && sgEntry->entry_count > 0)
621     {
622         for (auto i = 0; i < sgEntry->entry_count; i++)
623         {
624             auto sceneryEntryIndex = sgEntry->scenery_entries[i];
625             scenery_set_invented(sceneryEntryIndex);
626         }
627     }
628 }
629 
set_all_scenery_groups_not_invented()630 void set_all_scenery_groups_not_invented()
631 {
632     for (int32_t i = 0; i < MAX_SCENERY_GROUP_OBJECTS; ++i)
633     {
634         rct_scenery_group_entry* scenery_set = get_scenery_group_entry(i);
635         if (scenery_set == nullptr)
636         {
637             continue;
638         }
639 
640         for (int32_t j = 0; j < scenery_set->entry_count; ++j)
641         {
642             scenery_set_not_invented(scenery_set->scenery_entries[j]);
643         }
644     }
645 }
646 
set_all_scenery_items_invented()647 void set_all_scenery_items_invented()
648 {
649     for (auto sceneryType = 0; sceneryType < SCENERY_TYPE_COUNT; sceneryType++)
650     {
651         std::fill(std::begin(_researchedSceneryItems[sceneryType]), std::end(_researchedSceneryItems[sceneryType]), true);
652     }
653 }
654 
set_all_scenery_items_not_invented()655 void set_all_scenery_items_not_invented()
656 {
657     for (auto sceneryType = 0; sceneryType < SCENERY_TYPE_COUNT; sceneryType++)
658     {
659         std::fill(std::begin(_researchedSceneryItems[sceneryType]), std::end(_researchedSceneryItems[sceneryType]), false);
660     }
661 }
662 
set_every_ride_type_invented()663 void set_every_ride_type_invented()
664 {
665     std::fill(std::begin(_researchedRideTypes), std::end(_researchedRideTypes), true);
666 }
667 
set_every_ride_type_not_invented()668 void set_every_ride_type_not_invented()
669 {
670     std::fill(std::begin(_researchedRideTypes), std::end(_researchedRideTypes), false);
671 }
672 
set_every_ride_entry_invented()673 void set_every_ride_entry_invented()
674 {
675     std::fill(std::begin(_researchedRideEntries), std::end(_researchedRideEntries), true);
676 }
677 
set_every_ride_entry_not_invented()678 void set_every_ride_entry_not_invented()
679 {
680     std::fill(std::begin(_researchedRideEntries), std::end(_researchedRideEntries), false);
681 }
682 
683 /**
684  *
685  *  rct2: 0x0068563D
686  */
GetName() const687 rct_string_id ResearchItem::GetName() const
688 {
689     if (type == Research::EntryType::Ride)
690     {
691         rct_ride_entry* rideEntry = get_ride_entry(entryIndex);
692         if (rideEntry == nullptr)
693         {
694             return STR_EMPTY;
695         }
696 
697         return rideEntry->naming.Name;
698     }
699 
700     rct_scenery_group_entry* sceneryEntry = get_scenery_group_entry(entryIndex);
701     if (sceneryEntry == nullptr)
702     {
703         return STR_EMPTY;
704     }
705 
706     return sceneryEntry->name;
707 }
708 
709 /**
710  *
711  *  rct2: 0x00685A79
712  *  Do not use the research list outside of the inventions list window with the flags
713  *  Clears flags like "always researched".
714  */
research_remove_flags()715 void research_remove_flags()
716 {
717     for (auto& researchItem : gResearchItemsUninvented)
718     {
719         researchItem.flags &= ~(RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED | RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED);
720     }
721     for (auto& researchItem : gResearchItemsInvented)
722     {
723         researchItem.flags &= ~(RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED | RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED);
724     }
725 }
726 
research_fix()727 void research_fix()
728 {
729     // Fix invalid research items
730     for (auto it = gResearchItemsInvented.begin(); it != gResearchItemsInvented.end();)
731     {
732         auto& researchItem = *it;
733         if (researchItem.type == Research::EntryType::Ride)
734         {
735             rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex);
736             if (rideEntry == nullptr)
737             {
738                 it = gResearchItemsInvented.erase(it);
739             }
740             else
741             {
742                 it++;
743             }
744         }
745         else
746         {
747             rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.entryIndex);
748             if (sceneryGroupEntry == nullptr)
749             {
750                 it = gResearchItemsInvented.erase(it);
751             }
752             else
753             {
754                 it++;
755             }
756         }
757     }
758     for (auto it = gResearchItemsUninvented.begin(); it != gResearchItemsUninvented.end();)
759     {
760         auto& researchItem = *it;
761         if (researchItem.type == Research::EntryType::Ride)
762         {
763             rct_ride_entry* rideEntry = get_ride_entry(researchItem.entryIndex);
764             if (rideEntry == nullptr)
765             {
766                 it = gResearchItemsUninvented.erase(it);
767             }
768             else
769             {
770                 it++;
771             }
772         }
773         else
774         {
775             rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem.entryIndex);
776             if (sceneryGroupEntry == nullptr)
777             {
778                 it = gResearchItemsUninvented.erase(it);
779             }
780             else
781             {
782                 it++;
783             }
784         }
785     }
786 
787     research_update_uncompleted_types();
788     if (gResearchUncompletedCategories == 0)
789         gResearchProgressStage = RESEARCH_STAGE_FINISHED_ALL;
790 
791     // Sometimes ride entries are not in the research table.
792     // If all research is done, simply insert all of them as researched.
793     // For good measure, also include scenery groups.
794     if (gResearchProgressStage == RESEARCH_STAGE_FINISHED_ALL)
795     {
796         for (ObjectEntryIndex i = 0; i < MAX_RIDE_OBJECTS; i++)
797         {
798             const rct_ride_entry* rideEntry = get_ride_entry(i);
799 
800             if (rideEntry != nullptr)
801             {
802                 research_insert_ride_entry(i, true);
803                 ride_entry_set_invented(i);
804 
805                 for (uint8_t j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++)
806                 {
807                     uint32_t rideType = rideEntry->ride_type[j];
808                     if (rideType != RIDE_TYPE_NULL)
809                     {
810                         ride_type_set_invented(rideEntry->ride_type[j]);
811                     }
812                 }
813             }
814         }
815 
816         for (uint8_t i = 0; i < MAX_SCENERY_GROUP_OBJECTS; i++)
817         {
818             const rct_scenery_group_entry* groupEntry = get_scenery_group_entry(i);
819 
820             if (groupEntry != nullptr)
821                 research_insert_scenery_group_entry(i, true);
822         }
823     }
824 }
825 
research_items_make_all_unresearched()826 void research_items_make_all_unresearched()
827 {
828     gResearchItemsUninvented.insert(
829         gResearchItemsUninvented.end(), std::make_move_iterator(gResearchItemsInvented.begin()),
830         std::make_move_iterator(gResearchItemsInvented.end()));
831     gResearchItemsInvented.clear();
832 }
833 
research_items_make_all_researched()834 void research_items_make_all_researched()
835 {
836     gResearchItemsInvented.insert(
837         gResearchItemsInvented.end(), std::make_move_iterator(gResearchItemsUninvented.begin()),
838         std::make_move_iterator(gResearchItemsUninvented.end()));
839     gResearchItemsUninvented.clear();
840 }
841 
842 /**
843  *
844  *  rct2: 0x00685A93
845  */
research_items_shuffle()846 void research_items_shuffle()
847 {
848     std::shuffle(std::begin(gResearchItemsUninvented), std::end(gResearchItemsUninvented), std::default_random_engine{});
849 }
850 
IsAlwaysResearched() const851 bool ResearchItem::IsAlwaysResearched() const
852 {
853     return (flags & (RESEARCH_ENTRY_FLAG_RIDE_ALWAYS_RESEARCHED | RESEARCH_ENTRY_FLAG_SCENERY_SET_ALWAYS_RESEARCHED)) != 0;
854 }
855 
IsNull() const856 bool ResearchItem::IsNull() const
857 {
858     return entryIndex == OBJECT_ENTRY_INDEX_NULL;
859 }
860 
SetNull()861 void ResearchItem::SetNull()
862 {
863     entryIndex = OBJECT_ENTRY_INDEX_NULL;
864 }
865 
Exists() const866 bool ResearchItem::Exists() const
867 {
868     for (auto const& researchItem : gResearchItemsUninvented)
869     {
870         if (researchItem == *this)
871         {
872             return true;
873         }
874     }
875     for (auto const& researchItem : gResearchItemsInvented)
876     {
877         if (researchItem == *this)
878         {
879             return true;
880         }
881     }
882     return false;
883 }
884 
885 // clang-format off
886 static constexpr const rct_string_id _editorInventionsResearchCategories[] = {
887     STR_RESEARCH_NEW_TRANSPORT_RIDES,
888     STR_RESEARCH_NEW_GENTLE_RIDES,
889     STR_RESEARCH_NEW_ROLLER_COASTERS,
890     STR_RESEARCH_NEW_THRILL_RIDES,
891     STR_RESEARCH_NEW_WATER_RIDES,
892     STR_RESEARCH_NEW_SHOPS_AND_STALLS,
893     STR_RESEARCH_NEW_SCENERY_AND_THEMING,
894 };
895 // clang-format on
896 
GetCategoryInventionString() const897 rct_string_id ResearchItem::GetCategoryInventionString() const
898 {
899     const auto categoryValue = EnumValue(category);
900     Guard::Assert(categoryValue <= 6, "Unsupported category invention string");
901     return _editorInventionsResearchCategories[categoryValue];
902 }
903 
904 // clang-format off
905 static constexpr const rct_string_id _researchCategoryNames[] = {
906     STR_RESEARCH_CATEGORY_TRANSPORT,
907     STR_RESEARCH_CATEGORY_GENTLE,
908     STR_RESEARCH_CATEGORY_ROLLERCOASTER,
909     STR_RESEARCH_CATEGORY_THRILL,
910     STR_RESEARCH_CATEGORY_WATER,
911     STR_RESEARCH_CATEGORY_SHOP,
912     STR_RESEARCH_CATEGORY_SCENERY_GROUP,
913 };
914 // clang-format on
915 
GetCategoryName() const916 rct_string_id ResearchItem::GetCategoryName() const
917 {
918     const auto categoryValue = EnumValue(category);
919     Guard::Assert(categoryValue <= 6, "Unsupported category name");
920     return _researchCategoryNames[categoryValue];
921 }
922 
operator ==(const ResearchItem & rhs) const923 bool ResearchItem::operator==(const ResearchItem& rhs) const
924 {
925     return (entryIndex == rhs.entryIndex && baseRideType == rhs.baseRideType && type == rhs.type);
926 }
927 
928 static std::bitset<RIDE_TYPE_COUNT> _seenRideType = {};
929 
research_update_first_of_type(ResearchItem * researchItem)930 static void research_update_first_of_type(ResearchItem* researchItem)
931 {
932     if (researchItem->IsNull())
933         return;
934 
935     if (researchItem->type != Research::EntryType::Ride)
936         return;
937 
938     auto rideType = researchItem->baseRideType;
939     if (rideType >= RIDE_TYPE_COUNT)
940     {
941         log_error("Research item has non-existent ride type index %d", rideType);
942         return;
943     }
944 
945     const auto& rtd = GetRideTypeDescriptor(rideType);
946     if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY))
947     {
948         researchItem->flags |= RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE;
949         return;
950     }
951 
952     if (!_seenRideType[rideType])
953         researchItem->flags |= RESEARCH_ENTRY_FLAG_FIRST_OF_TYPE;
954 }
955 
research_mark_ride_type_as_seen(const ResearchItem & researchItem)956 static void research_mark_ride_type_as_seen(const ResearchItem& researchItem)
957 {
958     auto rideType = researchItem.baseRideType;
959     if (rideType >= RIDE_TYPE_COUNT)
960         return;
961 
962     _seenRideType[rideType] = true;
963 }
964 
research_determine_first_of_type()965 void research_determine_first_of_type()
966 {
967     _seenRideType.reset();
968 
969     for (const auto& researchItem : gResearchItemsInvented)
970     {
971         if (researchItem.type != Research::EntryType::Ride)
972             continue;
973 
974         auto rideType = researchItem.baseRideType;
975         if (rideType >= RIDE_TYPE_COUNT)
976             continue;
977 
978         const auto& rtd = GetRideTypeDescriptor(rideType);
979         if (rtd.HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY))
980             continue;
981 
982         // The last research item will also be present in gResearchItemsInvented.
983         // Avoid marking its ride type as "invented" prematurely.
984         if (gResearchLastItem.has_value() && !gResearchLastItem->IsNull() && researchItem == gResearchLastItem.value())
985             continue;
986 
987         // The next research item is (sometimes?) also present in gResearchItemsInvented, even though it isn't invented yet(!)
988         if (gResearchNextItem.has_value() && !gResearchNextItem->IsNull() && researchItem == gResearchNextItem.value())
989             continue;
990 
991         research_mark_ride_type_as_seen(researchItem);
992     }
993 
994     if (gResearchLastItem.has_value())
995     {
996         research_update_first_of_type(&gResearchLastItem.value());
997         research_mark_ride_type_as_seen(gResearchLastItem.value());
998     }
999     if (gResearchNextItem.has_value())
1000     {
1001         research_update_first_of_type(&gResearchNextItem.value());
1002         research_mark_ride_type_as_seen(gResearchNextItem.value());
1003     }
1004 
1005     for (auto& researchItem : gResearchItemsUninvented)
1006     {
1007         // The next research item is (sometimes?) also present in gResearchItemsUninvented
1008         if (gResearchNextItem.has_value() && !gResearchNextItem->IsNull() && researchItem == gResearchNextItem.value())
1009         {
1010             // Copy the "first of type" flag.
1011             researchItem.flags = gResearchNextItem->flags;
1012             continue;
1013         }
1014 
1015         research_update_first_of_type(&researchItem);
1016         research_mark_ride_type_as_seen(researchItem);
1017     }
1018 }
1019