2014-04-02 01:31:55 +02:00
|
|
|
/*****************************************************************************
|
2024-01-01 12:52:28 +01:00
|
|
|
* Copyright (c) 2014-2024 OpenRCT2 developers
|
2015-10-09 18:22:37 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
2015-10-09 18:22:37 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2014-04-02 01:31:55 +02:00
|
|
|
*****************************************************************************/
|
2015-10-09 18:22:37 +02:00
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "Game.h"
|
|
|
|
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "Cheats.h"
|
2017-03-26 22:42:07 +02:00
|
|
|
#include "Context.h"
|
2017-09-22 07:56:24 +02:00
|
|
|
#include "Editor.h"
|
2017-07-28 19:35:07 +02:00
|
|
|
#include "FileClassifier.h"
|
2023-12-28 00:29:06 +01:00
|
|
|
#include "GameState.h"
|
2019-05-11 21:31:34 +02:00
|
|
|
#include "GameStateSnapshots.h"
|
2017-12-12 14:52:57 +01:00
|
|
|
#include "Input.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "OpenRCT2.h"
|
|
|
|
#include "ParkImporter.h"
|
2020-03-31 23:12:35 +02:00
|
|
|
#include "PlatformEnvironment.h"
|
2018-12-06 06:34:27 +01:00
|
|
|
#include "ReplayManager.h"
|
2023-10-04 17:13:40 +02:00
|
|
|
#include "actions/GameSetSpeedAction.h"
|
Split actions hpp files into separate h and cpp files (#13548)
* Split up SmallSceneryPlace/Remove
Added undo function for Remove Scenery
* Refactor: Balloon and Banner actions hpp=>h/cpp
* Refactor: rename all action *.hpp files to *.cpp
This is preparation for separation in later commits. Note that without
the complete set of commits in this branch, the code will not build.
* Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp
* VSCode: add src subdirectories to includePath
* Refactor Guest actions hpp=>h/cpp
* Refactor Land actions hpp=>h/cpp
* Refactor LargeScenery actions hpp=>h/cpp
* Refactor Load, Maze, Network actions hpp=>h/cpp
* Refactor Park actions hpp=>h/cpp
* Refactor/style: move private function declarations in actions *.h
Previous action .h files included private function declarations with
private member variables, before public function declarations. This
commit re-orders the header files to the following order:
- public member variables
- private member variables
- public functions
- private functions
* Refactor Pause action hpp=>h/cpp
* Refactor Peep, Place, Player actions hpp=>h/cpp
* Refactor Ride actions hpp=>h/cpp
* Refactor Scenario, Set*, Sign* actions hpp=>h/cpp
* Refactor SmallScenerySetColourAction hpp=>h/cpp
* Refactor Staff actions hpp=>h/cpp
* Refactor Surface, Tile, Track* actions hpp=>h/cpp
* Refactor Wall and Water actions hpp=>h/cpp
* Fix various includes and other compile errors
Update includes for tests.
Move static function declarations to .h files
Add explicit includes to various files that were previously implicit
(the required header was a nested include in an action hpp file, and the
action .h file does not include that header)
Move RideSetStatus string enum to the cpp file to avoid unused imports
* Xcode: modify project file for actions refactor
* Cleanup whitespace and end-of-file newlines
Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
|
|
|
#include "actions/LoadOrQuitAction.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "audio/audio.h"
|
|
|
|
#include "config/Config.h"
|
2021-02-13 03:50:29 +01:00
|
|
|
#include "core/Console.hpp"
|
2022-01-08 13:57:29 +01:00
|
|
|
#include "core/File.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "core/FileScanner.h"
|
2020-03-31 23:12:35 +02:00
|
|
|
#include "core/Path.hpp"
|
2021-11-24 15:58:01 +01:00
|
|
|
#include "entity/EntityRegistry.h"
|
2022-03-08 01:14:52 +01:00
|
|
|
#include "entity/PatrolArea.h"
|
2021-11-25 22:47:24 +01:00
|
|
|
#include "entity/Peep.h"
|
|
|
|
#include "entity/Staff.h"
|
2020-05-28 23:37:50 +02:00
|
|
|
#include "interface/Colour.h"
|
2017-06-09 00:02:39 +02:00
|
|
|
#include "interface/Screenshot.h"
|
2018-01-06 00:45:53 +01:00
|
|
|
#include "interface/Viewport.h"
|
2018-01-06 01:05:16 +01:00
|
|
|
#include "interface/Window.h"
|
2018-01-06 18:32:25 +01:00
|
|
|
#include "localisation/Localisation.h"
|
2017-10-06 22:37:06 +02:00
|
|
|
#include "management/Finance.h"
|
2017-10-11 21:38:26 +02:00
|
|
|
#include "management/Marketing.h"
|
2017-10-05 16:22:37 +02:00
|
|
|
#include "management/Research.h"
|
2015-02-12 12:30:57 +01:00
|
|
|
#include "network/network.h"
|
2018-01-02 20:36:42 +01:00
|
|
|
#include "object/Object.h"
|
2023-02-15 22:35:16 +01:00
|
|
|
#include "object/ObjectEntryManager.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "object/ObjectList.h"
|
2023-01-27 20:44:58 +01:00
|
|
|
#include "object/WaterEntry.h"
|
2022-02-18 21:57:00 +01:00
|
|
|
#include "platform/Platform.h"
|
2017-12-31 13:21:34 +01:00
|
|
|
#include "ride/Ride.h"
|
2018-01-18 23:33:06 +01:00
|
|
|
#include "ride/RideRatings.h"
|
2018-02-09 23:49:50 +01:00
|
|
|
#include "ride/Station.h"
|
2017-10-17 13:51:47 +02:00
|
|
|
#include "ride/Track.h"
|
2017-09-01 13:38:21 +02:00
|
|
|
#include "ride/TrackDesign.h"
|
2017-10-11 23:32:15 +02:00
|
|
|
#include "ride/Vehicle.h"
|
2018-01-02 18:58:43 +01:00
|
|
|
#include "scenario/Scenario.h"
|
2024-04-23 21:31:05 +02:00
|
|
|
#include "scenes/title/TitleScene.h"
|
2020-02-07 00:15:20 +01:00
|
|
|
#include "scripting/ScriptEngine.h"
|
2018-06-02 01:07:14 +02:00
|
|
|
#include "ui/UiContext.h"
|
|
|
|
#include "ui/WindowManager.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "util/SawyerCoding.h"
|
|
|
|
#include "util/Util.h"
|
2017-10-19 10:01:05 +02:00
|
|
|
#include "windows/Intent.h"
|
2017-12-14 10:34:12 +01:00
|
|
|
#include "world/Banner.h"
|
2017-03-11 12:24:18 +01:00
|
|
|
#include "world/Climate.h"
|
2017-12-14 10:34:12 +01:00
|
|
|
#include "world/Entrance.h"
|
2018-01-11 10:59:26 +01:00
|
|
|
#include "world/Footpath.h"
|
2018-01-03 14:56:08 +01:00
|
|
|
#include "world/Map.h"
|
2018-01-11 10:59:26 +01:00
|
|
|
#include "world/MapAnimation.h"
|
2017-12-31 21:40:00 +01:00
|
|
|
#include "world/Park.h"
|
2018-01-11 10:59:26 +01:00
|
|
|
#include "world/Scenery.h"
|
2018-05-01 16:33:16 +02:00
|
|
|
#include "world/Surface.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
|
2018-08-12 13:50:40 +02:00
|
|
|
#include <algorithm>
|
2020-04-12 11:32:39 +02:00
|
|
|
#include <cstdio>
|
2018-11-21 23:16:04 +01:00
|
|
|
#include <iterator>
|
2018-06-22 23:25:16 +02:00
|
|
|
#include <memory>
|
2016-01-04 16:22:15 +01:00
|
|
|
|
2023-12-28 00:29:06 +01:00
|
|
|
using namespace OpenRCT2;
|
|
|
|
|
2018-12-01 16:36:00 +01:00
|
|
|
uint16_t gCurrentDeltaTime;
|
2018-06-22 23:25:16 +02:00
|
|
|
uint8_t gGamePaused = 0;
|
|
|
|
int32_t gGameSpeed = 1;
|
2018-09-23 23:40:47 +02:00
|
|
|
bool gDoSingleUpdate = false;
|
2018-06-22 23:25:16 +02:00
|
|
|
float gDayNightCycle = 0;
|
|
|
|
bool gInUpdateCode = false;
|
|
|
|
bool gInMapInitCode = false;
|
2018-12-15 19:39:00 +01:00
|
|
|
std::string gCurrentLoadedPath;
|
2022-09-25 08:33:28 +02:00
|
|
|
bool gIsAutosave = false;
|
|
|
|
bool gIsAutosaveLoaded = false;
|
2014-07-26 11:30:55 +02:00
|
|
|
|
2017-10-30 12:07:01 +01:00
|
|
|
bool gLoadKeepWindowsOpen = false;
|
|
|
|
|
2018-12-01 16:36:00 +01:00
|
|
|
uint32_t gCurrentRealTimeTicks;
|
2016-09-03 22:25:19 +02:00
|
|
|
|
2022-03-21 23:56:21 +01:00
|
|
|
#ifdef ENABLE_SCRIPTING
|
|
|
|
static bool _mapChangedExpected;
|
|
|
|
#endif
|
|
|
|
|
2017-07-23 01:19:09 +02:00
|
|
|
using namespace OpenRCT2;
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameResetSpeed()
|
2020-03-22 23:22:28 +01:00
|
|
|
{
|
2023-10-04 17:13:40 +02:00
|
|
|
auto setSpeedAction = GameSetSpeedAction(1);
|
|
|
|
GameActions::Execute(&setSpeedAction);
|
2020-03-22 23:22:28 +01:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameIncreaseGameSpeed()
|
2014-11-05 19:50:51 +01:00
|
|
|
{
|
2023-10-04 17:13:40 +02:00
|
|
|
auto newSpeed = std::min(gConfigGeneral.DebuggingTools ? 5 : 4, gGameSpeed + 1);
|
|
|
|
if (newSpeed == 5)
|
|
|
|
newSpeed = 8;
|
|
|
|
|
|
|
|
auto setSpeedAction = GameSetSpeedAction(newSpeed);
|
|
|
|
GameActions::Execute(&setSpeedAction);
|
2014-11-05 19:50:51 +01:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameReduceGameSpeed()
|
2014-11-05 19:50:51 +01:00
|
|
|
{
|
2023-10-04 17:13:40 +02:00
|
|
|
auto newSpeed = std::max(1, gGameSpeed - 1);
|
|
|
|
if (newSpeed == 7)
|
|
|
|
newSpeed = 4;
|
|
|
|
|
|
|
|
auto setSpeedAction = GameSetSpeedAction(newSpeed);
|
|
|
|
GameActions::Execute(&setSpeedAction);
|
2014-11-05 19:50:51 +01:00
|
|
|
}
|
|
|
|
|
2014-04-11 03:42:39 +02:00
|
|
|
/**
|
2015-10-09 18:22:37 +02:00
|
|
|
*
|
2014-04-11 03:42:39 +02:00
|
|
|
* rct2: 0x0066B5C0 (part of 0x0066B3E8)
|
|
|
|
*/
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameCreateWindows()
|
2014-04-11 03:42:39 +02:00
|
|
|
{
|
2022-11-06 21:49:07 +01:00
|
|
|
ContextOpenWindow(WindowClass::MainWindow);
|
|
|
|
ContextOpenWindow(WindowClass::TopToolbar);
|
|
|
|
ContextOpenWindow(WindowClass::BottomToolbar);
|
2023-01-16 21:13:42 +01:00
|
|
|
WindowResizeGui(ContextGetWidth(), ContextGetHeight());
|
2014-04-11 03:42:39 +02:00
|
|
|
}
|
|
|
|
|
2017-11-30 18:19:26 +01:00
|
|
|
enum
|
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
SPR_GAME_PALETTE_DEFAULT = 1532,
|
|
|
|
SPR_GAME_PALETTE_WATER = 1533,
|
2017-06-06 23:24:18 +02:00
|
|
|
SPR_GAME_PALETTE_WATER_DARKER_1 = 1534,
|
|
|
|
SPR_GAME_PALETTE_WATER_DARKER_2 = 1535,
|
2018-06-22 23:25:16 +02:00
|
|
|
SPR_GAME_PALETTE_3 = 1536,
|
|
|
|
SPR_GAME_PALETTE_3_DARKER_1 = 1537,
|
|
|
|
SPR_GAME_PALETTE_3_DARKER_2 = 1538,
|
|
|
|
SPR_GAME_PALETTE_4 = 1539,
|
|
|
|
SPR_GAME_PALETTE_4_DARKER_1 = 1540,
|
|
|
|
SPR_GAME_PALETTE_4_DARKER_2 = 1541,
|
2016-11-10 13:21:47 +01:00
|
|
|
};
|
|
|
|
|
2014-07-18 05:29:39 +02:00
|
|
|
/**
|
2018-06-22 23:25:16 +02:00
|
|
|
*
|
|
|
|
* rct2: 0x006838BD
|
|
|
|
*/
|
2023-01-17 13:24:51 +01:00
|
|
|
void UpdatePaletteEffects()
|
2014-07-18 05:29:39 +02:00
|
|
|
{
|
2023-02-15 22:35:16 +01:00
|
|
|
auto water_type = OpenRCT2::ObjectManager::GetObjectEntry<WaterObjectEntry>(0);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-11-30 18:19:26 +01:00
|
|
|
if (gClimateLightningFlash == 1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Change palette to lighter colour during lightning
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t palette = SPR_GAME_PALETTE_DEFAULT;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-11-30 18:21:12 +01:00
|
|
|
if (water_type != nullptr)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
palette = water_type->image_id;
|
|
|
|
}
|
2023-01-19 09:16:44 +01:00
|
|
|
const G1Element* g1 = GfxGetG1Element(palette);
|
2017-11-30 18:21:12 +01:00
|
|
|
if (g1 != nullptr)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t xoffset = g1->x_offset;
|
2017-10-26 14:14:37 +02:00
|
|
|
xoffset = xoffset * 4;
|
2018-06-22 23:25:16 +02:00
|
|
|
uint8_t* paletteOffset = gGamePalette + xoffset;
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < g1->width; i++)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
|
|
|
paletteOffset[(i * 4) + 0] = -((0xFF - g1->offset[(i * 3) + 0]) / 2) - 1;
|
|
|
|
paletteOffset[(i * 4) + 1] = -((0xFF - g1->offset[(i * 3) + 1]) / 2) - 1;
|
|
|
|
paletteOffset[(i * 4) + 2] = -((0xFF - g1->offset[(i * 3) + 2]) / 2) - 1;
|
|
|
|
}
|
2022-01-10 13:10:56 +01:00
|
|
|
UpdatePalette(gGamePalette, PALETTE_OFFSET_DYNAMIC, PALETTE_LENGTH_DYNAMIC);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
gClimateLightningFlash++;
|
2017-11-30 18:19:26 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (gClimateLightningFlash == 2)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Change palette back to normal after lightning
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t palette = SPR_GAME_PALETTE_DEFAULT;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-11-30 18:21:12 +01:00
|
|
|
if (water_type != nullptr)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
palette = water_type->image_id;
|
|
|
|
}
|
|
|
|
|
2023-01-19 09:16:44 +01:00
|
|
|
const G1Element* g1 = GfxGetG1Element(palette);
|
2017-11-30 18:21:12 +01:00
|
|
|
if (g1 != nullptr)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t xoffset = g1->x_offset;
|
2017-10-26 14:14:37 +02:00
|
|
|
xoffset = xoffset * 4;
|
2018-06-22 23:25:16 +02:00
|
|
|
uint8_t* paletteOffset = gGamePalette + xoffset;
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < g1->width; i++)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
|
|
|
paletteOffset[(i * 4) + 0] = g1->offset[(i * 3) + 0];
|
|
|
|
paletteOffset[(i * 4) + 1] = g1->offset[(i * 3) + 1];
|
|
|
|
paletteOffset[(i * 4) + 2] = g1->offset[(i * 3) + 2];
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Animate the water/lava/chain movement palette
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t shade = 0;
|
2022-10-16 17:27:01 +02:00
|
|
|
if (gConfigGeneral.RenderWeatherGloom)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2024-01-24 11:18:54 +01:00
|
|
|
auto paletteId = ClimateGetWeatherGloomPaletteId(GetGameState().ClimateCurrent);
|
2020-12-05 19:13:59 +01:00
|
|
|
if (paletteId != FilterPaletteID::PaletteNull)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
shade = 1;
|
2020-12-05 19:13:59 +01:00
|
|
|
if (paletteId != FilterPaletteID::PaletteDarken1)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
shade = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t j = gPaletteEffectFrame;
|
2020-04-17 21:36:25 +02:00
|
|
|
j = ((static_cast<uint16_t>((~j / 2) * 128) * 15) >> 16);
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t waterId = SPR_GAME_PALETTE_WATER;
|
2017-11-30 18:21:12 +01:00
|
|
|
if (water_type != nullptr)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
waterId = water_type->palette_index_1;
|
|
|
|
}
|
2023-01-19 09:16:44 +01:00
|
|
|
const G1Element* g1 = GfxGetG1Element(shade + waterId);
|
2017-11-30 18:21:12 +01:00
|
|
|
if (g1 != nullptr)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
uint8_t* vs = &g1->offset[j * 3];
|
2020-05-28 23:37:50 +02:00
|
|
|
uint8_t* vd = &gGamePalette[PALETTE_OFFSET_WATER_WAVES * 4];
|
|
|
|
int32_t n = PALETTE_LENGTH_WATER_WAVES;
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < n; i++)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
|
|
|
vd[0] = vs[0];
|
|
|
|
vd[1] = vs[1];
|
|
|
|
vd[2] = vs[2];
|
|
|
|
vs += 9;
|
|
|
|
if (vs >= &g1->offset[9 * n])
|
|
|
|
{
|
|
|
|
vs -= 9 * n;
|
|
|
|
}
|
|
|
|
vd += 4;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
waterId = SPR_GAME_PALETTE_3;
|
2017-11-30 18:21:12 +01:00
|
|
|
if (water_type != nullptr)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
waterId = water_type->palette_index_2;
|
|
|
|
}
|
2023-01-16 19:52:17 +01:00
|
|
|
g1 = GfxGetG1Element(shade + waterId);
|
2017-11-30 18:21:12 +01:00
|
|
|
if (g1 != nullptr)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
uint8_t* vs = &g1->offset[j * 3];
|
2020-05-28 23:37:50 +02:00
|
|
|
uint8_t* vd = &gGamePalette[PALETTE_OFFSET_WATER_SPARKLES * 4];
|
|
|
|
int32_t n = PALETTE_LENGTH_WATER_SPARKLES;
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < n; i++)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
|
|
|
vd[0] = vs[0];
|
|
|
|
vd[1] = vs[1];
|
|
|
|
vd[2] = vs[2];
|
|
|
|
vs += 9;
|
|
|
|
if (vs >= &g1->offset[9 * n])
|
|
|
|
{
|
|
|
|
vs -= 9 * n;
|
|
|
|
}
|
|
|
|
vd += 4;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
j = (static_cast<uint16_t>(gPaletteEffectFrame * -960) * 3) >> 16;
|
2017-06-06 23:24:18 +02:00
|
|
|
waterId = SPR_GAME_PALETTE_4;
|
2023-01-16 19:52:17 +01:00
|
|
|
g1 = GfxGetG1Element(shade + waterId);
|
2017-11-30 18:21:12 +01:00
|
|
|
if (g1 != nullptr)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
uint8_t* vs = &g1->offset[j * 3];
|
2020-05-28 23:37:50 +02:00
|
|
|
uint8_t* vd = &gGamePalette[PALETTE_INDEX_243 * 4];
|
2018-06-22 23:25:16 +02:00
|
|
|
int32_t n = 3;
|
2018-06-20 17:28:51 +02:00
|
|
|
for (int32_t i = 0; i < n; i++)
|
2017-10-26 14:14:37 +02:00
|
|
|
{
|
|
|
|
vd[0] = vs[0];
|
|
|
|
vd[1] = vs[1];
|
|
|
|
vd[2] = vs[2];
|
|
|
|
vs += 3;
|
2017-11-30 18:19:26 +01:00
|
|
|
if (vs >= &g1->offset[3 * n])
|
|
|
|
{
|
2017-10-26 14:14:37 +02:00
|
|
|
vs -= 3 * n;
|
|
|
|
}
|
|
|
|
vd += 4;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-10 13:10:56 +01:00
|
|
|
UpdatePalette(gGamePalette, PALETTE_OFFSET_ANIMATED, PALETTE_LENGTH_ANIMATED);
|
2017-11-30 18:19:26 +01:00
|
|
|
if (gClimateLightningFlash == 2)
|
|
|
|
{
|
2022-01-10 13:10:56 +01:00
|
|
|
UpdatePalette(gGamePalette, PALETTE_OFFSET_DYNAMIC, PALETTE_LENGTH_DYNAMIC);
|
2017-06-06 23:24:18 +02:00
|
|
|
gClimateLightningFlash = 0;
|
|
|
|
}
|
|
|
|
}
|
2014-07-18 05:29:39 +02:00
|
|
|
}
|
2014-08-05 19:15:28 +02:00
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void PauseToggle()
|
2015-03-31 03:21:30 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gGamePaused ^= GAME_PAUSED_NORMAL;
|
2023-01-16 21:13:42 +01:00
|
|
|
WindowInvalidateByClass(WindowClass::TopToolbar);
|
2017-11-30 18:19:26 +01:00
|
|
|
if (gGamePaused & GAME_PAUSED_NORMAL)
|
|
|
|
{
|
2020-10-06 23:34:42 +02:00
|
|
|
OpenRCT2::Audio::StopAll();
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-03-31 03:21:30 +02:00
|
|
|
}
|
2014-08-05 19:15:28 +02:00
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
bool GameIsPaused()
|
2016-04-23 14:34:55 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return gGamePaused != 0;
|
2016-04-23 14:34:55 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
bool GameIsNotPaused()
|
2016-04-23 14:34:55 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return gGamePaused == 0;
|
2016-04-23 14:34:55 +02:00
|
|
|
}
|
|
|
|
|
2014-05-02 23:21:08 +02:00
|
|
|
/**
|
2014-11-20 19:40:10 +01:00
|
|
|
*
|
2014-05-02 23:21:08 +02:00
|
|
|
* rct2: 0x0066DC0F
|
|
|
|
*/
|
2023-01-17 13:24:51 +01:00
|
|
|
static void LoadLandscape()
|
2014-05-02 23:21:08 +02:00
|
|
|
{
|
2022-08-21 18:38:25 +02:00
|
|
|
auto intent = Intent(WindowClass::Loadsave);
|
2023-01-17 20:46:55 +01:00
|
|
|
intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_LANDSCAPE);
|
2022-11-06 21:49:07 +01:00
|
|
|
ContextOpenIntent(&intent);
|
2014-05-02 23:21:08 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 00:12:35 +01:00
|
|
|
void RCT2StringToUTF8Self(char* buffer, size_t length)
|
2016-03-13 16:33:38 +01:00
|
|
|
{
|
2017-11-30 18:19:26 +01:00
|
|
|
if (length > 0)
|
|
|
|
{
|
2023-01-17 00:12:35 +01:00
|
|
|
auto temp = RCT2StringToUTF8(buffer, RCT2LanguageId::EnglishUK);
|
2023-01-18 22:42:16 +01:00
|
|
|
SafeStrCpy(buffer, temp.data(), length);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-03-13 16:33:38 +01:00
|
|
|
}
|
|
|
|
|
2023-10-20 13:26:30 +02:00
|
|
|
static void FixGuestsHeadingToParkCount()
|
|
|
|
{
|
2024-01-28 11:26:22 +01:00
|
|
|
auto& gameState = GetGameState();
|
|
|
|
|
2023-10-20 13:26:30 +02:00
|
|
|
uint32_t guestsHeadingToPark = 0;
|
|
|
|
|
|
|
|
for (auto* peep : EntityList<Guest>())
|
|
|
|
{
|
|
|
|
if (peep->OutsideOfPark && peep->State != PeepState::LeavingPark)
|
|
|
|
{
|
|
|
|
guestsHeadingToPark++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-28 11:26:22 +01:00
|
|
|
if (gameState.NumGuestsHeadingForPark != guestsHeadingToPark)
|
2023-10-20 14:02:28 +02:00
|
|
|
{
|
2024-01-28 11:26:22 +01:00
|
|
|
LOG_WARNING(
|
|
|
|
"Corrected bad amount of guests heading to park: %u -> %u", gameState.NumGuestsHeadingForPark, guestsHeadingToPark);
|
2023-10-20 14:02:28 +02:00
|
|
|
}
|
|
|
|
|
2024-01-28 11:26:22 +01:00
|
|
|
gameState.NumGuestsHeadingForPark = guestsHeadingToPark;
|
2023-10-20 13:26:30 +02:00
|
|
|
}
|
|
|
|
|
2023-10-20 13:22:14 +02:00
|
|
|
static void FixGuestCount()
|
2017-07-05 18:18:40 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Recalculates peep count after loading a save to fix corrupted files
|
2020-06-10 18:54:34 +02:00
|
|
|
uint32_t guestCount = 0;
|
2023-10-20 13:27:02 +02:00
|
|
|
|
|
|
|
for (auto guest : EntityList<Guest>())
|
2017-11-30 18:24:44 +01:00
|
|
|
{
|
2023-10-20 13:27:02 +02:00
|
|
|
if (!guest->OutsideOfPark)
|
2020-06-10 18:54:34 +02:00
|
|
|
{
|
2023-10-20 13:27:02 +02:00
|
|
|
guestCount++;
|
2020-06-10 18:54:34 +02:00
|
|
|
}
|
2017-11-30 18:24:44 +01:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2024-01-28 23:17:43 +01:00
|
|
|
auto& gameState = GetGameState();
|
|
|
|
if (gameState.NumGuestsInPark != guestCount)
|
2023-10-20 14:02:28 +02:00
|
|
|
{
|
2024-01-28 23:17:43 +01:00
|
|
|
LOG_WARNING("Corrected bad amount of guests in park: %u -> %u", gameState.NumGuestsInPark, guestCount);
|
2023-10-20 14:02:28 +02:00
|
|
|
}
|
|
|
|
|
2024-01-28 23:17:43 +01:00
|
|
|
gameState.NumGuestsInPark = guestCount;
|
2023-10-20 13:22:14 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2023-10-20 13:22:14 +02:00
|
|
|
static void FixPeepsWithInvalidRideReference()
|
|
|
|
{
|
2018-02-10 20:24:50 +01:00
|
|
|
// Peeps to remove have to be cached here, as removing them from within the loop breaks iteration
|
2019-02-28 20:28:58 +01:00
|
|
|
std::vector<Peep*> peepsToRemove;
|
2018-02-10 20:24:50 +01:00
|
|
|
|
2018-02-09 22:43:31 +01:00
|
|
|
// Fix possibly invalid field values
|
2021-03-16 12:18:06 +01:00
|
|
|
for (auto peep : EntityList<Guest>())
|
2018-02-09 22:43:31 +01:00
|
|
|
{
|
2022-01-26 13:28:19 +01:00
|
|
|
if (peep->CurrentRideStation.ToUnderlying() >= OpenRCT2::Limits::MaxStationsPerRide)
|
2018-02-09 22:43:31 +01:00
|
|
|
{
|
2020-06-08 14:04:07 +02:00
|
|
|
const auto srcStation = peep->CurrentRideStation;
|
|
|
|
const auto rideIdx = peep->CurrentRide;
|
2022-01-19 17:24:23 +01:00
|
|
|
if (rideIdx.IsNull())
|
2018-02-10 20:24:50 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2023-01-17 01:32:54 +01:00
|
|
|
Ride* ride = GetRide(rideIdx);
|
2019-11-09 17:00:09 +01:00
|
|
|
if (ride == nullptr)
|
|
|
|
{
|
2023-01-27 18:18:44 +01:00
|
|
|
LOG_WARNING("Couldn't find ride %u, resetting ride on peep %u", rideIdx, peep->Id);
|
2022-01-19 17:24:23 +01:00
|
|
|
peep->CurrentRide = RideId::GetNull();
|
2019-11-09 17:00:09 +01:00
|
|
|
continue;
|
|
|
|
}
|
2019-07-21 21:44:19 +02:00
|
|
|
auto curName = peep->GetName();
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_WARNING(
|
2023-01-27 18:18:44 +01:00
|
|
|
"Peep %u (%s) has invalid ride station = %u for ride %u.", peep->Id, curName.c_str(), srcStation.ToUnderlying(),
|
|
|
|
rideIdx);
|
2023-01-17 01:32:54 +01:00
|
|
|
auto station = RideGetFirstValidStationExit(*ride);
|
2022-01-26 13:28:19 +01:00
|
|
|
if (station.IsNull())
|
2018-02-09 23:49:50 +01:00
|
|
|
{
|
2023-01-27 18:18:44 +01:00
|
|
|
LOG_WARNING("Couldn't find station, removing peep %u", peep->Id);
|
2018-02-10 20:24:50 +01:00
|
|
|
peepsToRemove.push_back(peep);
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_WARNING("Amending ride station to %u.", station);
|
2020-06-08 03:18:17 +02:00
|
|
|
peep->CurrentRideStation = station;
|
2018-02-09 23:49:50 +01:00
|
|
|
}
|
2018-02-09 22:43:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-20 20:39:47 +02:00
|
|
|
if (!peepsToRemove.empty())
|
2018-03-10 08:39:57 +01:00
|
|
|
{
|
|
|
|
// Some broken saves have broken spatial indexes
|
2021-11-24 14:37:47 +01:00
|
|
|
ResetEntitySpatialIndices();
|
2018-03-10 08:39:57 +01:00
|
|
|
}
|
|
|
|
|
2018-02-10 20:24:50 +01:00
|
|
|
for (auto ptr : peepsToRemove)
|
|
|
|
{
|
2018-03-27 22:04:57 +02:00
|
|
|
ptr->Remove();
|
2018-02-10 20:24:50 +01:00
|
|
|
}
|
2023-10-20 13:22:14 +02:00
|
|
|
}
|
2018-02-10 20:24:50 +01:00
|
|
|
|
2023-10-20 13:22:14 +02:00
|
|
|
static void FixInvalidSurfaces()
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Fixes broken saves where a surface element could be null
|
2017-07-05 18:18:40 +02:00
|
|
|
// and broken saves with incorrect invisible map border tiles
|
2023-10-20 13:22:14 +02:00
|
|
|
|
2024-03-17 14:41:36 +01:00
|
|
|
for (int32_t y = 0; y < kMaximumMapSizeTechnical; y++)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2024-03-17 14:41:36 +01:00
|
|
|
for (int32_t x = 0; x < kMaximumMapSizeTechnical; x++)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2023-04-05 22:13:47 +02:00
|
|
|
auto* surfaceElement = MapGetSurfaceElementAt(TileCoordsXY{ x, y });
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2019-08-11 10:06:04 +02:00
|
|
|
if (surfaceElement == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_ERROR("Null map element at x = %d and y = %d. Fixing...", x, y);
|
2021-02-04 17:21:17 +01:00
|
|
|
surfaceElement = TileElementInsert<SurfaceElement>(TileCoordsXYZ{ x, y, 14 }.ToCoordsXYZ(), 0b0000);
|
|
|
|
if (surfaceElement == nullptr)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_ERROR("Unable to fix: Map element limit reached.");
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-07-05 18:18:40 +02:00
|
|
|
|
|
|
|
// Fix the invisible border tiles.
|
2019-08-11 10:06:04 +02:00
|
|
|
// At this point, we can be sure that surfaceElement is not NULL.
|
2024-02-12 22:32:08 +01:00
|
|
|
auto& gameState = GetGameState();
|
|
|
|
if (x == 0 || x == gameState.MapSize.x - 1 || y == 0 || y == gameState.MapSize.y - 1)
|
2017-07-05 18:18:40 +02:00
|
|
|
{
|
2024-03-01 21:23:29 +01:00
|
|
|
surfaceElement->SetBaseZ(kMinimumLandZ);
|
|
|
|
surfaceElement->SetClearanceZ(kMinimumLandZ);
|
2019-08-11 10:06:04 +02:00
|
|
|
surfaceElement->SetSlope(0);
|
|
|
|
surfaceElement->SetWaterHeight(0);
|
2017-07-05 18:18:40 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2023-10-20 13:22:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// OpenRCT2 workaround to recalculate some values which are saved redundantly in the save to fix corrupted files.
|
|
|
|
// For example recalculate guest count by looking at all the guests instead of trusting the value in the file.
|
|
|
|
void GameFixSaveVars()
|
|
|
|
{
|
2023-10-20 13:26:30 +02:00
|
|
|
FixGuestsHeadingToParkCount();
|
|
|
|
|
2023-10-20 13:22:14 +02:00
|
|
|
FixGuestCount();
|
|
|
|
|
|
|
|
FixPeepsWithInvalidRideReference();
|
|
|
|
|
|
|
|
FixInvalidSurfaces();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2021-10-27 14:21:14 +02:00
|
|
|
ResearchFix();
|
2017-06-29 18:06:26 +02:00
|
|
|
|
2018-02-24 16:33:11 +01:00
|
|
|
// Fix banners which share their index
|
2023-01-28 23:43:59 +01:00
|
|
|
BannerApplyFixes();
|
2018-02-24 16:33:11 +01:00
|
|
|
|
2017-07-06 03:20:51 +02:00
|
|
|
// Fix invalid vehicle sprite sizes, thus preventing visual corruption of sprites
|
2023-01-17 01:32:54 +01:00
|
|
|
FixInvalidVehicleSpriteSizes();
|
2017-10-22 23:22:46 +02:00
|
|
|
|
2017-10-31 12:57:40 +01:00
|
|
|
// Fix gParkEntrance locations for which the tile_element no longer exists
|
2022-10-04 08:38:00 +02:00
|
|
|
ParkEntranceFixLocations();
|
2021-10-27 14:21:14 +02:00
|
|
|
|
2022-03-08 01:14:52 +01:00
|
|
|
UpdateConsolidatedPatrolAreas();
|
2022-11-23 20:02:34 +01:00
|
|
|
|
|
|
|
MapCountRemainingLandRights();
|
2015-10-07 20:53:55 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameLoadInit()
|
2015-07-05 17:19:01 +02:00
|
|
|
{
|
2024-04-23 21:31:05 +02:00
|
|
|
auto* context = GetContext();
|
|
|
|
|
|
|
|
IGameStateSnapshots* snapshots = context->GetGameStateSnapshots();
|
2019-05-11 21:31:34 +02:00
|
|
|
snapshots->Reset();
|
|
|
|
|
2024-04-23 21:31:05 +02:00
|
|
|
context->SetActiveScene(context->GetGameScene());
|
|
|
|
|
2017-10-30 12:07:01 +01:00
|
|
|
if (!gLoadKeepWindowsOpen)
|
|
|
|
{
|
2023-01-16 21:14:50 +01:00
|
|
|
ViewportInitAll();
|
2023-01-17 13:24:51 +01:00
|
|
|
GameCreateWindows();
|
2017-11-03 01:16:30 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-16 21:13:42 +01:00
|
|
|
auto* mainWindow = WindowGetMain();
|
|
|
|
WindowUnfollowSprite(*mainWindow);
|
2017-10-30 12:07:01 +01:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2024-04-23 21:31:05 +02:00
|
|
|
auto windowManager = context->GetUiContext()->GetWindowManager();
|
2024-02-25 21:46:01 +01:00
|
|
|
auto& gameState = GetGameState();
|
|
|
|
windowManager->SetMainView(gameState.SavedView, gameState.SavedViewZoom, gameState.SavedViewRotation);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2023-01-18 07:05:53 +01:00
|
|
|
if (NetworkGetMode() != NETWORK_MODE_CLIENT)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2019-08-20 23:51:12 +02:00
|
|
|
GameActions::ClearQueue();
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2021-11-24 14:37:47 +01:00
|
|
|
ResetEntitySpatialIndices();
|
2023-01-17 13:24:51 +01:00
|
|
|
ResetAllSpriteQuadrantPlacements();
|
2022-10-08 11:56:17 +02:00
|
|
|
ScenerySetDefaultPlacementConfiguration();
|
2017-10-07 01:28:00 +02:00
|
|
|
|
2018-02-05 22:59:44 +01:00
|
|
|
auto intent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES);
|
2022-11-06 21:49:07 +01:00
|
|
|
ContextBroadcastIntent(&intent);
|
2017-10-07 01:28:00 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
gWindowUpdateTicks = 0;
|
2024-02-13 20:03:30 +01:00
|
|
|
gCurrentRealTimeTicks = 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2023-01-16 14:50:43 +01:00
|
|
|
LoadPalette();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2017-11-30 18:19:26 +01:00
|
|
|
if (!gOpenRCT2Headless)
|
|
|
|
{
|
2018-02-05 22:59:44 +01:00
|
|
|
intent = Intent(INTENT_ACTION_CLEAR_TILE_INSPECTOR_CLIPBOARD);
|
2022-11-06 21:49:07 +01:00
|
|
|
ContextBroadcastIntent(&intent);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gGameSpeed = 1;
|
2020-02-22 23:18:22 +01:00
|
|
|
}
|
2020-02-07 00:15:20 +01:00
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameLoadScripts()
|
2020-02-22 23:18:22 +01:00
|
|
|
{
|
2020-04-24 16:58:01 +02:00
|
|
|
#ifdef ENABLE_SCRIPTING
|
2021-03-02 23:55:41 +01:00
|
|
|
GetContext()->GetScriptEngine().LoadTransientPlugins();
|
2020-02-23 13:55:48 +01:00
|
|
|
#endif
|
2020-02-07 00:15:20 +01:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameUnloadScripts()
|
2020-02-07 00:15:20 +01:00
|
|
|
{
|
2020-04-24 16:58:01 +02:00
|
|
|
#ifdef ENABLE_SCRIPTING
|
2021-03-02 23:55:41 +01:00
|
|
|
GetContext()->GetScriptEngine().UnloadTransientPlugins();
|
2020-02-23 13:55:48 +01:00
|
|
|
#endif
|
2014-05-04 17:21:15 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameNotifyMapChange()
|
2022-02-20 03:05:24 +01:00
|
|
|
{
|
|
|
|
#ifdef ENABLE_SCRIPTING
|
2022-03-21 23:56:21 +01:00
|
|
|
// Ensure we don't get a two lots of change events
|
|
|
|
if (_mapChangedExpected)
|
|
|
|
return;
|
|
|
|
|
2022-02-20 03:05:24 +01:00
|
|
|
using namespace OpenRCT2::Scripting;
|
|
|
|
|
|
|
|
auto& scriptEngine = GetContext()->GetScriptEngine();
|
|
|
|
auto& hookEngine = scriptEngine.GetHookEngine();
|
|
|
|
hookEngine.Call(HOOK_TYPE::MAP_CHANGE, false);
|
2022-03-21 23:56:21 +01:00
|
|
|
_mapChangedExpected = true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameNotifyMapChanged()
|
2022-03-21 23:56:21 +01:00
|
|
|
{
|
|
|
|
#ifdef ENABLE_SCRIPTING
|
|
|
|
using namespace OpenRCT2::Scripting;
|
|
|
|
|
|
|
|
auto& scriptEngine = GetContext()->GetScriptEngine();
|
|
|
|
auto& hookEngine = scriptEngine.GetHookEngine();
|
|
|
|
hookEngine.Call(HOOK_TYPE::MAP_CHANGED, false);
|
|
|
|
_mapChangedExpected = false;
|
2022-02-20 03:05:24 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-12-11 16:38:37 +01:00
|
|
|
/**
|
2014-06-21 16:50:13 +02:00
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x0069E9A7
|
2015-05-30 11:15:29 +02:00
|
|
|
* Call after a rotation or loading of a save to reset sprite quadrants
|
2014-06-21 16:50:13 +02:00
|
|
|
*/
|
2023-01-17 13:24:51 +01:00
|
|
|
void ResetAllSpriteQuadrantPlacements()
|
2014-11-24 00:03:45 +01:00
|
|
|
{
|
2022-02-13 00:48:36 +01:00
|
|
|
for (EntityId::UnderlyingType i = 0; i < MAX_ENTITIES; i++)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2022-02-12 22:31:06 +01:00
|
|
|
auto* spr = GetEntity(EntityId::FromUnderlying(i));
|
2021-03-17 09:04:41 +01:00
|
|
|
if (spr != nullptr && spr->Type != EntityType::Null)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2021-09-28 02:16:04 +02:00
|
|
|
spr->MoveTo(spr->GetLocation());
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-21 16:50:13 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void SaveGame()
|
2014-05-27 23:33:16 +02:00
|
|
|
{
|
2022-09-25 08:33:28 +02:00
|
|
|
if (!gFirstTimeSaving && !gIsAutosaveLoaded)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2022-02-26 17:40:49 +01:00
|
|
|
const auto savePath = Path::WithExtension(gScenarioSavePath, ".park");
|
2023-01-17 13:24:51 +01:00
|
|
|
SaveGameWithName(savePath);
|
2017-11-30 18:19:26 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
SaveGameAs();
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-08-19 21:54:25 +02:00
|
|
|
}
|
2017-09-12 00:04:03 +02:00
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void SaveGameCmd(u8string_view name /* = {} */)
|
2018-10-14 22:36:41 +02:00
|
|
|
{
|
2022-01-29 13:01:05 +01:00
|
|
|
if (name.empty())
|
2018-10-14 22:36:41 +02:00
|
|
|
{
|
2022-02-26 17:40:49 +01:00
|
|
|
const auto savePath = Path::WithExtension(gScenarioSavePath, ".park");
|
2021-12-22 16:19:02 +01:00
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
SaveGameWithName(savePath);
|
2018-10-14 22:36:41 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-01-29 13:01:05 +01:00
|
|
|
auto env = GetContext()->GetPlatformEnvironment();
|
2022-02-27 22:23:35 +01:00
|
|
|
auto savePath = Path::Combine(env->GetDirectoryPath(DIRBASE::USER, DIRID::SAVE), u8string(name) + u8".park");
|
2023-01-17 13:24:51 +01:00
|
|
|
SaveGameWithName(savePath);
|
2018-10-14 22:36:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void SaveGameWithName(u8string_view name)
|
2018-10-14 22:36:41 +02:00
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_VERBOSE("Saving to %s", u8string(name).c_str());
|
2023-12-28 00:29:06 +01:00
|
|
|
|
|
|
|
auto& gameState = GetGameState();
|
|
|
|
if (ScenarioSave(gameState, name, gConfigGeneral.SavePluginData ? 1 : 0))
|
2018-10-14 22:36:41 +02:00
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_VERBOSE("Saved to %s", u8string(name).c_str());
|
2018-12-15 19:39:00 +01:00
|
|
|
gCurrentLoadedPath = name;
|
2022-09-25 08:33:28 +02:00
|
|
|
gIsAutosaveLoaded = false;
|
2018-10-14 22:36:41 +02:00
|
|
|
gScreenAge = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
std::unique_ptr<Intent> CreateSaveGameAsIntent()
|
2015-08-19 21:54:25 +02:00
|
|
|
{
|
2022-02-26 17:20:07 +01:00
|
|
|
auto name = Path::GetFileNameWithoutExtension(gScenarioSavePath);
|
2017-09-12 00:04:03 +02:00
|
|
|
|
2022-08-21 18:38:25 +02:00
|
|
|
auto intent = std::make_unique<Intent>(WindowClass::Loadsave);
|
2023-01-17 20:46:55 +01:00
|
|
|
intent->PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_GAME);
|
|
|
|
intent->PutExtra(INTENT_EXTRA_PATH, name);
|
2017-09-12 00:04:03 +02:00
|
|
|
|
|
|
|
return intent;
|
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void SaveGameAs()
|
2017-09-12 00:04:03 +02:00
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
auto intent = CreateSaveGameAsIntent();
|
2022-11-06 21:49:07 +01:00
|
|
|
ContextOpenIntent(intent.get());
|
2014-05-27 23:33:16 +02:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
static void LimitAutosaveCount(const size_t numberOfFilesToKeep, bool processLandscapeFolder)
|
2016-01-04 16:22:15 +01:00
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
size_t autosavesCount = 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t numAutosavesToDelete = 0;
|
2016-01-04 16:22:15 +01:00
|
|
|
|
2020-10-17 16:57:18 +02:00
|
|
|
auto environment = GetContext()->GetPlatformEnvironment();
|
|
|
|
auto folderDirectory = environment->GetDirectoryPath(DIRBASE::USER, DIRID::SAVE);
|
2021-11-22 10:41:44 +01:00
|
|
|
char const* fileFilter = "autosave_*.park";
|
2017-11-30 18:19:26 +01:00
|
|
|
if (processLandscapeFolder)
|
|
|
|
{
|
2020-10-17 16:57:18 +02:00
|
|
|
folderDirectory = environment->GetDirectoryPath(DIRBASE::USER, DIRID::LANDSCAPE);
|
2022-01-24 20:03:20 +01:00
|
|
|
fileFilter = "autosave_*.park";
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-11-13 20:17:49 +01:00
|
|
|
|
2022-03-10 10:51:34 +01:00
|
|
|
const u8string filter = Path::Combine(folderDirectory, "autosave", fileFilter);
|
2020-10-17 16:18:23 +02:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// At first, count how many autosaves there are
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2021-05-20 17:06:53 +02:00
|
|
|
auto scanner = Path::ScanDirectory(filter, false);
|
2018-01-06 19:52:12 +01:00
|
|
|
while (scanner->Next())
|
|
|
|
{
|
|
|
|
autosavesCount++;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-11-13 20:17:49 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// If there are fewer autosaves than the number of files to keep we don't need to delete anything
|
2017-11-30 18:19:26 +01:00
|
|
|
if (autosavesCount <= numberOfFilesToKeep)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-11-13 20:17:49 +01:00
|
|
|
|
2022-03-10 10:51:34 +01:00
|
|
|
std::vector<u8string> autosaveFiles;
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2021-05-20 17:06:53 +02:00
|
|
|
auto scanner = Path::ScanDirectory(filter, false);
|
2018-01-06 19:52:12 +01:00
|
|
|
for (size_t i = 0; i < autosavesCount; i++)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2018-01-06 19:52:12 +01:00
|
|
|
if (scanner->Next())
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2022-03-10 10:51:34 +01:00
|
|
|
autosaveFiles.emplace_back(Path::Combine(folderDirectory, "autosave", scanner->GetPathRelative()));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-04 16:22:15 +01:00
|
|
|
|
2020-06-28 10:12:13 +02:00
|
|
|
std::sort(autosaveFiles.begin(), autosaveFiles.end(), [](const auto& saveFile0, const auto& saveFile1) {
|
|
|
|
return saveFile0.compare(saveFile1) < 0;
|
|
|
|
});
|
2016-01-04 16:22:15 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// Calculate how many saves we need to delete.
|
2022-03-10 10:51:34 +01:00
|
|
|
numAutosavesToDelete = autosaveFiles.size() - numberOfFilesToKeep;
|
2016-11-13 20:17:49 +01:00
|
|
|
|
2017-12-03 22:38:02 +01:00
|
|
|
for (size_t i = 0; numAutosavesToDelete > 0; i++, numAutosavesToDelete--)
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2022-03-10 10:51:34 +01:00
|
|
|
if (!File::Delete(autosaveFiles[i]))
|
2020-10-25 09:35:42 +01:00
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LOG_WARNING("Failed to delete autosave file: %s", autosaveFiles[i].data());
|
2020-10-25 09:35:42 +01:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-01-04 16:22:15 +01:00
|
|
|
}
|
2015-08-19 21:54:25 +02:00
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameAutosave()
|
2015-02-21 12:05:15 +01:00
|
|
|
{
|
2022-01-29 13:01:05 +01:00
|
|
|
auto subDirectory = DIRID::SAVE;
|
2021-11-22 10:41:44 +01:00
|
|
|
const char* fileExtension = ".park";
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t saveFlags = 0x80000000;
|
2017-11-30 18:19:26 +01:00
|
|
|
if (gScreenFlags & SCREEN_FLAGS_EDITOR)
|
|
|
|
{
|
2022-01-29 13:01:05 +01:00
|
|
|
subDirectory = DIRID::LANDSCAPE;
|
2022-01-24 20:03:20 +01:00
|
|
|
fileExtension = ".park";
|
2017-06-06 23:24:18 +02:00
|
|
|
saveFlags |= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve current time
|
2020-06-28 21:46:14 +02:00
|
|
|
auto currentDate = Platform::GetDateLocal();
|
|
|
|
auto currentTime = Platform::GetTimeLocal();
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
utf8 timeName[44];
|
2018-06-22 23:25:16 +02:00
|
|
|
snprintf(
|
2018-07-21 13:51:54 +02:00
|
|
|
timeName, sizeof(timeName), "autosave_%04u-%02u-%02u_%02u-%02u-%02u%s", currentDate.year, currentDate.month,
|
|
|
|
currentDate.day, currentTime.hour, currentTime.minute, currentTime.second, fileExtension);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-10-16 17:27:01 +02:00
|
|
|
int32_t autosavesToKeep = gConfigGeneral.AutosaveAmount;
|
2023-01-17 13:24:51 +01:00
|
|
|
LimitAutosaveCount(autosavesToKeep - 1, (gScreenFlags & SCREEN_FLAGS_EDITOR));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-01-29 13:01:05 +01:00
|
|
|
auto env = GetContext()->GetPlatformEnvironment();
|
2022-02-27 22:23:35 +01:00
|
|
|
auto autosaveDir = Path::Combine(env->GetDirectoryPath(DIRBASE::USER, subDirectory), u8"autosave");
|
2023-05-09 21:08:46 +02:00
|
|
|
Path::CreateDirectory(autosaveDir);
|
2022-01-29 13:01:05 +01:00
|
|
|
|
|
|
|
auto path = Path::Combine(autosaveDir, timeName);
|
2022-02-27 22:23:35 +01:00
|
|
|
auto backupFileName = u8string(u8"autosave") + fileExtension + u8".bak";
|
2022-01-29 13:01:05 +01:00
|
|
|
auto backupPath = Path::Combine(autosaveDir, backupFileName);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2022-01-08 18:38:09 +01:00
|
|
|
if (File::Exists(path))
|
2017-11-30 18:19:26 +01:00
|
|
|
{
|
2022-01-08 13:57:29 +01:00
|
|
|
File::Copy(path, backupPath, true);
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
|
|
|
|
2023-12-28 00:29:06 +01:00
|
|
|
auto& gameState = GetGameState();
|
|
|
|
|
|
|
|
if (!ScenarioSave(gameState, path, saveFlags))
|
2021-02-13 01:15:27 +01:00
|
|
|
Console::Error::WriteLine("Could not autosave the scenario. Is the save folder writeable?");
|
2015-02-21 12:05:15 +01:00
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
static void GameLoadOrQuitNoSavePromptCallback(int32_t result, const utf8* path)
|
2016-11-29 19:41:54 +01:00
|
|
|
{
|
2017-11-30 18:19:26 +01:00
|
|
|
if (result == MODAL_RESULT_OK)
|
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
GameNotifyMapChange();
|
|
|
|
GameUnloadScripts();
|
2023-01-16 21:13:42 +01:00
|
|
|
WindowCloseByClass(WindowClass::EditorObjectSelection);
|
2022-10-08 19:23:26 +02:00
|
|
|
GetContext()->LoadParkFromFile(path);
|
2023-01-17 13:24:51 +01:00
|
|
|
GameLoadScripts();
|
|
|
|
GameNotifyMapChanged();
|
2022-09-25 08:33:28 +02:00
|
|
|
gIsAutosaveLoaded = gIsAutosave;
|
|
|
|
gFirstTimeSaving = false;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2016-11-29 19:41:54 +01:00
|
|
|
}
|
|
|
|
|
2023-01-06 12:56:46 +01:00
|
|
|
static void NewGameWindowCallback(const utf8* path)
|
|
|
|
{
|
2023-01-16 21:13:42 +01:00
|
|
|
WindowCloseByClass(WindowClass::EditorObjectSelection);
|
2023-01-17 13:24:51 +01:00
|
|
|
GameNotifyMapChange();
|
2023-01-06 12:56:46 +01:00
|
|
|
GetContext()->LoadParkFromFile(path, false, true);
|
2023-01-17 13:24:51 +01:00
|
|
|
GameLoadScripts();
|
|
|
|
GameNotifyMapChanged();
|
2023-01-06 12:56:46 +01:00
|
|
|
}
|
|
|
|
|
2014-05-02 23:21:08 +02:00
|
|
|
/**
|
2015-10-09 18:22:37 +02:00
|
|
|
*
|
2014-05-02 23:21:08 +02:00
|
|
|
* rct2: 0x0066DB79
|
|
|
|
*/
|
2023-01-17 13:24:51 +01:00
|
|
|
void GameLoadOrQuitNoSavePrompt()
|
2014-05-02 23:21:08 +02:00
|
|
|
{
|
2017-11-30 18:19:26 +01:00
|
|
|
switch (gSavePromptMode)
|
|
|
|
{
|
2020-10-03 04:04:59 +02:00
|
|
|
case PromptMode::SaveBeforeLoad:
|
2019-02-21 10:34:30 +01:00
|
|
|
{
|
|
|
|
auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::CloseSavePrompt);
|
|
|
|
GameActions::Execute(&loadOrQuitAction);
|
2023-01-16 21:13:42 +01:00
|
|
|
ToolCancel();
|
2018-06-22 23:25:16 +02:00
|
|
|
if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
|
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
LoadLandscape();
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-08-21 18:38:25 +02:00
|
|
|
auto intent = Intent(WindowClass::Loadsave);
|
2023-01-17 20:46:55 +01:00
|
|
|
intent.PutExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_LOAD | LOADSAVETYPE_GAME);
|
|
|
|
intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast<void*>(GameLoadOrQuitNoSavePromptCallback));
|
2022-11-06 21:49:07 +01:00
|
|
|
ContextOpenIntent(&intent);
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-02-21 10:34:30 +01:00
|
|
|
}
|
2020-10-03 04:04:59 +02:00
|
|
|
case PromptMode::SaveBeforeQuit:
|
2019-02-21 10:34:30 +01:00
|
|
|
{
|
|
|
|
auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::CloseSavePrompt);
|
|
|
|
GameActions::Execute(&loadOrQuitAction);
|
2023-01-16 21:13:42 +01:00
|
|
|
ToolCancel();
|
2023-01-17 13:24:51 +01:00
|
|
|
if (InputTestFlag(INPUT_FLAG_5))
|
2018-06-22 23:25:16 +02:00
|
|
|
{
|
2023-01-17 13:24:51 +01:00
|
|
|
InputSetFlag(INPUT_FLAG_5, false);
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
2023-10-04 17:13:40 +02:00
|
|
|
GameResetSpeed();
|
2018-06-22 23:25:16 +02:00
|
|
|
gFirstTimeSaving = true;
|
2023-01-17 13:24:51 +01:00
|
|
|
GameNotifyMapChange();
|
|
|
|
GameUnloadScripts();
|
2024-04-23 21:31:05 +02:00
|
|
|
|
|
|
|
auto* context = OpenRCT2::GetContext();
|
|
|
|
context->SetActiveScene(context->GetTitleScene());
|
2018-06-22 23:25:16 +02:00
|
|
|
break;
|
2019-02-21 10:34:30 +01:00
|
|
|
}
|
2023-01-06 12:56:46 +01:00
|
|
|
case PromptMode::SaveBeforeNewGame:
|
|
|
|
{
|
|
|
|
auto loadOrQuitAction = LoadOrQuitAction(LoadOrQuitModes::CloseSavePrompt);
|
|
|
|
GameActions::Execute(&loadOrQuitAction);
|
2023-01-16 21:13:42 +01:00
|
|
|
ToolCancel();
|
2023-01-06 12:56:46 +01:00
|
|
|
auto intent = Intent(WindowClass::ScenarioSelect);
|
2023-01-17 20:46:55 +01:00
|
|
|
intent.PutExtra(INTENT_EXTRA_CALLBACK, reinterpret_cast<void*>(NewGameWindowCallback));
|
2023-01-06 12:56:46 +01:00
|
|
|
ContextOpenIntent(&intent);
|
|
|
|
break;
|
|
|
|
}
|
2018-06-22 23:25:16 +02:00
|
|
|
default:
|
2023-01-17 13:24:51 +01:00
|
|
|
GameUnloadScripts();
|
|
|
|
OpenRCT2Finish();
|
2018-06-22 23:25:16 +02:00
|
|
|
break;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2014-05-02 23:21:08 +02:00
|
|
|
}
|
2020-03-31 23:12:35 +02:00
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
void StartSilentRecord()
|
2020-03-31 23:12:35 +02:00
|
|
|
{
|
|
|
|
std::string name = Path::Combine(
|
2022-02-27 22:23:35 +01:00
|
|
|
OpenRCT2::GetContext()->GetPlatformEnvironment()->GetDirectoryPath(OpenRCT2::DIRBASE::USER), u8"debug_replay.parkrep");
|
2020-03-31 23:12:35 +02:00
|
|
|
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
|
|
|
|
if (replayManager->StartRecording(name, OpenRCT2::k_MaxReplayTicks, OpenRCT2::IReplayManager::RecordType::SILENT))
|
|
|
|
{
|
|
|
|
OpenRCT2::ReplayRecordInfo info;
|
|
|
|
replayManager->GetCurrentReplayInfo(info);
|
2022-01-27 14:21:46 +01:00
|
|
|
gSilentRecordingName = info.FilePath;
|
2020-03-31 23:12:35 +02:00
|
|
|
|
2020-05-02 16:40:21 +02:00
|
|
|
const char* logFmt = "Silent replay recording started: (%s) %s\n";
|
2021-05-09 20:11:04 +02:00
|
|
|
Console::WriteLine(logFmt, info.Name.c_str(), info.FilePath.c_str());
|
2020-03-31 23:12:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-17 13:24:51 +01:00
|
|
|
bool StopSilentRecord()
|
2020-03-31 23:12:35 +02:00
|
|
|
{
|
|
|
|
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
|
|
|
|
if (!replayManager->IsRecording() && !replayManager->IsNormalising())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenRCT2::ReplayRecordInfo info;
|
|
|
|
replayManager->GetCurrentReplayInfo(info);
|
|
|
|
|
|
|
|
if (replayManager->StopRecording())
|
|
|
|
{
|
|
|
|
const char* logFmt = "Replay recording stopped: (%s) %s\n"
|
|
|
|
" Ticks: %u\n"
|
|
|
|
" Commands: %u\n"
|
|
|
|
" Checksums: %u";
|
|
|
|
|
2021-05-09 20:11:04 +02:00
|
|
|
Console::WriteLine(logFmt, info.Name.c_str(), info.FilePath.c_str(), info.Ticks, info.NumCommands, info.NumChecksums);
|
2020-03-31 23:12:35 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2022-03-07 21:40:48 +01:00
|
|
|
|
|
|
|
void PrepareMapForSave()
|
|
|
|
{
|
2023-01-16 21:14:50 +01:00
|
|
|
ViewportSetSavedView();
|
2022-03-07 21:40:48 +01:00
|
|
|
|
|
|
|
#ifdef ENABLE_SCRIPTING
|
|
|
|
auto& scriptEngine = GetContext()->GetScriptEngine();
|
|
|
|
auto& hookEngine = scriptEngine.GetHookEngine();
|
|
|
|
if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::MAP_SAVE))
|
|
|
|
{
|
|
|
|
hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::MAP_SAVE, false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|