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 "../Context.h"
11 #include "../Diagnostic.h"
12 #include "../Editor.h"
13 #include "../Game.h"
14 #include "../GameState.h"
15 #include "../OpenRCT2.h"
16 #include "../ParkImporter.h"
17 #include "../config/Config.h"
18 #include "../core/Console.hpp"
19 #include "../core/FileStream.h"
20 #include "../core/IStream.hpp"
21 #include "../core/Numerics.hpp"
22 #include "../core/Path.hpp"
23 #include "../core/Random.hpp"
24 #include "../core/String.hpp"
25 #include "../interface/Viewport.h"
26 #include "../localisation/Date.h"
27 #include "../localisation/Localisation.h"
28 #include "../management/Award.h"
29 #include "../management/Finance.h"
30 #include "../management/Marketing.h"
31 #include "../management/NewsItem.h"
32 #include "../management/Research.h"
33 #include "../network/network.h"
34 #include "../object/ObjectLimits.h"
35 #include "../object/ObjectList.h"
36 #include "../object/ObjectManager.h"
37 #include "../object/ObjectRepository.h"
38 #include "../peep/Guest.h"
39 #include "../peep/RideUseSystem.h"
40 #include "../peep/Staff.h"
41 #include "../rct12/RCT12.h"
42 #include "../rct12/SawyerChunkReader.h"
43 #include "../rct12/SawyerEncoding.h"
44 #include "../rct2/RCT2.h"
45 #include "../ride/Ride.h"
46 #include "../ride/RideData.h"
47 #include "../ride/RideRatings.h"
48 #include "../ride/ShopItem.h"
49 #include "../ride/Station.h"
50 #include "../ride/Track.h"
51 #include "../ride/TrainManager.h"
52 #include "../ride/Vehicle.h"
53 #include "../scenario/Scenario.h"
54 #include "../scenario/ScenarioRepository.h"
55 #include "../util/SawyerCoding.h"
56 #include "../util/Util.h"
57 #include "../world/Balloon.h"
58 #include "../world/Climate.h"
59 #include "../world/Duck.h"
60 #include "../world/EntityList.h"
61 #include "../world/EntityTweener.h"
62 #include "../world/Entrance.h"
63 #include "../world/Fountain.h"
64 #include "../world/Litter.h"
65 #include "../world/MapAnimation.h"
66 #include "../world/MoneyEffect.h"
67 #include "../world/Park.h"
68 #include "../world/Particle.h"
69 #include "../world/Scenery.h"
70 #include "../world/Sprite.h"
71 #include "../world/Surface.h"
72
73 #include <algorithm>
74 #include <bitset>
75
76 #define DECRYPT_MONEY(money) (static_cast<money32>(Numerics::rol32((money) ^ 0xF4EC9621, 13)))
77
78 /**
79 * Class to import RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6).
80 */
81 class S6Importer final : public IParkImporter
82 {
83 private:
84 IObjectRepository& _objectRepository;
85
86 const utf8* _s6Path = nullptr;
87 rct_s6_data _s6{};
88 uint8_t _gameVersion = 0;
89 bool _isSV7 = false;
90 std::bitset<RCT12_MAX_RIDES_IN_PARK> _isFlatRide{};
91
92 public:
S6Importer(IObjectRepository & objectRepository)93 S6Importer(IObjectRepository& objectRepository)
94 : _objectRepository(objectRepository)
95 {
96 }
97
Load(const utf8 * path)98 ParkLoadResult Load(const utf8* path) override
99 {
100 const utf8* extension = Path::GetExtension(path);
101 if (String::Equals(extension, ".sc6", true))
102 {
103 return LoadScenario(path);
104 }
105 if (String::Equals(extension, ".sv6", true))
106 {
107 return LoadSavedGame(path);
108 }
109
110 throw std::runtime_error("Invalid RCT2 park extension.");
111 }
112
LoadSavedGame(const utf8 * path,bool skipObjectCheck=false)113 ParkLoadResult LoadSavedGame(const utf8* path, bool skipObjectCheck = false) override
114 {
115 auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN);
116 auto result = LoadFromStream(&fs, false, skipObjectCheck);
117 _s6Path = path;
118 return result;
119 }
120
LoadScenario(const utf8 * path,bool skipObjectCheck=false)121 ParkLoadResult LoadScenario(const utf8* path, bool skipObjectCheck = false) override
122 {
123 auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN);
124 auto result = LoadFromStream(&fs, true, skipObjectCheck);
125 _s6Path = path;
126 return result;
127 }
128
LoadFromStream(OpenRCT2::IStream * stream,bool isScenario,bool skipObjectCheck=false,const utf8 * path=String::Empty)129 ParkLoadResult LoadFromStream(
130 OpenRCT2::IStream* stream, bool isScenario, [[maybe_unused]] bool skipObjectCheck = false,
131 const utf8* path = String::Empty) override
132 {
133 if (isScenario && !gConfigGeneral.allow_loading_with_incorrect_checksum && !SawyerEncoding::ValidateChecksum(stream))
134 {
135 throw IOException("Invalid checksum.");
136 }
137
138 auto chunkReader = SawyerChunkReader(stream);
139 chunkReader.ReadChunk(&_s6.header, sizeof(_s6.header));
140
141 log_verbose("saved game classic_flag = 0x%02x", _s6.header.classic_flag);
142 if (isScenario)
143 {
144 if (_s6.header.type != S6_TYPE_SCENARIO)
145 {
146 throw std::runtime_error("Park is not a scenario.");
147 }
148 chunkReader.ReadChunk(&_s6.info, sizeof(_s6.info));
149 }
150 else
151 {
152 if (_s6.header.type != S6_TYPE_SAVEDGAME)
153 {
154 throw std::runtime_error("Park is not a saved game.");
155 }
156 }
157
158 if (_s6.header.classic_flag == 0xf)
159 {
160 throw UnsupportedRCTCFlagException(_s6.header.classic_flag);
161 }
162
163 // Read packed objects
164 // TODO try to contain this more and not store objects until later
165 for (uint16_t i = 0; i < _s6.header.num_packed_objects; i++)
166 {
167 _objectRepository.ExportPackedObject(stream);
168 }
169
170 if (path)
171 {
172 auto extension = path_get_extension(path);
173 _isSV7 = _stricmp(extension, ".sv7") == 0;
174 }
175
176 chunkReader.ReadChunk(&_s6.Objects, sizeof(_s6.Objects));
177
178 if (isScenario)
179 {
180 chunkReader.ReadChunk(&_s6.elapsed_months, 16);
181 chunkReader.ReadChunk(&_s6.tile_elements, sizeof(_s6.tile_elements));
182 chunkReader.ReadChunk(&_s6.next_free_tile_element_pointer_index, 2560076);
183 chunkReader.ReadChunk(&_s6.guests_in_park, 4);
184 chunkReader.ReadChunk(&_s6.last_guests_in_park, 8);
185 chunkReader.ReadChunk(&_s6.park_rating, 2);
186 chunkReader.ReadChunk(&_s6.active_research_types, 1082);
187 chunkReader.ReadChunk(&_s6.current_expenditure, 16);
188 chunkReader.ReadChunk(&_s6.park_value, 4);
189 chunkReader.ReadChunk(&_s6.completed_company_value, 483816);
190 }
191 else
192 {
193 chunkReader.ReadChunk(&_s6.elapsed_months, 16);
194 chunkReader.ReadChunk(&_s6.tile_elements, sizeof(_s6.tile_elements));
195 chunkReader.ReadChunk(&_s6.next_free_tile_element_pointer_index, 3048816);
196 }
197
198 _s6Path = path;
199
200 return ParkLoadResult(GetRequiredObjects());
201 }
202
GetDetails(scenario_index_entry * dst)203 bool GetDetails(scenario_index_entry* dst) override
204 {
205 *dst = {};
206 return false;
207 }
208
Import()209 void Import() override
210 {
211 Initialise();
212
213 gEditorStep = _s6.info.editor_step;
214 gScenarioCategory = static_cast<SCENARIO_CATEGORY>(_s6.info.category);
215
216 // Some scenarios have their scenario details in UTF-8, due to earlier bugs in OpenRCT2.
217 auto loadMaybeUTF8 = [](std::string_view str) -> std::string {
218 return !IsLikelyUTF8(str) ? rct2_to_utf8(str, RCT2LanguageId::EnglishUK) : std::string(str);
219 };
220
221 if (_s6.header.type == S6_TYPE_SCENARIO)
222 {
223 gScenarioName = loadMaybeUTF8(_s6.info.name);
224 gScenarioDetails = loadMaybeUTF8(_s6.info.details);
225 }
226 else
227 {
228 // Saved games do not have an info chunk
229 gScenarioName = loadMaybeUTF8(_s6.scenario_name);
230 gScenarioDetails = loadMaybeUTF8(_s6.scenario_description);
231 }
232
233 gDateMonthsElapsed = static_cast<int32_t>(_s6.elapsed_months);
234 gDateMonthTicks = _s6.current_day;
235 gCurrentTicks = _s6.game_ticks_1;
236
237 scenario_rand_seed(_s6.scenario_srand_0, _s6.scenario_srand_1);
238
239 DetermineFlatRideStatus();
240 ImportTileElements();
241 ImportEntities();
242
243 gInitialCash = ToMoney64(_s6.initial_cash);
244 gBankLoan = ToMoney64(_s6.current_loan);
245 gParkFlags = _s6.park_flags;
246 gParkEntranceFee = _s6.park_entrance_fee;
247 // rct1_park_entrance_x
248 // rct1_park_entrance_y
249 // pad_013573EE
250 // rct1_park_entrance_z
251
252 ImportPeepSpawns();
253
254 gGuestChangeModifier = _s6.guest_count_change_modifier;
255 gResearchFundingLevel = _s6.current_research_level;
256 // pad_01357400
257 ImportResearchedRideTypes();
258 ImportResearchedRideEntries();
259 // _s6.researched_track_types_a
260 // _s6.researched_track_types_b
261
262 gNumGuestsInPark = _s6.guests_in_park;
263 gNumGuestsHeadingForPark = _s6.guests_heading_for_park;
264
265 for (size_t i = 0; i < RCT12_EXPENDITURE_TABLE_MONTH_COUNT; i++)
266 {
267 for (size_t j = 0; j < RCT12_EXPENDITURE_TYPE_COUNT; j++)
268 {
269 gExpenditureTable[i][j] = ToMoney64(_s6.expenditure_table[i][j]);
270 }
271 }
272
273 gNumGuestsInParkLastWeek = _s6.last_guests_in_park;
274 // pad_01357BCA
275 gStaffHandymanColour = _s6.handyman_colour;
276 gStaffMechanicColour = _s6.mechanic_colour;
277 gStaffSecurityColour = _s6.security_colour;
278
279 ImportResearchedSceneryItems();
280
281 gParkRating = _s6.park_rating;
282
283 auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark();
284 park.ResetHistories();
285 std::copy(std::begin(_s6.park_rating_history), std::end(_s6.park_rating_history), gParkRatingHistory);
286 for (size_t i = 0; i < std::size(_s6.guests_in_park_history); i++)
287 {
288 if (_s6.guests_in_park_history[i] != RCT12ParkHistoryUndefined)
289 {
290 gGuestsInParkHistory[i] = _s6.guests_in_park_history[i] * RCT12GuestsInParkHistoryFactor;
291 }
292 }
293
294 gResearchPriorities = _s6.active_research_types;
295 gResearchProgressStage = _s6.research_progress_stage;
296 if (_s6.last_researched_item_subject != RCT12_RESEARCHED_ITEMS_SEPARATOR)
297 gResearchLastItem = ResearchItem(
298 RCT12ResearchItem{ _s6.last_researched_item_subject, EnumValue(ResearchCategory::Transport) });
299 else
300 gResearchLastItem = std::nullopt;
301 // pad_01357CF8
302 if (_s6.next_research_item != RCT12_RESEARCHED_ITEMS_SEPARATOR)
303 gResearchNextItem = ResearchItem(RCT12ResearchItem{ _s6.next_research_item, _s6.next_research_category });
304 else
305 gResearchNextItem = std::nullopt;
306
307 gResearchProgress = _s6.research_progress;
308 gResearchExpectedDay = _s6.next_research_expected_day;
309 gResearchExpectedMonth = _s6.next_research_expected_month;
310 gGuestInitialHappiness = _s6.guest_initial_happiness;
311 gParkSize = _s6.park_size;
312 _guestGenerationProbability = _s6.guest_generation_probability;
313 gTotalRideValueForMoney = _s6.total_ride_value_for_money;
314 gMaxBankLoan = ToMoney64(_s6.maximum_loan);
315 gGuestInitialCash = _s6.guest_initial_cash;
316 gGuestInitialHunger = _s6.guest_initial_hunger;
317 gGuestInitialThirst = _s6.guest_initial_thirst;
318 gScenarioObjective.Type = _s6.objective_type;
319 gScenarioObjective.Year = _s6.objective_year;
320 // pad_013580FA
321 gScenarioObjective.Currency = _s6.objective_currency;
322 // In RCT2, the ride string IDs start at index STR_0002 and are directly mappable.
323 // This is not always the case in OpenRCT2, so we use the actual ride ID.
324 if (gScenarioObjective.Type == OBJECTIVE_BUILD_THE_BEST)
325 gScenarioObjective.RideId = _s6.objective_guests - RCT2_RIDE_STRING_START;
326 else
327 gScenarioObjective.NumGuests = _s6.objective_guests;
328 ImportMarketingCampaigns();
329
330 gCurrentExpenditure = ToMoney64(_s6.current_expenditure);
331 gCurrentProfit = ToMoney64(_s6.current_profit);
332 gWeeklyProfitAverageDividend = ToMoney64(_s6.weekly_profit_average_dividend);
333 gWeeklyProfitAverageDivisor = _s6.weekly_profit_average_divisor;
334 // pad_0135833A
335
336 gParkValue = ToMoney64(_s6.park_value);
337
338 for (size_t i = 0; i < RCT12_FINANCE_GRAPH_SIZE; i++)
339 {
340 gCashHistory[i] = ToMoney64(_s6.balance_history[i]);
341 gWeeklyProfitHistory[i] = ToMoney64(_s6.weekly_profit_history[i]);
342 gParkValueHistory[i] = ToMoney64(_s6.park_value_history[i]);
343 }
344
345 gScenarioCompletedCompanyValue = RCT12CompletedCompanyValueToOpenRCT2(_s6.completed_company_value);
346 gTotalAdmissions = _s6.total_admissions;
347 gTotalIncomeFromAdmissions = ToMoney64(_s6.income_from_admissions);
348 gCompanyValue = ToMoney64(_s6.company_value);
349 std::memcpy(gPeepWarningThrottle, _s6.peep_warning_throttle, sizeof(_s6.peep_warning_throttle));
350
351 // Awards
352 for (int32_t i = 0; i < RCT12_MAX_AWARDS; i++)
353 {
354 rct12_award* src = &_s6.awards[i];
355 Award* dst = &gCurrentAwards[i];
356 dst->Time = src->time;
357 dst->Type = src->type;
358 }
359
360 gLandPrice = _s6.land_price;
361 gConstructionRightsPrice = _s6.construction_rights_price;
362 // unk_01358774
363 // pad_01358776
364 // _s6.cd_key
365 _gameVersion = _s6.game_version_number;
366 gScenarioCompanyValueRecord = _s6.completed_company_value_record;
367 // _s6.loan_hash;
368 // pad_013587CA
369 gHistoricalProfit = ToMoney64(_s6.historical_profit);
370 // pad_013587D4
371 gScenarioCompletedBy = std::string_view(_s6.scenario_completed_name, sizeof(_s6.scenario_completed_name));
372 gCash = ToMoney64(DECRYPT_MONEY(_s6.cash));
373 // pad_013587FC
374 gParkRatingCasualtyPenalty = _s6.park_rating_casualty_penalty;
375 gMapSize = _s6.map_size;
376 gSamePriceThroughoutPark = _s6.same_price_throughout
377 | (static_cast<uint64_t>(_s6.same_price_throughout_extended) << 32);
378 _suggestedGuestMaximum = _s6.suggested_max_guests;
379 gScenarioParkRatingWarningDays = _s6.park_rating_warning_days;
380 gLastEntranceStyle = _s6.last_entrance_style;
381 // rct1_water_colour
382 // pad_01358842
383 ImportResearchList();
384 gMapBaseZ = _s6.map_base_z;
385 gBankLoanInterestRate = _s6.current_interest_rate;
386 // pad_0135934B
387 // Preserve compatibility with vanilla RCT2's save format.
388 gParkEntrances.clear();
389 for (uint8_t i = 0; i < RCT12_MAX_PARK_ENTRANCES; i++)
390 {
391 if (_s6.park_entrance_x[i] != LOCATION_NULL)
392 {
393 CoordsXYZD entrance;
394 entrance.x = _s6.park_entrance_x[i];
395 entrance.y = _s6.park_entrance_y[i];
396 entrance.z = _s6.park_entrance_z[i];
397 entrance.direction = _s6.park_entrance_direction[i];
398 gParkEntrances.push_back(entrance);
399 }
400 }
401 if (_s6.header.type == S6_TYPE_SCENARIO)
402 {
403 // _s6.scenario_filename is wrong for some RCT2 expansion scenarios, so we use the real filename
404 String::Set(gScenarioFileName, sizeof(gScenarioFileName), Path::GetFileName(_s6Path));
405 }
406 else
407 {
408 // For savegames the filename can be arbitrary, so we have no choice but to rely on the name provided
409 String::Set(gScenarioFileName, sizeof(gScenarioFileName), _s6.scenario_filename);
410 }
411 std::memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names));
412 gCurrentRealTimeTicks = 0;
413
414 ImportRides();
415
416 gSavedAge = _s6.saved_age;
417 gSavedView = ScreenCoordsXY{ _s6.saved_view_x, _s6.saved_view_y };
418 gSavedViewZoom = _s6.saved_view_zoom;
419 gSavedViewRotation = _s6.saved_view_rotation;
420
421 ImportRideRatingsCalcData();
422 ImportRideMeasurements();
423 gNextGuestNumber = _s6.next_guest_index;
424 gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos;
425 // unk_13CA73E
426 // pad_13CA73F
427 // unk_13CA740
428 gClimate = ClimateType{ _s6.climate };
429 // pad_13CA741;
430 // byte_13CA742
431 // pad_013CA747
432 gClimateUpdateTimer = _s6.climate_update_timer;
433 gClimateCurrent.Weather = WeatherType{ _s6.current_weather };
434 gClimateNext.Weather = WeatherType{ _s6.next_weather };
435 gClimateCurrent.Temperature = _s6.temperature;
436 gClimateNext.Temperature = _s6.next_temperature;
437 gClimateCurrent.WeatherEffect = WeatherEffectType{ _s6.current_weather_effect };
438 gClimateNext.WeatherEffect = WeatherEffectType{ _s6.next_weather_effect };
439 gClimateCurrent.WeatherGloom = _s6.current_weather_gloom;
440 gClimateNext.WeatherGloom = _s6.next_weather_gloom;
441 gClimateCurrent.Level = static_cast<WeatherLevel>(_s6.current_weather_level);
442 gClimateNext.Level = static_cast<WeatherLevel>(_s6.next_weather_level);
443
444 // News items
445 News::InitQueue();
446 for (size_t i = 0; i < RCT12_MAX_NEWS_ITEMS; i++)
447 {
448 const rct12_news_item* src = &_s6.news_items[i];
449 News::Item* dst = &gNewsItems[i];
450 if (src->Type < News::ItemTypeCount)
451 {
452 dst->Type = static_cast<News::ItemType>(src->Type);
453 dst->Flags = src->Flags;
454 dst->Assoc = src->Assoc;
455 dst->Ticks = src->Ticks;
456 dst->MonthYear = src->MonthYear;
457 dst->Day = src->Day;
458 dst->Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text)));
459 }
460 else
461 {
462 // In case where news item type is broken, consider all remaining news items invalid.
463 log_error("Invalid news type 0x%x for news item %d, ignoring remaining news items", src->Type, i);
464 // Still need to set the correct type to properly terminate the queue
465 dst->Type = News::ItemType::Null;
466 break;
467 }
468 }
469
470 // pad_13CE730
471 // rct1_scenario_flags
472 gWidePathTileLoopPosition.x = _s6.wide_path_tile_loop_x;
473 gWidePathTileLoopPosition.y = _s6.wide_path_tile_loop_y;
474 // pad_13CE778
475
476 // Fix and set dynamic variables
477 map_strip_ghost_flag_from_elements();
478 game_convert_strings_to_utf8();
479 map_count_remaining_land_rights();
480 determine_ride_entrance_and_exit_locations();
481
482 park.Name = GetUserString(_s6.park_name);
483
484 FixLandOwnership();
485
486 research_determine_first_of_type();
487 staff_update_greyed_patrol_areas();
488 }
489
FixLandOwnership() const490 void FixLandOwnership() const
491 {
492 if (String::Equals(_s6.scenario_filename, "Europe - European Cultural Festival.SC6"))
493 {
494 // This scenario breaks pathfinding. Create passages between the worlds. (List is grouped by neighbouring tiles.)
495 // clang-format off
496 FixLandOwnershipTilesWithOwnership(
497 {
498 { 67, 94 }, { 68, 94 }, { 69, 94 },
499 { 58, 24 }, { 58, 25 }, { 58, 26 }, { 58, 27 }, { 58, 28 }, { 58, 29 }, { 58, 30 }, { 58, 31 }, { 58, 32 },
500 { 26, 44 }, { 26, 45 },
501 { 32, 79 }, { 32, 80 }, { 32, 81 },
502 },
503 OWNERSHIP_OWNED);
504 // clang-format on
505 }
506 else if (String::Equals(gScenarioFileName, "N America - Extreme Hawaiian Island.SC6"))
507 {
508 FixLandOwnershipTilesWithOwnership(
509 {
510 { 132, 124 },
511 { 133, 124 },
512 { 133, 125 },
513 { 133, 126 },
514 { 119, 35 },
515 { 132, 62 },
516 { 133, 67 },
517 { 136, 71 },
518 { 87, 33 },
519 { 87, 34 },
520 { 90, 36 },
521 { 91, 36 },
522 },
523 OWNERSHIP_OWNED);
524 // We set the doNotDowngrade flag for cases where the player has used a cheat to own all land.
525 FixLandOwnershipTilesWithOwnership(
526 {
527 { 49, 99 },
528 { 50, 99 },
529 { 88, 110 },
530 },
531 OWNERSHIP_AVAILABLE, true);
532 }
533 }
534
ImportRides()535 void ImportRides()
536 {
537 for (uint8_t index = 0; index < RCT12_MAX_RIDES_IN_PARK; index++)
538 {
539 auto src = &_s6.rides[index];
540 if (src->type != RIDE_TYPE_NULL)
541 {
542 const auto rideId = static_cast<ride_id_t>(index);
543 auto dst = GetOrAllocateRide(rideId);
544 ImportRide(dst, src, rideId);
545 }
546 }
547 }
548
549 /**
550 * This code is needed to detect hacks where a tracked ride has been made invisible
551 * by setting its ride type to a flat ride.
552 *
553 * The function should classify rides as follows:
554 * 1. If the ride type is tracked and its vehicles also belong on tracks, it should be classified as tracked.
555 * 2. If the ride type is a flat ride, but its vehicles belong on tracks,
556 * it should be classified as tracked (Crooked House mod).
557 * 3. If the ride type is tracked and its vehicles belong to a flat ride, it should be classified as tracked.
558 * 4. If the ride type is a flat ride and its vehicles also belong to a flat ride, it should be classified as a flat ride.
559 */
DetermineFlatRideStatus()560 void DetermineFlatRideStatus()
561 {
562 for (uint8_t index = 0; index < RCT12_MAX_RIDES_IN_PARK; index++)
563 {
564 auto src = &_s6.rides[index];
565 if (src->type == RIDE_TYPE_NULL)
566 continue;
567
568 auto subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->subtype);
569 auto* rideEntry = get_ride_entry(subtype);
570 // If the ride is tracked, we don’t need to check the vehicle any more.
571 if (!GetRideTypeDescriptor(src->type).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
572 {
573 _isFlatRide[index] = false;
574 continue;
575 }
576
577 // We have established the ride type is a flat ride, which means the vehicle now determines whether it is a
578 // true flat ride (scenario 4) or a tracked ride with an invisibility hack (scenario 2).
579 ObjectEntryIndex originalRideType = src->type;
580 if (rideEntry != nullptr)
581 {
582 originalRideType = ride_entry_get_first_non_null_ride_type(rideEntry);
583 }
584 const auto isFlatRide = GetRideTypeDescriptor(originalRideType).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE);
585 _isFlatRide.set(static_cast<size_t>(index), isFlatRide);
586 }
587 }
588
IsFlatRide(const uint8_t rct12RideIndex)589 bool IsFlatRide(const uint8_t rct12RideIndex)
590 {
591 if (rct12RideIndex == RCT12_RIDE_ID_NULL)
592 return false;
593 return _isFlatRide[rct12RideIndex];
594 }
595
ImportRide(Ride * dst,const rct2_ride * src,const ride_id_t rideIndex)596 void ImportRide(Ride* dst, const rct2_ride* src, const ride_id_t rideIndex)
597 {
598 *dst = {};
599 dst->id = rideIndex;
600
601 ObjectEntryIndex rideType = src->type;
602 auto subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->subtype);
603 if (RCT2RideTypeNeedsConversion(src->type))
604 {
605 auto* rideEntry = get_ride_entry(subtype);
606 if (rideEntry != nullptr)
607 {
608 rideType = RCT2RideTypeToOpenRCT2RideType(src->type, rideEntry);
609 }
610 }
611
612 if (rideType >= RIDE_TYPE_COUNT)
613 {
614 log_error("Invalid ride type for a ride in this save.");
615 throw UnsupportedRideTypeException(rideType);
616 }
617 dst->type = rideType;
618 dst->subtype = subtype;
619 // pad_002;
620 dst->mode = static_cast<RideMode>(src->mode);
621 dst->colour_scheme_type = src->colour_scheme_type;
622
623 for (uint8_t i = 0; i < RCT2_MAX_CARS_PER_TRAIN; i++)
624 {
625 dst->vehicle_colours[i].Body = src->vehicle_colours[i].body_colour;
626 dst->vehicle_colours[i].Trim = src->vehicle_colours[i].trim_colour;
627 }
628
629 // pad_046;
630 dst->status = static_cast<RideStatus>(src->status);
631
632 dst->default_name_number = src->name_arguments_number;
633 if (is_user_string_id(src->name))
634 {
635 dst->custom_name = GetUserString(src->name);
636 }
637 else
638 {
639 dst->default_name_number = src->name_arguments_number;
640 }
641
642 if (src->overall_view.IsNull())
643 {
644 dst->overall_view.SetNull();
645 }
646 else
647 {
648 auto tileLoc = TileCoordsXY(src->overall_view.x, src->overall_view.y);
649 dst->overall_view = tileLoc.ToCoordsXY();
650 }
651
652 for (int32_t i = 0; i < RCT12_MAX_STATIONS_PER_RIDE; i++)
653 {
654 if (src->station_starts[i].IsNull())
655 {
656 dst->stations[i].Start.SetNull();
657 }
658 else
659 {
660 auto tileStartLoc = TileCoordsXY(src->station_starts[i].x, src->station_starts[i].y);
661 dst->stations[i].Start = tileStartLoc.ToCoordsXY();
662 }
663 dst->stations[i].Height = src->station_heights[i];
664 dst->stations[i].Length = src->station_length[i];
665 dst->stations[i].Depart = src->station_depart[i];
666 dst->stations[i].TrainAtStation = src->train_at_station[i];
667 // Direction is fixed later.
668
669 if (src->entrances[i].IsNull())
670 ride_clear_entrance_location(dst, i);
671 else
672 ride_set_entrance_location(dst, i, { src->entrances[i].x, src->entrances[i].y, src->station_heights[i], 0 });
673
674 if (src->exits[i].IsNull())
675 ride_clear_exit_location(dst, i);
676 else
677 ride_set_exit_location(dst, i, { src->exits[i].x, src->exits[i].y, src->station_heights[i], 0 });
678
679 dst->stations[i].LastPeepInQueue = src->last_peep_in_queue[i];
680
681 dst->stations[i].SegmentLength = src->length[i];
682 dst->stations[i].SegmentTime = src->time[i];
683
684 dst->stations[i].QueueTime = src->queue_time[i];
685
686 dst->stations[i].QueueLength = src->queue_length[i];
687 }
688 // All other values take 0 as their default. Since they're already memset to that, no need to do it again.
689 for (int32_t i = RCT12_MAX_STATIONS_PER_RIDE; i < MAX_STATIONS; i++)
690 {
691 dst->stations[i].Start.SetNull();
692 dst->stations[i].TrainAtStation = RideStation::NO_TRAIN;
693 ride_clear_entrance_location(dst, i);
694 ride_clear_exit_location(dst, i);
695 dst->stations[i].LastPeepInQueue = SPRITE_INDEX_NULL;
696 }
697
698 for (int32_t i = 0; i <= RCT2_MAX_VEHICLES_PER_RIDE; i++)
699 {
700 dst->vehicles[i] = src->vehicles[i];
701 }
702 for (int32_t i = RCT2_MAX_VEHICLES_PER_RIDE; i <= MAX_VEHICLES_PER_RIDE; i++)
703 {
704 dst->vehicles[i] = SPRITE_INDEX_NULL;
705 }
706
707 dst->depart_flags = src->depart_flags;
708
709 dst->num_stations = src->num_stations;
710 dst->num_vehicles = src->num_vehicles;
711 dst->num_cars_per_train = src->num_cars_per_train;
712 dst->proposed_num_vehicles = src->proposed_num_vehicles;
713 dst->proposed_num_cars_per_train = src->proposed_num_cars_per_train;
714 dst->max_trains = src->max_trains;
715 dst->MinCarsPerTrain = src->GetMinCarsPerTrain();
716 dst->MaxCarsPerTrain = src->GetMaxCarsPerTrain();
717 dst->min_waiting_time = src->min_waiting_time;
718 dst->max_waiting_time = src->max_waiting_time;
719
720 // Includes time_limit, num_laps, launch_speed, speed, rotations
721 dst->operation_option = src->operation_option;
722
723 dst->boat_hire_return_direction = src->boat_hire_return_direction;
724 dst->boat_hire_return_position = { src->boat_hire_return_position.x, src->boat_hire_return_position.y };
725
726 dst->special_track_elements = src->special_track_elements;
727 // pad_0D6[2];
728
729 dst->max_speed = src->max_speed;
730 dst->average_speed = src->average_speed;
731 dst->current_test_segment = src->current_test_segment;
732 dst->average_speed_test_timeout = src->average_speed_test_timeout;
733 // pad_0E2[0x2];
734
735 dst->max_positive_vertical_g = src->max_positive_vertical_g;
736 dst->max_negative_vertical_g = src->max_negative_vertical_g;
737 dst->max_lateral_g = src->max_lateral_g;
738 dst->previous_vertical_g = src->previous_vertical_g;
739 dst->previous_lateral_g = src->previous_lateral_g;
740 // pad_106[0x2];
741 dst->testing_flags = src->testing_flags;
742
743 if (src->cur_test_track_location.IsNull())
744 {
745 dst->CurTestTrackLocation.SetNull();
746 }
747 else
748 {
749 dst->CurTestTrackLocation = { src->cur_test_track_location.x, src->cur_test_track_location.y,
750 src->cur_test_track_z };
751 }
752
753 dst->turn_count_default = src->turn_count_default;
754 dst->turn_count_banked = src->turn_count_banked;
755 dst->turn_count_sloped = src->turn_count_sloped;
756 if (dst->type == RIDE_TYPE_MINI_GOLF)
757 dst->holes = src->inversions & 0x1F;
758 else
759 dst->inversions = src->inversions & 0x1F;
760 dst->sheltered_eighths = src->inversions >> 5;
761 dst->drops = src->drops;
762 dst->start_drop_height = src->start_drop_height;
763 dst->highest_drop_height = src->highest_drop_height;
764 dst->sheltered_length = src->sheltered_length;
765 dst->var_11C = src->var_11C;
766 dst->num_sheltered_sections = src->num_sheltered_sections;
767
768 dst->cur_num_customers = src->cur_num_customers;
769 dst->num_customers_timeout = src->num_customers_timeout;
770
771 for (uint8_t i = 0; i < RCT2_CUSTOMER_HISTORY_SIZE; i++)
772 {
773 dst->num_customers[i] = src->num_customers[i];
774 }
775
776 dst->price[0] = src->price;
777
778 for (uint8_t i = 0; i < 2; i++)
779 {
780 dst->ChairliftBullwheelLocation[i] = { src->chairlift_bullwheel_location[i].x,
781 src->chairlift_bullwheel_location[i].y, src->chairlift_bullwheel_z[i] };
782 }
783
784 dst->ratings = src->ratings;
785 dst->value = src->value;
786
787 dst->chairlift_bullwheel_rotation = src->chairlift_bullwheel_rotation;
788
789 dst->satisfaction = src->satisfaction;
790 dst->satisfaction_time_out = src->satisfaction_time_out;
791 dst->satisfaction_next = src->satisfaction_next;
792
793 dst->window_invalidate_flags = src->window_invalidate_flags;
794 // pad_14E[0x02];
795
796 dst->total_customers = src->total_customers;
797 dst->total_profit = ToMoney64(src->total_profit);
798 dst->popularity = src->popularity;
799 dst->popularity_time_out = src->popularity_time_out;
800 dst->popularity_next = src->popularity_next;
801
802 ImportNumRiders(dst, rideIndex);
803
804 dst->music_tune_id = src->music_tune_id;
805 dst->slide_in_use = src->slide_in_use;
806 // Includes maze_tiles
807 dst->slide_peep = src->slide_peep;
808 // pad_160[0xE];
809 dst->slide_peep_t_shirt_colour = src->slide_peep_t_shirt_colour;
810 // pad_16F[0x7];
811 dst->spiral_slide_progress = src->spiral_slide_progress;
812 // pad_177[0x9];
813 dst->build_date = static_cast<int32_t>(src->build_date);
814 dst->upkeep_cost = src->upkeep_cost;
815 dst->race_winner = src->race_winner;
816 // pad_186[0x02];
817 dst->music_position = src->music_position;
818
819 dst->breakdown_reason_pending = src->breakdown_reason_pending;
820 dst->mechanic_status = src->mechanic_status;
821 dst->mechanic = src->mechanic;
822 dst->inspection_station = src->inspection_station;
823 dst->broken_vehicle = src->broken_vehicle;
824 dst->broken_car = src->broken_car;
825 dst->breakdown_reason = src->breakdown_reason;
826
827 dst->price[1] = src->price_secondary;
828
829 dst->reliability = src->reliability;
830 dst->unreliability_factor = src->unreliability_factor;
831 dst->downtime = src->downtime;
832 dst->inspection_interval = src->inspection_interval;
833 dst->last_inspection = src->last_inspection;
834
835 for (uint8_t i = 0; i < RCT2_DOWNTIME_HISTORY_SIZE; i++)
836 {
837 dst->downtime_history[i] = src->downtime_history[i];
838 }
839
840 dst->no_primary_items_sold = src->no_primary_items_sold;
841 dst->no_secondary_items_sold = src->no_secondary_items_sold;
842
843 dst->breakdown_sound_modifier = src->breakdown_sound_modifier;
844 dst->not_fixed_timeout = src->not_fixed_timeout;
845 dst->last_crash_type = src->last_crash_type;
846 dst->connected_message_throttle = src->connected_message_throttle;
847
848 dst->income_per_hour = ToMoney64(src->income_per_hour);
849 dst->profit = ToMoney64(src->profit);
850
851 for (uint8_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++)
852 {
853 dst->track_colour[i].main = src->track_colour_main[i];
854 dst->track_colour[i].additional = src->track_colour_additional[i];
855 dst->track_colour[i].supports = src->track_colour_supports[i];
856 }
857 // This stall was not colourable in RCT2.
858 if (dst->type == RIDE_TYPE_FOOD_STALL)
859 {
860 auto object = object_entry_get_object(ObjectType::Ride, dst->subtype);
861 if (object != nullptr && object->GetIdentifier() == "rct2.icecr1")
862 {
863 dst->track_colour[0].main = COLOUR_LIGHT_BLUE;
864 }
865 }
866
867 auto musicStyle = OBJECT_ENTRY_INDEX_NULL;
868 if (GetRideTypeDescriptor(dst->type).HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC))
869 {
870 musicStyle = src->music;
871 }
872 dst->music = musicStyle;
873
874 auto entranceStyle = src->entrance_style;
875 // In SV7, "plain" entrances are invisible.
876 if (_isSV7 && entranceStyle == RCT12_STATION_STYLE_PLAIN)
877 {
878 entranceStyle = RCT12_STATION_STYLE_INVISIBLE;
879 }
880 dst->entrance_style = entranceStyle;
881 dst->vehicle_change_timeout = src->vehicle_change_timeout;
882 dst->num_block_brakes = src->num_block_brakes;
883 dst->lift_hill_speed = src->lift_hill_speed;
884 dst->guests_favourite = src->guests_favourite;
885 dst->lifecycle_flags = src->lifecycle_flags;
886
887 for (uint8_t i = 0; i < RCT2_MAX_CARS_PER_TRAIN; i++)
888 {
889 dst->vehicle_colours[i].Ternary = src->vehicle_colours_extended[i];
890 }
891
892 dst->total_air_time = src->total_air_time;
893 dst->current_test_station = src->current_test_station;
894 dst->num_circuits = src->num_circuits;
895 dst->CableLiftLoc = { src->cable_lift_x, src->cable_lift_y, src->cable_lift_z * COORDS_Z_STEP };
896 // pad_1FD;
897 dst->cable_lift = src->cable_lift;
898
899 // pad_208[0x58];
900 }
901
ImportRideRatingsCalcData()902 void ImportRideRatingsCalcData()
903 {
904 const auto& src = _s6.ride_ratings_calc_data;
905 auto& dst = gRideRatingUpdateState;
906 dst = {};
907 dst.Proximity = { src.proximity_x, src.proximity_y, src.proximity_z };
908 dst.ProximityStart = { src.proximity_start_x, src.proximity_start_y, src.proximity_start_z };
909 dst.CurrentRide = RCT12RideIdToOpenRCT2RideId(src.current_ride);
910 dst.State = src.state;
911 if (src.current_ride < RCT12_MAX_RIDES_IN_PARK && _s6.rides[src.current_ride].type < std::size(RideTypeDescriptors))
912 dst.ProximityTrackType = RCT2TrackTypeToOpenRCT2(
913 src.proximity_track_type, _s6.rides[src.current_ride].type, IsFlatRide(src.current_ride));
914 else
915 dst.ProximityTrackType = 0xFF;
916 dst.ProximityBaseHeight = src.proximity_base_height;
917 dst.ProximityTotal = src.proximity_total;
918 for (size_t i = 0; i < std::size(src.proximity_scores); i++)
919 {
920 dst.ProximityScores[i] = src.proximity_scores[i];
921 }
922 dst.AmountOfBrakes = src.num_brakes;
923 dst.AmountOfReversers = src.num_reversers;
924 dst.StationFlags = src.station_flags;
925 }
926
ImportRideMeasurements()927 void ImportRideMeasurements()
928 {
929 for (const auto& src : _s6.ride_measurements)
930 {
931 if (src.ride_index != RCT12_RIDE_ID_NULL)
932 {
933 const auto rideId = static_cast<ride_id_t>(src.ride_index);
934 auto ride = get_ride(rideId);
935 if (ride != nullptr)
936 {
937 ride->measurement = std::make_unique<RideMeasurement>();
938 ImportRideMeasurement(*ride->measurement, src);
939 }
940 }
941 }
942 }
943
ImportRideMeasurement(RideMeasurement & dst,const RCT12RideMeasurement & src)944 void ImportRideMeasurement(RideMeasurement& dst, const RCT12RideMeasurement& src)
945 {
946 dst.flags = src.flags;
947 dst.last_use_tick = src.last_use_tick;
948 dst.num_items = src.num_items;
949 dst.current_item = src.current_item;
950 dst.vehicle_index = src.vehicle_index;
951 dst.current_station = src.current_station;
952 for (size_t i = 0; i < std::size(src.velocity); i++)
953 {
954 dst.velocity[i] = src.velocity[i];
955 dst.altitude[i] = src.altitude[i];
956 dst.vertical[i] = src.vertical[i];
957 dst.lateral[i] = src.lateral[i];
958 }
959 }
960
ImportResearchedRideTypes()961 void ImportResearchedRideTypes()
962 {
963 set_every_ride_type_not_invented();
964
965 for (int32_t rideType = 0; rideType < RCT2_RIDE_TYPE_COUNT; rideType++)
966 {
967 int32_t quadIndex = rideType >> 5;
968 int32_t bitIndex = rideType & 0x1F;
969 bool invented = (_s6.researched_ride_types[quadIndex] & (1UL << bitIndex));
970
971 if (invented)
972 ride_type_set_invented(rideType);
973 }
974 }
975
ImportResearchedRideEntries()976 void ImportResearchedRideEntries()
977 {
978 set_every_ride_entry_not_invented();
979
980 for (int32_t rideEntryIndex = 0; rideEntryIndex < MAX_RIDE_OBJECTS; rideEntryIndex++)
981 {
982 int32_t quadIndex = rideEntryIndex >> 5;
983 int32_t bitIndex = rideEntryIndex & 0x1F;
984 bool invented = (_s6.researched_ride_entries[quadIndex] & (1UL << bitIndex));
985
986 if (invented)
987 ride_entry_set_invented(rideEntryIndex);
988 }
989 }
990
ImportResearchedSceneryItems()991 void ImportResearchedSceneryItems()
992 {
993 set_all_scenery_items_not_invented();
994
995 for (uint16_t sceneryEntryIndex = 0; sceneryEntryIndex < RCT2_MAX_RESEARCHED_SCENERY_ITEMS; sceneryEntryIndex++)
996 {
997 int32_t quadIndex = sceneryEntryIndex >> 5;
998 int32_t bitIndex = sceneryEntryIndex & 0x1F;
999 bool invented = (_s6.researched_scenery_items[quadIndex] & (1UL << bitIndex));
1000
1001 if (invented)
1002 {
1003 ScenerySelection scenerySelection = { static_cast<uint8_t>((sceneryEntryIndex >> 8) & 0xFF),
1004 static_cast<uint16_t>(sceneryEntryIndex & 0xFF) };
1005
1006 // SV6 has room for 8 types of scenery, and sometimes scenery of non-existing types 5 and 6 is marked as
1007 // "invented".
1008 if (scenerySelection.SceneryType < SCENERY_TYPE_COUNT)
1009 {
1010 scenery_set_invented(scenerySelection);
1011 }
1012 }
1013 }
1014 }
1015
ImportResearchList()1016 void ImportResearchList()
1017 {
1018 bool invented = true;
1019 for (const auto& researchItem : _s6.research_items)
1020 {
1021 if (researchItem.IsInventedEndMarker())
1022 {
1023 invented = false;
1024 continue;
1025 }
1026 if (researchItem.IsUninventedEndMarker() || researchItem.IsRandomEndMarker())
1027 {
1028 break;
1029 }
1030
1031 if (invented)
1032 gResearchItemsInvented.emplace_back(researchItem);
1033 else
1034 gResearchItemsUninvented.emplace_back(researchItem);
1035 }
1036 }
1037
ImportBanner(Banner * dst,const RCT12Banner * src)1038 void ImportBanner(Banner* dst, const RCT12Banner* src)
1039 {
1040 *dst = {};
1041 dst->type = RCTEntryIndexToOpenRCT2EntryIndex(src->type);
1042 dst->flags = src->flags;
1043
1044 if (!(src->flags & BANNER_FLAG_LINKED_TO_RIDE) && is_user_string_id(src->string_idx))
1045 {
1046 dst->text = GetUserString(src->string_idx);
1047 }
1048
1049 if (src->flags & BANNER_FLAG_LINKED_TO_RIDE)
1050 {
1051 dst->ride_index = RCT12RideIdToOpenRCT2RideId(src->ride_index);
1052 }
1053 else
1054 {
1055 dst->colour = src->colour;
1056 }
1057
1058 dst->text_colour = src->text_colour;
1059 dst->position.x = src->x;
1060 dst->position.y = src->y;
1061 }
1062
Initialise()1063 void Initialise()
1064 {
1065 OpenRCT2::GetContext()->GetGameState()->InitAll(_s6.map_size);
1066 }
1067
1068 /**
1069 * Imports guest entry points.
1070 * Includes fixes for incorrectly set guest entry points in some scenarios.
1071 */
ImportPeepSpawns()1072 void ImportPeepSpawns()
1073 {
1074 // Many WW and TT have scenario_filename fields containing an incorrect filename. Check for both this filename
1075 // and the corrected filename.
1076
1077 // In this park, peep_spawns[0] is incorrect, and peep_spawns[1] is correct.
1078 if (String::Equals(_s6.scenario_filename, "WW South America - Rio Carnival.SC6")
1079 || String::Equals(_s6.scenario_filename, "South America - Rio Carnival.SC6"))
1080 {
1081 _s6.peep_spawns[0] = { 2160, 3167, 6, 1 };
1082 _s6.peep_spawns[1].x = RCT12_PEEP_SPAWN_UNDEFINED;
1083 }
1084 // In this park, peep_spawns[0] is correct. Just clear the other.
1085 else if (
1086 String::Equals(_s6.scenario_filename, "Great Wall of China Tourism Enhancement.SC6")
1087 || String::Equals(_s6.scenario_filename, "Asia - Great Wall of China Tourism Enhancement.SC6"))
1088 {
1089 _s6.peep_spawns[1].x = RCT12_PEEP_SPAWN_UNDEFINED;
1090 }
1091 // Amity Airfield has peeps entering from the corner of the tile, instead of the middle.
1092 else if (String::Equals(_s6.scenario_filename, "Amity Airfield.SC6"))
1093 {
1094 _s6.peep_spawns[0].y = 1296;
1095 }
1096 // #9926: Africa - Oasis has peeps spawning on the edge underground near the entrance
1097 else if (String::Equals(_s6.scenario_filename, "Africa - Oasis.SC6"))
1098 {
1099 _s6.peep_spawns[0].y = 2128;
1100 _s6.peep_spawns[0].z = 7;
1101 }
1102
1103 gPeepSpawns.clear();
1104 for (size_t i = 0; i < RCT12_MAX_PEEP_SPAWNS; i++)
1105 {
1106 if (_s6.peep_spawns[i].x != RCT12_PEEP_SPAWN_UNDEFINED)
1107 {
1108 PeepSpawn spawn = { _s6.peep_spawns[i].x, _s6.peep_spawns[i].y, _s6.peep_spawns[i].z * 16,
1109 _s6.peep_spawns[i].direction };
1110 gPeepSpawns.push_back(spawn);
1111 }
1112 }
1113 }
1114
ImportNumRiders(Ride * dst,const ride_id_t rideIndex)1115 void ImportNumRiders(Ride* dst, const ride_id_t rideIndex)
1116 {
1117 // The number of riders might have overflown or underflown. Re-calculate the value.
1118 uint16_t numRiders = 0;
1119 for (const auto& sprite : _s6.sprites)
1120 {
1121 if (sprite.unknown.sprite_identifier == RCT12SpriteIdentifier::Peep)
1122 {
1123 if (sprite.peep.current_ride == static_cast<RCT12RideId>(rideIndex)
1124 && (static_cast<PeepState>(sprite.peep.state) == PeepState::OnRide
1125 || static_cast<PeepState>(sprite.peep.state) == PeepState::EnteringRide))
1126 {
1127 numRiders++;
1128 }
1129 }
1130 }
1131 dst->num_riders = numRiders;
1132 }
1133
ImportTileElements()1134 void ImportTileElements()
1135 {
1136 // Build tile pointer cache (needed to get the first element at a certain location)
1137 auto tilePointerIndex = TilePointerIndex<RCT12TileElement>(RCT2_MAXIMUM_MAP_SIZE_TECHNICAL, _s6.tile_elements);
1138
1139 std::vector<TileElement> tileElements;
1140 for (TileCoordsXY coords = { 0, 0 }; coords.y < MAXIMUM_MAP_SIZE_TECHNICAL; coords.y++)
1141 {
1142 for (coords.x = 0; coords.x < MAXIMUM_MAP_SIZE_TECHNICAL; coords.x++)
1143 {
1144 if (coords.x >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL || coords.y >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL)
1145 {
1146 auto& dstElement = tileElements.emplace_back();
1147 dstElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE);
1148 dstElement.SetLastForTile(true);
1149 continue;
1150 }
1151
1152 RCT12TileElement* srcElement = tilePointerIndex.GetFirstElementAt(coords);
1153 // This might happen with damaged parks. Make sure there is *something* to avoid crashes.
1154 if (srcElement == nullptr)
1155 {
1156 auto& dstElement = tileElements.emplace_back();
1157 dstElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE);
1158 dstElement.SetLastForTile(true);
1159 continue;
1160 }
1161
1162 do
1163 {
1164 auto& dstElement = tileElements.emplace_back();
1165 if (srcElement->base_height == RCT12_MAX_ELEMENT_HEIGHT)
1166 {
1167 std::memcpy(&dstElement, srcElement, sizeof(*srcElement));
1168 }
1169 else
1170 {
1171 auto tileElementType = static_cast<RCT12TileElementType>(srcElement->GetType());
1172 // Todo: replace with setting invisibility bit
1173 if (tileElementType == RCT12TileElementType::Corrupt
1174 || tileElementType == RCT12TileElementType::EightCarsCorrupt14
1175 || tileElementType == RCT12TileElementType::EightCarsCorrupt15)
1176 std::memcpy(&dstElement, srcElement, sizeof(*srcElement));
1177 else
1178 ImportTileElement(&dstElement, srcElement);
1179 }
1180 } while (!(srcElement++)->IsLastForTile());
1181
1182 // Set last element flag in case the original last element was never added
1183 if (tileElements.size() > 0)
1184 {
1185 tileElements.back().SetLastForTile(true);
1186 }
1187 }
1188 }
1189 SetTileElements(std::move(tileElements));
1190 }
1191
ImportTileElement(TileElement * dst,const RCT12TileElement * src)1192 void ImportTileElement(TileElement* dst, const RCT12TileElement* src)
1193 {
1194 // Todo: allow for changing definition of OpenRCT2 tile element types - replace with a map
1195 uint8_t tileElementType = src->GetType();
1196 dst->ClearAs(tileElementType);
1197 dst->SetDirection(src->GetDirection());
1198 dst->SetBaseZ(src->base_height * COORDS_Z_STEP);
1199 dst->SetClearanceZ(src->clearance_height * COORDS_Z_STEP);
1200
1201 // All saved in "flags"
1202 dst->SetOccupiedQuadrants(src->GetOccupiedQuadrants());
1203 dst->SetGhost(src->IsGhost());
1204 dst->SetLastForTile(src->IsLastForTile());
1205
1206 switch (tileElementType)
1207 {
1208 case TILE_ELEMENT_TYPE_SURFACE:
1209 {
1210 auto dst2 = dst->AsSurface();
1211 auto src2 = src->AsSurface();
1212
1213 dst2->SetSlope(src2->GetSlope());
1214 dst2->SetSurfaceStyle(src2->GetSurfaceStyle());
1215 dst2->SetEdgeStyle(src2->GetEdgeStyle());
1216 dst2->SetGrassLength(src2->GetGrassLength());
1217 dst2->SetOwnership(src2->GetOwnership());
1218 dst2->SetParkFences(src2->GetParkFences());
1219 dst2->SetWaterHeight(src2->GetWaterHeight());
1220 dst2->SetHasTrackThatNeedsWater(src2->HasTrackThatNeedsWater());
1221
1222 break;
1223 }
1224 case TILE_ELEMENT_TYPE_PATH:
1225 {
1226 auto dst2 = dst->AsPath();
1227 auto src2 = src->AsPath();
1228
1229 dst2->SetLegacyPathEntryIndex(src2->GetEntryIndex());
1230 dst2->SetQueueBannerDirection(src2->GetQueueBannerDirection());
1231 dst2->SetSloped(src2->IsSloped());
1232 dst2->SetSlopeDirection(src2->GetSlopeDirection());
1233 dst2->SetRideIndex(RCT12RideIdToOpenRCT2RideId(src2->GetRideIndex()));
1234 dst2->SetStationIndex(src2->GetStationIndex());
1235 dst2->SetWide(src2->IsWide());
1236 dst2->SetIsQueue(src2->IsQueue());
1237 dst2->SetHasQueueBanner(src2->HasQueueBanner());
1238 dst2->SetEdges(src2->GetEdges());
1239 dst2->SetCorners(src2->GetCorners());
1240 dst2->SetAddition(src2->GetAddition());
1241 dst2->SetAdditionIsGhost(src2->AdditionIsGhost());
1242 dst2->SetAdditionStatus(src2->GetAdditionStatus());
1243 dst2->SetIsBroken(src2->IsBroken());
1244 dst2->SetIsBlockedByVehicle(src2->IsBlockedByVehicle());
1245
1246 break;
1247 }
1248 case TILE_ELEMENT_TYPE_TRACK:
1249 {
1250 auto dst2 = dst->AsTrack();
1251 auto src2 = src->AsTrack();
1252
1253 auto rideType = _s6.rides[src2->GetRideIndex()].type;
1254 track_type_t trackType = static_cast<track_type_t>(src2->GetTrackType());
1255
1256 dst2->SetTrackType(RCT2TrackTypeToOpenRCT2(trackType, rideType, IsFlatRide(src2->GetRideIndex())));
1257 dst2->SetRideType(rideType);
1258 dst2->SetSequenceIndex(src2->GetSequenceIndex());
1259 dst2->SetRideIndex(RCT12RideIdToOpenRCT2RideId(src2->GetRideIndex()));
1260 dst2->SetColourScheme(src2->GetColourScheme());
1261 dst2->SetHasChain(src2->HasChain());
1262 dst2->SetHasCableLift(src2->HasCableLift());
1263 dst2->SetInverted(src2->IsInverted());
1264 dst2->SetStationIndex(src2->GetStationIndex());
1265 dst2->SetHasGreenLight(src2->HasGreenLight());
1266 dst2->SetBlockBrakeClosed(src2->BlockBrakeClosed());
1267 dst2->SetIsIndestructible(src2->IsIndestructible());
1268 // Skipping IsHighlighted()
1269
1270 if (TrackTypeHasSpeedSetting(trackType))
1271 {
1272 dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed());
1273 }
1274 else if (trackType == TrackElemType::OnRidePhoto)
1275 {
1276 dst2->SetPhotoTimeout(src2->GetPhotoTimeout());
1277 }
1278
1279 // This has to be done last, since the maze entry shares fields with the colour and sequence fields.
1280 if (rideType == RIDE_TYPE_MAZE)
1281 {
1282 dst2->SetMazeEntry(src2->GetMazeEntry());
1283 }
1284 else if (rideType == RIDE_TYPE_GHOST_TRAIN)
1285 {
1286 dst2->SetDoorAState(src2->GetDoorAState());
1287 dst2->SetDoorBState(src2->GetDoorBState());
1288 }
1289 else
1290 {
1291 dst2->SetSeatRotation(src2->GetSeatRotation());
1292 }
1293
1294 break;
1295 }
1296 case TILE_ELEMENT_TYPE_SMALL_SCENERY:
1297 {
1298 auto dst2 = dst->AsSmallScenery();
1299 auto src2 = src->AsSmallScenery();
1300
1301 dst2->SetEntryIndex(src2->GetEntryIndex());
1302 dst2->SetAge(src2->GetAge());
1303 dst2->SetSceneryQuadrant(src2->GetSceneryQuadrant());
1304 dst2->SetPrimaryColour(src2->GetPrimaryColour());
1305 dst2->SetSecondaryColour(src2->GetSecondaryColour());
1306 if (src2->NeedsSupports())
1307 dst2->SetNeedsSupports();
1308
1309 break;
1310 }
1311 case TILE_ELEMENT_TYPE_ENTRANCE:
1312 {
1313 auto dst2 = dst->AsEntrance();
1314 auto src2 = src->AsEntrance();
1315
1316 dst2->SetEntranceType(src2->GetEntranceType());
1317 dst2->SetRideIndex(RCT12RideIdToOpenRCT2RideId(src2->GetRideIndex()));
1318 dst2->SetStationIndex(src2->GetStationIndex());
1319 dst2->SetSequenceIndex(src2->GetSequenceIndex());
1320 dst2->SetLegacyPathEntryIndex(src2->GetPathType());
1321
1322 break;
1323 }
1324 case TILE_ELEMENT_TYPE_WALL:
1325 {
1326 auto dst2 = dst->AsWall();
1327 auto src2 = src->AsWall();
1328
1329 dst2->SetEntryIndex(src2->GetEntryIndex());
1330 dst2->SetSlope(src2->GetSlope());
1331 dst2->SetPrimaryColour(src2->GetPrimaryColour());
1332 dst2->SetSecondaryColour(src2->GetSecondaryColour());
1333 dst2->SetTertiaryColour(src2->GetTertiaryColour());
1334 dst2->SetAnimationFrame(src2->GetAnimationFrame());
1335 dst2->SetAcrossTrack(src2->IsAcrossTrack());
1336 dst2->SetAnimationIsBackwards(src2->AnimationIsBackwards());
1337
1338 // Import banner information
1339 dst2->SetBannerIndex(BANNER_INDEX_NULL);
1340 auto entry = dst2->GetEntry();
1341 if (entry != nullptr && entry->scrolling_mode != SCROLLING_MODE_NONE)
1342 {
1343 auto bannerIndex = src2->GetBannerIndex();
1344 if (bannerIndex < std::size(_s6.banners))
1345 {
1346 auto srcBanner = &_s6.banners[bannerIndex];
1347 auto dstBanner = GetOrCreateBanner(bannerIndex);
1348 if (dstBanner == nullptr)
1349 {
1350 dst2->SetBannerIndex(BANNER_INDEX_NULL);
1351 }
1352 else
1353 {
1354 ImportBanner(dstBanner, srcBanner);
1355 dst2->SetBannerIndex(src2->GetBannerIndex());
1356 }
1357 }
1358 }
1359 break;
1360 }
1361 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1362 {
1363 auto dst2 = dst->AsLargeScenery();
1364 auto src2 = src->AsLargeScenery();
1365
1366 dst2->SetEntryIndex(src2->GetEntryIndex());
1367 dst2->SetSequenceIndex(src2->GetSequenceIndex());
1368 dst2->SetPrimaryColour(src2->GetPrimaryColour());
1369 dst2->SetSecondaryColour(src2->GetSecondaryColour());
1370
1371 // Import banner information
1372 dst2->SetBannerIndex(BANNER_INDEX_NULL);
1373 auto entry = dst2->GetEntry();
1374 if (entry != nullptr && entry->scrolling_mode != SCROLLING_MODE_NONE)
1375 {
1376 auto bannerIndex = src2->GetBannerIndex();
1377 if (bannerIndex < std::size(_s6.banners))
1378 {
1379 auto srcBanner = &_s6.banners[bannerIndex];
1380 auto dstBanner = GetOrCreateBanner(bannerIndex);
1381 if (dstBanner == nullptr)
1382 {
1383 dst2->SetBannerIndex(BANNER_INDEX_NULL);
1384 }
1385 else
1386 {
1387 ImportBanner(dstBanner, srcBanner);
1388 dst2->SetBannerIndex(src2->GetBannerIndex());
1389 }
1390 }
1391 }
1392 break;
1393 }
1394 case TILE_ELEMENT_TYPE_BANNER:
1395 {
1396 auto dst2 = dst->AsBanner();
1397 auto src2 = src->AsBanner();
1398
1399 dst2->SetPosition(src2->GetPosition());
1400 dst2->SetAllowedEdges(src2->GetAllowedEdges());
1401
1402 auto bannerIndex = src2->GetIndex();
1403 if (bannerIndex < std::size(_s6.banners))
1404 {
1405 auto srcBanner = &_s6.banners[bannerIndex];
1406 auto dstBanner = GetOrCreateBanner(bannerIndex);
1407 if (dstBanner == nullptr)
1408 {
1409 dst2->SetIndex(BANNER_INDEX_NULL);
1410 }
1411 else
1412 {
1413 ImportBanner(dstBanner, srcBanner);
1414 dst2->SetIndex(bannerIndex);
1415 }
1416 }
1417 else
1418 {
1419 dst2->SetIndex(BANNER_INDEX_NULL);
1420 }
1421 break;
1422 }
1423 default:
1424 assert(false);
1425 }
1426 }
1427
ImportMarketingCampaigns()1428 void ImportMarketingCampaigns()
1429 {
1430 for (size_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
1431 {
1432 if (_s6.campaign_weeks_left[i] & CAMPAIGN_ACTIVE_FLAG)
1433 {
1434 MarketingCampaign campaign{};
1435 campaign.Type = static_cast<uint8_t>(i);
1436 campaign.WeeksLeft = _s6.campaign_weeks_left[i] & ~(CAMPAIGN_ACTIVE_FLAG | CAMPAIGN_FIRST_WEEK_FLAG);
1437 if ((_s6.campaign_weeks_left[i] & CAMPAIGN_FIRST_WEEK_FLAG) != 0)
1438 {
1439 campaign.Flags |= MarketingCampaignFlags::FIRST_WEEK;
1440 }
1441 if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE)
1442 {
1443 campaign.RideId = RCT12RideIdToOpenRCT2RideId(_s6.campaign_ride_index[i]);
1444 }
1445 else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
1446 {
1447 campaign.ShopItemType = ShopItem(_s6.campaign_ride_index[i]);
1448 }
1449 gMarketingCampaigns.push_back(campaign);
1450 }
1451 }
1452 }
1453
ImportStaffPatrolArea(Staff * staffmember,uint8_t staffId)1454 void ImportStaffPatrolArea(Staff* staffmember, uint8_t staffId)
1455 {
1456 // First check staff mode as vanilla did not clean up patrol areas when switching from patrol to walk
1457 // without doing this we could accidentally add a patrol when it didn't exist.
1458 if (_s6.staff_modes[staffId] != RCT2StaffMode::Patrol)
1459 {
1460 return;
1461 }
1462 int32_t peepOffset = staffId * RCT12_PATROL_AREA_SIZE;
1463 for (int32_t i = 0; i < RCT12_PATROL_AREA_SIZE; i++)
1464 {
1465 if (_s6.patrol_areas[peepOffset + i] == 0)
1466 {
1467 // No patrol for this area
1468 continue;
1469 }
1470
1471 // Loop over the bits of the uint32_t
1472 for (int32_t j = 0; j < 32; j++)
1473 {
1474 int8_t bit = (_s6.patrol_areas[peepOffset + i] >> j) & 1;
1475 if (bit == 0)
1476 {
1477 // No patrol for this area
1478 continue;
1479 }
1480 // val contains the 6 highest bits of both the x and y coordinates
1481 int32_t val = j | (i << 5);
1482 int32_t x = val & 0x03F;
1483 x <<= 7;
1484 int32_t y = val & 0xFC0;
1485 y <<= 1;
1486 staffmember->SetPatrolArea({ x, y }, true);
1487 }
1488 }
1489 }
1490
ImportEntities()1491 void ImportEntities()
1492 {
1493 for (int32_t i = 0; i < RCT2_MAX_SPRITES; i++)
1494 {
1495 ImportEntity(_s6.sprites[i].unknown);
1496 }
1497 }
1498
1499 template<typename OpenRCT2_T> void ImportEntity(const RCT12SpriteBase& src);
1500
ImportEntityPeep(Peep * dst,const RCT2SpritePeep * src)1501 void ImportEntityPeep(Peep* dst, const RCT2SpritePeep* src)
1502 {
1503 const auto isNullLocation = [](const rct12_xyzd8& pos) {
1504 return pos.x == 0xFF && pos.y == 0xFF && pos.z == 0xFF && pos.direction == INVALID_DIRECTION;
1505 };
1506
1507 ImportEntityCommonProperties(static_cast<EntityBase*>(dst), src);
1508 if (is_user_string_id(src->name_string_idx))
1509 {
1510 dst->SetName(GetUserString(src->name_string_idx));
1511 }
1512 dst->NextLoc = { src->next_x, src->next_y, src->next_z * COORDS_Z_STEP };
1513 dst->NextFlags = src->next_flags;
1514 dst->State = static_cast<PeepState>(src->state);
1515 dst->SubState = src->sub_state;
1516 dst->SpriteType = static_cast<PeepSpriteType>(src->sprite_type);
1517 dst->TshirtColour = src->tshirt_colour;
1518 dst->TrousersColour = src->trousers_colour;
1519 dst->DestinationX = src->destination_x;
1520 dst->DestinationY = src->destination_y;
1521 dst->DestinationTolerance = src->destination_tolerance;
1522 dst->Var37 = src->var_37;
1523 dst->Energy = src->energy;
1524 dst->EnergyTarget = src->energy_target;
1525 dst->Mass = src->mass;
1526 dst->WindowInvalidateFlags = src->window_invalidate_flags;
1527 dst->CurrentRide = RCT12RideIdToOpenRCT2RideId(src->current_ride);
1528 dst->CurrentRideStation = src->current_ride_station;
1529 dst->CurrentTrain = src->current_train;
1530 dst->TimeToSitdown = src->time_to_sitdown;
1531 dst->SpecialSprite = src->special_sprite;
1532 dst->ActionSpriteType = static_cast<PeepActionSpriteType>(src->action_sprite_type);
1533 dst->NextActionSpriteType = static_cast<PeepActionSpriteType>(src->next_action_sprite_type);
1534 dst->ActionSpriteImageOffset = src->action_sprite_image_offset;
1535 dst->Action = static_cast<PeepActionType>(src->action);
1536 dst->ActionFrame = src->action_frame;
1537 dst->StepProgress = src->step_progress;
1538 dst->PeepDirection = src->direction;
1539 dst->InteractionRideIndex = RCT12RideIdToOpenRCT2RideId(src->interaction_ride_index);
1540 dst->Id = src->id;
1541 dst->PathCheckOptimisation = src->path_check_optimisation;
1542 dst->PeepFlags = src->peep_flags;
1543 if (isNullLocation(src->pathfind_goal))
1544 {
1545 dst->PathfindGoal.SetNull();
1546 dst->PathfindGoal.direction = INVALID_DIRECTION;
1547 }
1548 else
1549 {
1550 dst->PathfindGoal = { src->pathfind_goal.x, src->pathfind_goal.y, src->pathfind_goal.z,
1551 src->pathfind_goal.direction };
1552 }
1553 for (size_t i = 0; i < std::size(src->pathfind_history); i++)
1554 {
1555 if (isNullLocation(src->pathfind_history[i]))
1556 {
1557 dst->PathfindHistory[i].SetNull();
1558 dst->PathfindHistory[i].direction = INVALID_DIRECTION;
1559 }
1560 else
1561 {
1562 dst->PathfindHistory[i] = { src->pathfind_history[i].x, src->pathfind_history[i].y, src->pathfind_history[i].z,
1563 src->pathfind_history[i].direction };
1564 }
1565 }
1566 dst->WalkingFrameNum = src->no_action_frame_num;
1567 }
1568
GetEntityTypeFromRCT2Sprite(const RCT12SpriteBase * src)1569 constexpr EntityType GetEntityTypeFromRCT2Sprite(const RCT12SpriteBase* src)
1570 {
1571 EntityType output = EntityType::Null;
1572 switch (src->sprite_identifier)
1573 {
1574 case RCT12SpriteIdentifier::Vehicle:
1575 output = EntityType::Vehicle;
1576 break;
1577 case RCT12SpriteIdentifier::Peep:
1578 if (RCT12PeepType(static_cast<const RCT2SpritePeep*>(src)->peep_type) == RCT12PeepType::Guest)
1579 {
1580 output = EntityType::Guest;
1581 }
1582 else
1583 {
1584 output = EntityType::Staff;
1585 }
1586 break;
1587 case RCT12SpriteIdentifier::Misc:
1588
1589 switch (RCT12MiscEntityType(src->type))
1590 {
1591 case RCT12MiscEntityType::SteamParticle:
1592 output = EntityType::SteamParticle;
1593 break;
1594 case RCT12MiscEntityType::MoneyEffect:
1595 output = EntityType::MoneyEffect;
1596 break;
1597 case RCT12MiscEntityType::CrashedVehicleParticle:
1598 output = EntityType::CrashedVehicleParticle;
1599 break;
1600 case RCT12MiscEntityType::ExplosionCloud:
1601 output = EntityType::ExplosionCloud;
1602 break;
1603 case RCT12MiscEntityType::CrashSplash:
1604 output = EntityType::CrashSplash;
1605 break;
1606 case RCT12MiscEntityType::ExplosionFlare:
1607 output = EntityType::ExplosionFlare;
1608 break;
1609 case RCT12MiscEntityType::JumpingFountainWater:
1610 case RCT12MiscEntityType::JumpingFountainSnow:
1611 output = EntityType::JumpingFountain;
1612 break;
1613 case RCT12MiscEntityType::Balloon:
1614 output = EntityType::Balloon;
1615 break;
1616 case RCT12MiscEntityType::Duck:
1617 output = EntityType::Duck;
1618 break;
1619 default:
1620 break;
1621 }
1622 break;
1623 case RCT12SpriteIdentifier::Litter:
1624 output = EntityType::Litter;
1625 break;
1626 default:
1627 break;
1628 }
1629 return output;
1630 }
1631
ImportEntityCommonProperties(EntityBase * dst,const RCT12SpriteBase * src)1632 void ImportEntityCommonProperties(EntityBase* dst, const RCT12SpriteBase* src)
1633 {
1634 dst->Type = GetEntityTypeFromRCT2Sprite(src);
1635 dst->sprite_height_negative = src->sprite_height_negative;
1636 dst->sprite_index = src->sprite_index;
1637 dst->x = src->x;
1638 dst->y = src->y;
1639 dst->z = src->z;
1640 dst->sprite_width = src->sprite_width;
1641 dst->sprite_height_positive = src->sprite_height_positive;
1642 dst->SpriteRect = ScreenRect(src->sprite_left, src->sprite_top, src->sprite_right, src->sprite_bottom);
1643 dst->sprite_direction = src->sprite_direction;
1644 }
1645
1646 void ImportEntity(const RCT12SpriteBase& src);
1647
GetUserString(rct_string_id stringId)1648 std::string GetUserString(rct_string_id stringId)
1649 {
1650 const auto originalString = _s6.custom_strings[(stringId - USER_STRING_START) % 1024];
1651 auto originalStringView = std::string_view(
1652 originalString, GetRCT2StringBufferLen(originalString, USER_STRING_MAX_LENGTH));
1653 auto asUtf8 = rct2_to_utf8(originalStringView, RCT2LanguageId::EnglishUK);
1654 auto justText = RCT12RemoveFormattingUTF8(asUtf8);
1655 return justText.data();
1656 }
1657
1658 template<size_t TInternalLimit, typename T>
AddRequiredObjects(std::vector<rct_object_entry> & required,const T & list)1659 static void AddRequiredObjects(std::vector<rct_object_entry>& required, const T& list)
1660 {
1661 rct_object_entry nullEntry = {};
1662 std::memset(&nullEntry, 0xFF, sizeof(nullEntry));
1663
1664 for (const auto& entry : list)
1665 {
1666 required.push_back(entry);
1667 }
1668
1669 // NOTE: The segment of this object type needs to be filled to the internal limit
1670 // the object manager currently expects this.
1671 for (size_t i = std::size(list); i < TInternalLimit; i++)
1672 {
1673 required.push_back(nullEntry);
1674 }
1675 }
1676
GetRequiredObjects()1677 ObjectList GetRequiredObjects()
1678 {
1679 std::vector<rct_object_entry> result;
1680
1681 AddRequiredObjects<MAX_RIDE_OBJECTS>(result, _s6.RideObjects);
1682 AddRequiredObjects<MAX_SMALL_SCENERY_OBJECTS>(result, _s6.SceneryObjects);
1683 AddRequiredObjects<MAX_LARGE_SCENERY_OBJECTS>(result, _s6.LargeSceneryObjects);
1684 AddRequiredObjects<MAX_WALL_SCENERY_OBJECTS>(result, _s6.WallSceneryObjects);
1685 AddRequiredObjects<MAX_BANNER_OBJECTS>(result, _s6.BannerObjects);
1686 AddRequiredObjects<MAX_PATH_OBJECTS>(result, _s6.PathObjects);
1687 AddRequiredObjects<MAX_PATH_ADDITION_OBJECTS>(result, _s6.PathAdditionObjects);
1688 AddRequiredObjects<MAX_SCENERY_GROUP_OBJECTS>(result, _s6.SceneryGroupObjects);
1689 AddRequiredObjects<MAX_PARK_ENTRANCE_OBJECTS>(result, _s6.ParkEntranceObjects);
1690 AddRequiredObjects<MAX_WATER_OBJECTS>(result, _s6.WaterObjects);
1691 AddRequiredObjects<MAX_SCENARIO_TEXT_OBJECTS>(result, _s6.ScenarioTextObjects);
1692
1693 ObjectList objectList;
1694 for (size_t i = 0; i < result.size(); i++)
1695 {
1696 ObjectType objectType;
1697 ObjectEntryIndex entryIndex;
1698 get_type_entry_index(i, &objectType, &entryIndex);
1699
1700 auto desc = ObjectEntryDescriptor(result[i]);
1701 if (desc.HasValue())
1702 {
1703 assert(desc.GetType() == objectType);
1704 objectList.SetObject(entryIndex, desc);
1705 }
1706 }
1707
1708 return objectList;
1709 }
1710 };
1711
ImportEntity(const RCT12SpriteBase & baseSrc)1712 template<> void S6Importer::ImportEntity<Vehicle>(const RCT12SpriteBase& baseSrc)
1713 {
1714 auto dst = CreateEntityAt<Vehicle>(baseSrc.sprite_index);
1715 auto src = static_cast<const RCT2SpriteVehicle*>(&baseSrc);
1716 const auto& ride = _s6.rides[src->ride];
1717
1718 ImportEntityCommonProperties(dst, src);
1719 dst->SubType = Vehicle::Type(src->type);
1720 dst->Pitch = src->Pitch;
1721 dst->bank_rotation = src->bank_rotation;
1722 dst->remaining_distance = src->remaining_distance;
1723 dst->velocity = src->velocity;
1724 dst->acceleration = src->acceleration;
1725 dst->ride = static_cast<ride_id_t>(src->ride);
1726 dst->vehicle_type = src->vehicle_type;
1727 dst->colours = src->colours;
1728 dst->track_progress = src->track_progress;
1729 dst->TrackLocation = { src->track_x, src->track_y, src->track_z };
1730 if (src->boat_location.IsNull() || static_cast<RideMode>(ride.mode) != RideMode::BoatHire
1731 || src->status != static_cast<uint8_t>(Vehicle::Status::TravellingBoat))
1732 {
1733 dst->BoatLocation.SetNull();
1734 dst->SetTrackDirection(src->GetTrackDirection());
1735 dst->SetTrackType(src->GetTrackType());
1736 // RotationControlToggle and Booster are saved as the same track piece ID
1737 // Which one the vehicle is using must be determined
1738 if (IsFlatRide(src->ride))
1739 {
1740 dst->SetTrackType(RCT12FlatTrackTypeToOpenRCT2(src->GetTrackType()));
1741 }
1742 else if (src->GetTrackType() == TrackElemType::RotationControlToggleAlias)
1743 {
1744 // Merging hacks mean the track type that's appropriate for the ride type is not necessarily the track type the
1745 // ride is on. It's possible to create unwanted behavior if a user layers spinning control track on top of
1746 // booster track but this is unlikely since only two rides have spinning control track - by default they load as
1747 // booster
1748 TileElement* tileElement2 = map_get_track_element_at_of_type_seq(
1749 dst->TrackLocation, TrackElemType::RotationControlToggle, 0);
1750
1751 if (tileElement2 != nullptr)
1752 dst->SetTrackType(TrackElemType::RotationControlToggle);
1753 }
1754 }
1755 else
1756 {
1757 dst->BoatLocation = TileCoordsXY{ src->boat_location.x, src->boat_location.y }.ToCoordsXY();
1758 dst->SetTrackDirection(0);
1759 dst->SetTrackType(0);
1760 }
1761
1762 dst->next_vehicle_on_train = src->next_vehicle_on_train;
1763 dst->prev_vehicle_on_ride = src->prev_vehicle_on_ride;
1764 dst->next_vehicle_on_ride = src->next_vehicle_on_ride;
1765 dst->var_44 = src->var_44;
1766 dst->mass = src->mass;
1767 dst->update_flags = src->update_flags;
1768 dst->SwingSprite = src->SwingSprite;
1769 dst->current_station = src->current_station;
1770 dst->current_time = src->current_time;
1771 dst->crash_z = src->crash_z;
1772
1773 Vehicle::Status statusSrc = Vehicle::Status::MovingToEndOfStation;
1774 if (src->status <= static_cast<uint8_t>(Vehicle::Status::StoppedByBlockBrakes))
1775 {
1776 statusSrc = static_cast<Vehicle::Status>(src->status);
1777 }
1778
1779 dst->status = statusSrc;
1780 dst->sub_state = src->sub_state;
1781 for (size_t i = 0; i < std::size(src->peep); i++)
1782 {
1783 dst->peep[i] = src->peep[i];
1784 dst->peep_tshirt_colours[i] = src->peep_tshirt_colours[i];
1785 }
1786 dst->num_seats = src->num_seats;
1787 dst->num_peeps = src->num_peeps;
1788 dst->next_free_seat = src->next_free_seat;
1789 dst->restraints_position = src->restraints_position;
1790 dst->crash_x = src->crash_x;
1791 dst->sound2_flags = src->sound2_flags;
1792 dst->spin_sprite = src->spin_sprite;
1793 dst->sound1_id = static_cast<OpenRCT2::Audio::SoundId>(src->sound1_id);
1794 dst->sound1_volume = src->sound1_volume;
1795 dst->sound2_id = static_cast<OpenRCT2::Audio::SoundId>(src->sound2_id);
1796 dst->sound2_volume = src->sound2_volume;
1797 dst->sound_vector_factor = src->sound_vector_factor;
1798 dst->time_waiting = src->time_waiting;
1799 dst->speed = src->speed;
1800 dst->powered_acceleration = src->powered_acceleration;
1801 dst->dodgems_collision_direction = src->dodgems_collision_direction;
1802 dst->animation_frame = src->animation_frame;
1803 dst->animationState = src->animationState;
1804 dst->scream_sound_id = static_cast<OpenRCT2::Audio::SoundId>(src->scream_sound_id);
1805 dst->TrackSubposition = VehicleTrackSubposition{ src->TrackSubposition };
1806 dst->var_CE = src->var_CE;
1807 dst->var_CF = src->var_CF;
1808 dst->lost_time_out = src->lost_time_out;
1809 dst->vertical_drop_countdown = src->vertical_drop_countdown;
1810 dst->var_D3 = src->var_D3;
1811 dst->mini_golf_current_animation = MiniGolfAnimation(src->mini_golf_current_animation);
1812 dst->mini_golf_flags = src->mini_golf_flags;
1813 dst->ride_subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->ride_subtype);
1814 dst->colours_extended = src->colours_extended;
1815 dst->seat_rotation = src->seat_rotation;
1816 dst->target_seat_rotation = src->target_seat_rotation;
1817 dst->IsCrashedVehicle = src->flags & RCT12_SPRITE_FLAGS_IS_CRASHED_VEHICLE_SPRITE;
1818 }
1819
AdjustScenarioToCurrentTicks(const rct_s6_data & s6,uint32_t tick)1820 static uint32_t AdjustScenarioToCurrentTicks(const rct_s6_data& s6, uint32_t tick)
1821 {
1822 // Previously gScenarioTicks was used as a time point, now it's gCurrentTicks.
1823 // gCurrentTicks and gScenarioTicks are now exported as the same, older saves that have a different
1824 // scenario tick must account for the difference between the two.
1825 uint32_t ticksElapsed = s6.scenario_ticks - tick;
1826 return s6.game_ticks_1 - ticksElapsed;
1827 }
1828
ImportEntity(const RCT12SpriteBase & baseSrc)1829 template<> void S6Importer::ImportEntity<Guest>(const RCT12SpriteBase& baseSrc)
1830 {
1831 auto dst = CreateEntityAt<Guest>(baseSrc.sprite_index);
1832 auto src = static_cast<const RCT2SpritePeep*>(&baseSrc);
1833 ImportEntityPeep(dst, src);
1834
1835 dst->OutsideOfPark = static_cast<bool>(src->outside_of_park);
1836 dst->GuestNumRides = src->no_of_rides;
1837 dst->Happiness = src->happiness;
1838 dst->HappinessTarget = src->happiness_target;
1839 dst->Nausea = src->nausea;
1840 dst->NauseaTarget = src->nausea_target;
1841 dst->Hunger = src->hunger;
1842 dst->Thirst = src->thirst;
1843 dst->Toilet = src->toilet;
1844 dst->TimeToConsume = src->time_to_consume;
1845 dst->Intensity = static_cast<IntensityRange>(src->intensity);
1846 dst->NauseaTolerance = static_cast<PeepNauseaTolerance>(src->nausea_tolerance);
1847 dst->PaidOnDrink = src->paid_on_drink;
1848
1849 OpenRCT2::RideUse::GetHistory().Set(dst->sprite_index, RCT12GetRidesBeenOn(src));
1850 OpenRCT2::RideUse::GetTypeHistory().Set(dst->sprite_index, RCT12GetRideTypesBeenOn(src));
1851
1852 dst->SetItemFlags(src->GetItemFlags());
1853 dst->Photo1RideRef = RCT12RideIdToOpenRCT2RideId(src->photo1_ride_ref);
1854 dst->Photo2RideRef = RCT12RideIdToOpenRCT2RideId(src->photo2_ride_ref);
1855 dst->Photo3RideRef = RCT12RideIdToOpenRCT2RideId(src->photo3_ride_ref);
1856 dst->Photo4RideRef = RCT12RideIdToOpenRCT2RideId(src->photo4_ride_ref);
1857 dst->GuestNextInQueue = src->next_in_queue;
1858 dst->TimeInQueue = src->time_in_queue;
1859 dst->CashInPocket = src->cash_in_pocket;
1860 dst->CashSpent = src->cash_spent;
1861 dst->ParkEntryTime = AdjustScenarioToCurrentTicks(_s6, src->park_entry_time);
1862 dst->RejoinQueueTimeout = src->rejoin_queue_timeout;
1863 dst->PreviousRide = RCT12RideIdToOpenRCT2RideId(src->previous_ride);
1864 dst->PreviousRideTimeOut = src->previous_ride_time_out;
1865 for (size_t i = 0; i < std::size(src->thoughts); i++)
1866 {
1867 auto srcThought = &src->thoughts[i];
1868 auto dstThought = &dst->Thoughts[i];
1869 dstThought->type = static_cast<PeepThoughtType>(srcThought->type);
1870 if (srcThought->item == RCT12PeepThoughtItemNone)
1871 dstThought->item = PeepThoughtItemNone;
1872 else
1873 dstThought->item = srcThought->item;
1874 dstThought->freshness = srcThought->freshness;
1875 dstThought->fresh_timeout = srcThought->fresh_timeout;
1876 }
1877 dst->GuestHeadingToRideId = RCT12RideIdToOpenRCT2RideId(src->guest_heading_to_ride_id);
1878 dst->GuestIsLostCountdown = src->peep_is_lost_countdown;
1879 dst->LitterCount = src->litter_count;
1880 dst->GuestTimeOnRide = src->time_on_ride;
1881 dst->DisgustingCount = src->disgusting_count;
1882 dst->PaidToEnter = src->paid_to_enter;
1883 dst->PaidOnRides = src->paid_on_rides;
1884 dst->PaidOnFood = src->paid_on_food;
1885 dst->PaidOnSouvenirs = src->paid_on_souvenirs;
1886 dst->AmountOfFood = src->no_of_food;
1887 dst->AmountOfDrinks = src->no_of_drinks;
1888 dst->AmountOfSouvenirs = src->no_of_souvenirs;
1889 dst->VandalismSeen = src->vandalism_seen;
1890 dst->VoucherType = src->voucher_type;
1891 dst->VoucherRideId = RCT12RideIdToOpenRCT2RideId(src->voucher_arguments);
1892 dst->SurroundingsThoughtTimeout = src->surroundings_thought_timeout;
1893 dst->Angriness = src->angriness;
1894 dst->TimeLost = src->time_lost;
1895 dst->DaysInQueue = src->days_in_queue;
1896 dst->BalloonColour = src->balloon_colour;
1897 dst->UmbrellaColour = src->umbrella_colour;
1898 dst->HatColour = src->hat_colour;
1899 dst->FavouriteRide = RCT12RideIdToOpenRCT2RideId(src->favourite_ride);
1900 dst->FavouriteRideRating = src->favourite_ride_rating;
1901 }
1902
ImportEntity(const RCT12SpriteBase & baseSrc)1903 template<> void S6Importer::ImportEntity<Staff>(const RCT12SpriteBase& baseSrc)
1904 {
1905 auto dst = CreateEntityAt<Staff>(baseSrc.sprite_index);
1906 auto src = static_cast<const RCT2SpritePeep*>(&baseSrc);
1907 ImportEntityPeep(dst, src);
1908
1909 dst->AssignedStaffType = StaffType(src->staff_type);
1910 dst->MechanicTimeSinceCall = src->mechanic_time_since_call;
1911
1912 dst->HireDate = src->park_entry_time;
1913 dst->StaffOrders = src->staff_orders;
1914 dst->StaffMowingTimeout = src->staff_mowing_timeout;
1915 dst->StaffLawnsMown = src->paid_to_enter;
1916 dst->StaffGardensWatered = src->paid_on_rides;
1917 dst->StaffLitterSwept = src->paid_on_food;
1918 dst->StaffBinsEmptied = src->paid_on_souvenirs;
1919
1920 ImportStaffPatrolArea(dst, src->staff_id);
1921 }
1922
ImportEntity(const RCT12SpriteBase & baseSrc)1923 template<> void S6Importer::ImportEntity<SteamParticle>(const RCT12SpriteBase& baseSrc)
1924 {
1925 auto dst = CreateEntityAt<SteamParticle>(baseSrc.sprite_index);
1926 auto src = static_cast<const RCT12SpriteSteamParticle*>(&baseSrc);
1927 ImportEntityCommonProperties(dst, src);
1928 dst->time_to_move = src->time_to_move;
1929 dst->frame = src->frame;
1930 }
1931
ImportEntity(const RCT12SpriteBase & baseSrc)1932 template<> void S6Importer::ImportEntity<MoneyEffect>(const RCT12SpriteBase& baseSrc)
1933 {
1934 auto dst = CreateEntityAt<MoneyEffect>(baseSrc.sprite_index);
1935 auto src = static_cast<const RCT12SpriteMoneyEffect*>(&baseSrc);
1936 ImportEntityCommonProperties(dst, src);
1937 dst->MoveDelay = src->move_delay;
1938 dst->NumMovements = src->num_movements;
1939 dst->Vertical = src->vertical;
1940 dst->Value = src->value;
1941 dst->OffsetX = src->offset_x;
1942 dst->Wiggle = src->wiggle;
1943 }
1944
ImportEntity(const RCT12SpriteBase & baseSrc)1945 template<> void S6Importer::ImportEntity<VehicleCrashParticle>(const RCT12SpriteBase& baseSrc)
1946 {
1947 auto dst = CreateEntityAt<VehicleCrashParticle>(baseSrc.sprite_index);
1948 auto src = static_cast<const RCT12SpriteCrashedVehicleParticle*>(&baseSrc);
1949 ImportEntityCommonProperties(dst, src);
1950 dst->frame = src->frame;
1951 dst->time_to_live = src->time_to_live;
1952 dst->frame = src->frame;
1953 dst->colour[0] = src->colour[0];
1954 dst->colour[1] = src->colour[1];
1955 dst->crashed_sprite_base = src->crashed_sprite_base;
1956 dst->velocity_x = src->velocity_x;
1957 dst->velocity_y = src->velocity_y;
1958 dst->velocity_z = src->velocity_z;
1959 dst->acceleration_x = src->acceleration_x;
1960 dst->acceleration_y = src->acceleration_y;
1961 dst->acceleration_z = src->acceleration_z;
1962 }
1963
ImportEntity(const RCT12SpriteBase & baseSrc)1964 template<> void S6Importer::ImportEntity<ExplosionCloud>(const RCT12SpriteBase& baseSrc)
1965 {
1966 auto dst = CreateEntityAt<ExplosionCloud>(baseSrc.sprite_index);
1967 auto src = static_cast<const RCT12SpriteParticle*>(&baseSrc);
1968 ImportEntityCommonProperties(dst, src);
1969 dst->frame = src->frame;
1970 }
1971
ImportEntity(const RCT12SpriteBase & baseSrc)1972 template<> void S6Importer::ImportEntity<ExplosionFlare>(const RCT12SpriteBase& baseSrc)
1973 {
1974 auto dst = CreateEntityAt<ExplosionFlare>(baseSrc.sprite_index);
1975 auto src = static_cast<const RCT12SpriteParticle*>(&baseSrc);
1976 ImportEntityCommonProperties(dst, src);
1977 dst->frame = src->frame;
1978 }
1979
ImportEntity(const RCT12SpriteBase & baseSrc)1980 template<> void S6Importer::ImportEntity<CrashSplashParticle>(const RCT12SpriteBase& baseSrc)
1981 {
1982 auto dst = CreateEntityAt<CrashSplashParticle>(baseSrc.sprite_index);
1983 auto src = static_cast<const RCT12SpriteParticle*>(&baseSrc);
1984 ImportEntityCommonProperties(dst, src);
1985 dst->frame = src->frame;
1986 }
1987
ImportEntity(const RCT12SpriteBase & baseSrc)1988 template<> void S6Importer::ImportEntity<JumpingFountain>(const RCT12SpriteBase& baseSrc)
1989 {
1990 auto dst = CreateEntityAt<JumpingFountain>(baseSrc.sprite_index);
1991 auto src = static_cast<const RCT12SpriteJumpingFountain*>(&baseSrc);
1992 ImportEntityCommonProperties(dst, src);
1993 dst->NumTicksAlive = src->num_ticks_alive;
1994 dst->frame = src->frame;
1995 dst->FountainFlags = src->fountain_flags;
1996 dst->TargetX = src->target_x;
1997 dst->TargetY = src->target_y;
1998 dst->Iteration = src->iteration;
1999 dst->FountainType = RCT12MiscEntityType(src->type) == RCT12MiscEntityType::JumpingFountainSnow ? JumpingFountainType::Snow
2000 : JumpingFountainType::Water;
2001 }
2002
ImportEntity(const RCT12SpriteBase & baseSrc)2003 template<> void S6Importer::ImportEntity<Balloon>(const RCT12SpriteBase& baseSrc)
2004 {
2005 auto dst = CreateEntityAt<Balloon>(baseSrc.sprite_index);
2006 auto src = static_cast<const RCT12SpriteBalloon*>(&baseSrc);
2007 ImportEntityCommonProperties(dst, src);
2008 dst->popped = src->popped;
2009 dst->time_to_move = src->time_to_move;
2010 dst->frame = src->frame;
2011 dst->colour = src->colour;
2012 }
2013
ImportEntity(const RCT12SpriteBase & baseSrc)2014 template<> void S6Importer::ImportEntity<Duck>(const RCT12SpriteBase& baseSrc)
2015 {
2016 auto dst = CreateEntityAt<Duck>(baseSrc.sprite_index);
2017 auto src = static_cast<const RCT12SpriteDuck*>(&baseSrc);
2018 ImportEntityCommonProperties(dst, src);
2019 dst->frame = src->frame;
2020 dst->target_x = src->target_x;
2021 dst->target_y = src->target_y;
2022 dst->state = static_cast<Duck::DuckState>(src->state);
2023 }
2024
ImportEntity(const RCT12SpriteBase & baseSrc)2025 template<> void S6Importer::ImportEntity<Litter>(const RCT12SpriteBase& baseSrc)
2026 {
2027 auto dst = CreateEntityAt<Litter>(baseSrc.sprite_index);
2028 auto src = static_cast<const RCT12SpriteLitter*>(&baseSrc);
2029 ImportEntityCommonProperties(dst, src);
2030 dst->SubType = Litter::Type(src->type);
2031 dst->creationTick = AdjustScenarioToCurrentTicks(_s6, src->creationTick);
2032 }
2033
ImportEntity(const RCT12SpriteBase & src)2034 void S6Importer::ImportEntity(const RCT12SpriteBase& src)
2035 {
2036 switch (GetEntityTypeFromRCT2Sprite(&src))
2037 {
2038 case EntityType::Vehicle:
2039 ImportEntity<Vehicle>(src);
2040 break;
2041 case EntityType::Guest:
2042 ImportEntity<Guest>(src);
2043 break;
2044 case EntityType::Staff:
2045 ImportEntity<Staff>(src);
2046 break;
2047 case EntityType::SteamParticle:
2048 ImportEntity<SteamParticle>(src);
2049 break;
2050 case EntityType::MoneyEffect:
2051 ImportEntity<MoneyEffect>(src);
2052 break;
2053 case EntityType::CrashedVehicleParticle:
2054 ImportEntity<VehicleCrashParticle>(src);
2055 break;
2056 case EntityType::ExplosionCloud:
2057 ImportEntity<ExplosionCloud>(src);
2058 break;
2059 case EntityType::ExplosionFlare:
2060 ImportEntity<ExplosionFlare>(src);
2061 break;
2062 case EntityType::CrashSplash:
2063 ImportEntity<CrashSplashParticle>(src);
2064 break;
2065 case EntityType::JumpingFountain:
2066 ImportEntity<JumpingFountain>(src);
2067 break;
2068 case EntityType::Balloon:
2069 ImportEntity<Balloon>(src);
2070 break;
2071 case EntityType::Duck:
2072 ImportEntity<Duck>(src);
2073 break;
2074 case EntityType::Litter:
2075 ImportEntity<Litter>(src);
2076 break;
2077 default:
2078 // Null elements do not need imported
2079 break;
2080 }
2081 }
2082
CreateS6(IObjectRepository & objectRepository)2083 std::unique_ptr<IParkImporter> ParkImporter::CreateS6(IObjectRepository& objectRepository)
2084 {
2085 return std::make_unique<S6Importer>(objectRepository);
2086 }
2087
show_error(uint8_t errorType,rct_string_id errorStringId)2088 static void show_error(uint8_t errorType, rct_string_id errorStringId)
2089 {
2090 if (errorType == ERROR_TYPE_GENERIC)
2091 {
2092 context_show_error(errorStringId, STR_NONE, {});
2093 }
2094 context_show_error(STR_UNABLE_TO_LOAD_FILE, errorStringId, {});
2095 }
2096
load_from_sv6(const char * path)2097 void load_from_sv6(const char* path)
2098 {
2099 auto context = OpenRCT2::GetContext();
2100 auto s6Importer = std::make_unique<S6Importer>(context->GetObjectRepository());
2101 try
2102 {
2103 auto& objectMgr = context->GetObjectManager();
2104 auto result = s6Importer->LoadSavedGame(path);
2105 objectMgr.LoadObjects(result.RequiredObjects);
2106 s6Importer->Import();
2107 game_fix_save_vars();
2108 AutoCreateMapAnimations();
2109 EntityTweener::Get().Reset();
2110 gScreenAge = 0;
2111 gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
2112 }
2113 catch (const ObjectLoadException&)
2114 {
2115 show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA);
2116 }
2117 catch (const IOException& loadError)
2118 {
2119 log_error("Error loading: %s", loadError.what());
2120 show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED);
2121 }
2122 catch (const UnsupportedRideTypeException&)
2123 {
2124 show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_UNSUPPORTED_RIDE_TYPES);
2125 }
2126 catch (const std::exception&)
2127 {
2128 show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA);
2129 }
2130 }
2131
2132 /**
2133 *
2134 * rct2: 0x00676053
2135 * scenario (ebx)
2136 */
load_from_sc6(const char * path)2137 void load_from_sc6(const char* path)
2138 {
2139 auto context = OpenRCT2::GetContext();
2140 auto& objManager = context->GetObjectManager();
2141 auto s6Importer = std::make_unique<S6Importer>(context->GetObjectRepository());
2142 try
2143 {
2144 auto result = s6Importer->LoadScenario(path);
2145 objManager.LoadObjects(result.RequiredObjects);
2146 s6Importer->Import();
2147 game_fix_save_vars();
2148 AutoCreateMapAnimations();
2149 EntityTweener::Get().Reset();
2150 return;
2151 }
2152 catch (const ObjectLoadException& loadError)
2153 {
2154 log_error("Error loading: %s", loadError.what());
2155 show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED);
2156 }
2157 catch (const IOException& loadError)
2158 {
2159 log_error("Error loading: %s", loadError.what());
2160 show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED);
2161 }
2162 catch (const std::exception&)
2163 {
2164 show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA);
2165 }
2166 gScreenAge = 0;
2167 gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
2168 }
2169