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