From 8e1c61ab71c4a6a4f6c3be221f1daa93a86cb56c Mon Sep 17 00:00:00 2001 From: Michal Charemza Date: Mon, 3 Jun 2024 20:26:30 +0100 Subject: [PATCH] Add: [Script] GSController::TriggerSave to trigger saves from game scripts The change in https://github.com/OpenTTD/OpenTTD/pull/10655, released as part of OpenTTD 14, changed how auto saving works from every X amount of game time, to every X amount of real time. This is an improvement for normal play, but there are cases where saving every X amount of game time is useful. For example when developing and comparing AIs, or using OpenTTD to conduct experiments such as via https://github.com/michalc/OpenTTDLab which extracts data from savegame files. This change addresses this by adding the GSController::TriggerSave function to the GameScript API that allows game scripts to trigger a save, and with a specific file name. Because game script have access to the in-game date through GSDate.GetCurrentDate, and can sleep arbitrary amounts of ticks, it means they can approximate the pre OpenTTD 14 behaviour of saving every X amount of game time. It also means that via game scripts, games can be saved immediately after game start, which wasn't possible with the previous autosave behaviour. The name of the function is not Save to avoid conflict and confusion between the existing Save function that game scripts can implement to add custom data to savegame files. --- src/script/api/game/game_controller.hpp.sq | 1 + src/script/api/game_changelog.hpp | 3 +++ src/script/api/script_controller.cpp | 5 +++++ src/script/api/script_controller.hpp | 7 +++++++ src/script/script_instance.cpp | 5 +++++ src/script/script_instance.hpp | 7 +++++++ 6 files changed, 28 insertions(+) diff --git a/src/script/api/game/game_controller.hpp.sq b/src/script/api/game/game_controller.hpp.sq index cbf61b89cd..0f2aa04c93 100644 --- a/src/script/api/game/game_controller.hpp.sq +++ b/src/script/api/game/game_controller.hpp.sq @@ -21,6 +21,7 @@ void SQGSController_Register(Squirrel *engine) SQGSController.DefSQStaticMethod(engine, &ScriptController::Break, "Break", 2, ".s"); SQGSController.DefSQStaticMethod(engine, &ScriptController::GetSetting, "GetSetting", 2, ".s"); SQGSController.DefSQStaticMethod(engine, &ScriptController::GetVersion, "GetVersion", 1, "."); + SQGSController.DefSQStaticMethod(engine, &ScriptController::TriggerSave, "TriggerSave", 2, ".s"); SQGSController.DefSQStaticMethod(engine, &ScriptController::Print, "Print", 3, ".bs"); SQGSController.PostRegister(engine); diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index c3365eb94c..6e1f70b1d3 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -17,6 +17,9 @@ * * This version is not yet released. The following changes are not set in stone yet. * + * API additions: + * \li GSController::TriggerSave + * * \b 14.0 * * API additions: diff --git a/src/script/api/script_controller.cpp b/src/script/api/script_controller.cpp index 2ffbf46730..55b14515af 100644 --- a/src/script/api/script_controller.cpp +++ b/src/script/api/script_controller.cpp @@ -94,6 +94,11 @@ ScriptController::ScriptController(CompanyID company) : return _openttd_newgrf_version; } +/* static */ bool ScriptController::TriggerSave(const std::string &filename) +{ + return ScriptObject::GetActiveInstance()->TriggerSave(filename); +} + /* static */ HSQOBJECT ScriptController::Import(const std::string &library, const std::string &class_name, int version) { ScriptController *controller = ScriptObject::GetActiveInstance()->GetController(); diff --git a/src/script/api/script_controller.hpp b/src/script/api/script_controller.hpp index aed7596f03..4bed1693bd 100644 --- a/src/script/api/script_controller.hpp +++ b/src/script/api/script_controller.hpp @@ -147,6 +147,13 @@ public: */ static uint GetVersion(); + /** + * Saves the current game to the savegame directory. + * @param filename The filename to save to, not including the ".sav" extension + * @return True if the saving was successful. + */ + static bool TriggerSave(const std::string &filename); + /** * Change the minimum amount of time the script should be put in suspend mode * when you execute a command. Normally in SP this is 1, and in MP it is diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index e26d29cdf6..59abb1a2be 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -751,6 +751,11 @@ SQInteger ScriptInstance::GetOpsTillSuspend() return this->engine->GetOpsTillSuspend(); } +bool ScriptInstance::TriggerSave(const std::string &filename) +{ + return SaveOrLoad(filename + ".sav", SLO_SAVE, DFT_GAME_FILE, SAVE_DIR) == SL_OK; +} + bool ScriptInstance::DoCommandCallback(const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data, Commands cmd) { ScriptObject::ActiveInstance active(this); diff --git a/src/script/script_instance.hpp b/src/script/script_instance.hpp index 342cb822b6..d1ece6ac51 100644 --- a/src/script/script_instance.hpp +++ b/src/script/script_instance.hpp @@ -213,6 +213,13 @@ public: */ SQInteger GetOpsTillSuspend(); + /** + * Saves the current game to the savegame directory. + * @param filename The filename to save to, not including the ".sav" extension + * @return True if the saving was successful. + */ + bool TriggerSave(const std::string &filename); + /** * DoCommand callback function for all commands executed by scripts. * @param result The result of the command.