diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp index b005f9c990..52e373b29d 100644 --- a/src/ai/ai.hpp +++ b/src/ai/ai.hpp @@ -28,8 +28,9 @@ public: * Start a new AI company. * @param company At which slot the AI company should start. * @param rerandomise_ai Whether to rerandomise the configured AI. + * @param deviate Whether to add random deviation to settings that allow it. */ - static void StartNew(CompanyID company, bool rerandomise_ai = true); + static void StartNew(CompanyID company, bool rerandomise_ai = true, bool deviate = true); /** * Called every game-tick to let AIs do something. diff --git a/src/ai/ai_config.hpp b/src/ai/ai_config.hpp index b64020b4f0..2f1a6c4a74 100644 --- a/src/ai/ai_config.hpp +++ b/src/ai/ai_config.hpp @@ -24,8 +24,8 @@ public: ScriptConfig() {} - AIConfig(const AIConfig *config) : - ScriptConfig(config) + AIConfig(const AIConfig *config, bool add_random_deviation) : + ScriptConfig(config, add_random_deviation) {} class AIInfo *GetInfo() const; diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp index 06ade9d34f..759a2c12cc 100644 --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -33,7 +33,7 @@ return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer); } -/* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai) +/* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai, bool deviate) { assert(Company::IsValidID(company)); @@ -46,8 +46,9 @@ info = AI::scanner_info->SelectRandomAI(); assert(info != nullptr); /* Load default data and store the name in the settings */ - config->Change(info->GetName(), -1, false, true); + config->Change(info->GetName(), -1, false, true, false); } + if (deviate) config->AddRandomDeviation(); config->AnchorUnchangeableSettings(); Backup cur_company(_current_company, company, FILE_LINE); diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 32743dd666..98b156a2a1 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -20,6 +20,7 @@ #include "network/network_base.h" #include "network/network_admin.h" #include "ai/ai.hpp" +#include "ai/ai_config.hpp" #include "company_manager_face.h" #include "window_func.h" #include "strings_func.h" @@ -577,9 +578,10 @@ void ResetCompanyLivery(Company *c) * * @param is_ai is an AI company? * @param company CompanyID to use for the new company + * @param deviate whether to add random deviation to settings that allow it, for the AI company * @return the company struct */ -Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) +Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY, bool deviate = true) { if (!Company::CanAllocateItem()) return nullptr; @@ -625,7 +627,7 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) BuildOwnerLegend(); InvalidateWindowData(WC_SMALLMAP, 0, 1); - if (is_ai && (!_networking || _network_server)) AI::StartNew(c->index); + if (is_ai && (!_networking || _network_server)) AI::StartNew(c->index, true, deviate); AI::BroadcastNewEvent(new ScriptEventCompanyNew(c->index), c->index); Game::NewEvent(new ScriptEventCompanyNew(c->index)); @@ -646,9 +648,16 @@ TimeoutTimer _new_competitor_timeout(0, []() { if (n >= _settings_game.difficulty.max_no_competitors) return; + CompanyID cid = COMPANY_FIRST; + /* Find the next free slot */ + for (const Company *c : Company::Iterate()) { + if (c->index != cid) break; + cid++; + } + /* Send a command to all clients to start up a new AI. * Works fine for Multiplayer and Singleplayer */ - Command::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); + Command::Post(CCA_NEW_AI, cid, CRR_NONE, INVALID_CLIENT_ID, !AIConfig::GetConfig(cid)->HasScript()); }); /** Start of a new game. */ @@ -760,15 +769,36 @@ void OnTick_Companies() /* If the interval is zero, start as many competitors as needed then check every ~10 minutes if a company went bankrupt and needs replacing. */ if (timeout == 0) { /* count number of competitors */ - uint8_t n = 0; + uint8_t n_ais = 0; for (const Company *cc : Company::Iterate()) { - if (cc->is_ai) n++; + if (cc->is_ai) n_ais++; } + /* Count number of total existing companies. */ + auto current_companies = Company::GetNumItems(); + + CompanyMask ais_to_start = 0; for (auto i = 0; i < _settings_game.difficulty.max_no_competitors; i++) { - if (_networking && Company::GetNumItems() >= _settings_client.network.max_companies) break; - if (n++ >= _settings_game.difficulty.max_no_competitors) break; - Command::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); + auto count = CountBits(ais_to_start); + if (_networking && current_companies + count >= _settings_client.network.max_companies) break; + if (n_ais + count >= _settings_game.difficulty.max_no_competitors) break; + if (current_companies + count >= MAX_COMPANIES) break; + + /* Find the first company which doesn't exist yet */ + CompanyID cid = INVALID_COMPANY; + for (cid = COMPANY_FIRST; cid < MAX_COMPANIES; cid++) { + if (!Company::IsValidID(cid)) { + if (count == 0) break; + count--; + } + } + assert(count == 0); + assert(!HasBit(ais_to_start, cid)); + SetBit(ais_to_start, cid); + } + + for (auto company : SetBitIterator(ais_to_start)) { + Command::Post(CCA_NEW_AI, (CompanyID)company, CRR_NONE, INVALID_CLIENT_ID, !AIConfig::GetConfig((CompanyID)company)->HasScript()); } timeout = 10 * 60 * Ticks::TICKS_PER_SECOND; } @@ -857,7 +887,7 @@ void CompanyAdminRemove(CompanyID company_id, CompanyRemoveReason reason) * @param client_id ClientID * @return the cost of this operation or an error */ -CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id) +CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id, bool deviate) { InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0); @@ -921,7 +951,7 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID /* For network game, just assume deletion happened. */ assert(company_id == INVALID_COMPANY || !Company::IsValidID(company_id)); - Company *c = DoStartupNewCompany(true, company_id); + Company *c = DoStartupNewCompany(true, company_id, deviate); if (c != nullptr) NetworkServerNewCompany(c, nullptr); break; } diff --git a/src/company_cmd.h b/src/company_cmd.h index b2be9a45bc..d1ba66da87 100644 --- a/src/company_cmd.h +++ b/src/company_cmd.h @@ -17,7 +17,7 @@ enum ClientID : uint32_t; enum Colours : byte; -CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id); +CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id, bool deviate); CommandCost CmdGiveMoney(DoCommandFlag flags, Money money, CompanyID dest_company); CommandCost CmdRenameCompany(DoCommandFlag flags, const std::string &text); CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text); diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 797b8e8ebe..dc4343bcb9 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1015,7 +1015,7 @@ DEF_CONSOLE_CMD(ConResetCompany) } /* It is safe to remove this company */ - Command::Post(CCA_DELETE, index, CRR_MANUAL, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, index, CRR_MANUAL, INVALID_CLIENT_ID, false); IConsolePrint(CC_DEFAULT, "Company deleted."); return true; @@ -1350,16 +1350,20 @@ DEF_CONSOLE_CMD(ConStartAI) return true; } - int n = 0; + CompanyID cid = COMPANY_FIRST; /* Find the next free slot */ for (const Company *c : Company::Iterate()) { - if (c->index != n) break; - n++; + if (c->index != cid) break; + cid++; } - AIConfig *config = AIConfig::GetConfig((CompanyID)n); + AIConfig *config = AIConfig::GetConfig(cid); + bool deviate = argc == 1 ? !config->HasScript() : argc == 2; if (argc >= 2) { - config->Change(argv[1], -1, false); + auto name = std::string(argv[1]); + int version = -1; + bool force_exact_match = false; + config->Change(name, version, force_exact_match, false, deviate); /* If the name is not found, and there is a dot in the name, * try again with the assumption everything right of the dot is @@ -1370,8 +1374,10 @@ DEF_CONSOLE_CMD(ConStartAI) size_t name_length = e - argv[1]; e++; - int version = atoi(e); - config->Change(std::string(argv[1], name_length), version, true); + version = atoi(e); + name = std::string(argv[1], name_length); + force_exact_match = true; + config->Change(name, version, force_exact_match, false, deviate); } } @@ -1385,7 +1391,7 @@ DEF_CONSOLE_CMD(ConStartAI) } /* Start a new AI company */ - Command::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); + Command::Post(CCA_NEW_AI, cid, CRR_NONE, INVALID_CLIENT_ID, deviate); return true; } @@ -1420,9 +1426,11 @@ DEF_CONSOLE_CMD(ConReloadAI) return true; } + bool deviate = AIConfig::GetConfig(company_id)->IsRandom(); + /* First kill the company of the AI, then start a new one. This should start the current AI again */ - Command::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID); - Command::Post(CCA_NEW_AI, company_id, CRR_NONE, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID, false); + Command::Post(CCA_NEW_AI, company_id, CRR_NONE, INVALID_CLIENT_ID, deviate); IConsolePrint(CC_DEFAULT, "AI reloaded."); return true; @@ -1459,7 +1467,7 @@ DEF_CONSOLE_CMD(ConStopAI) } /* Now kill the company of the AI. */ - Command::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID, false); IConsolePrint(CC_DEFAULT, "AI stopped, company deleted."); return true; diff --git a/src/economy.cpp b/src/economy.cpp index 7b262e7d4d..f0f39ddb60 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -645,7 +645,7 @@ static void CompanyCheckBankrupt(Company *c) * player we are sure (the above check) that we are not the local * company and thus we won't be moved. */ if (!_networking || _network_server) { - Command::Post(CCA_DELETE, c->index, CRR_BANKRUPT, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, c->index, CRR_BANKRUPT, INVALID_CLIENT_ID, false); return; } break; diff --git a/src/game/game.hpp b/src/game/game.hpp index c3664cd696..56d3570a00 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -31,8 +31,9 @@ public: /** * Start up a new GameScript. + * @param deviation Whether to add random deviation to settings that allow it. */ - static void StartNew(); + static void StartNew(bool deviation = true); /** * Uninitialize the Game system. diff --git a/src/game/game_config.hpp b/src/game/game_config.hpp index 41385035d2..0a07dee246 100644 --- a/src/game/game_config.hpp +++ b/src/game/game_config.hpp @@ -23,8 +23,8 @@ public: ScriptConfig() {} - GameConfig(const GameConfig *config) : - ScriptConfig(config) + GameConfig(const GameConfig *config, bool add_random_deviation) : + ScriptConfig(config, add_random_deviation) {} class GameInfo *GetInfo() const; diff --git a/src/game/game_core.cpp b/src/game/game_core.cpp index c7763429ff..68dcd2207b 100644 --- a/src/game/game_core.cpp +++ b/src/game/game_core.cpp @@ -69,7 +69,7 @@ } } -/* static */ void Game::StartNew() +/* static */ void Game::StartNew(bool deviation) { if (Game::instance != nullptr) return; @@ -83,6 +83,7 @@ GameInfo *info = config->GetInfo(); if (info == nullptr) return; + if (deviation) config->AddRandomDeviation(); config->AnchorUnchangeableSettings(); Backup cur_company(_current_company, FILE_LINE); diff --git a/src/misc.cpp b/src/misc.cpp index 0e25391bce..16d13beeff 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -187,18 +187,20 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { _settings_game.ai_config[c] = nullptr; if (_settings_newgame.ai_config[c] != nullptr) { - _settings_game.ai_config[c] = new AIConfig(_settings_newgame.ai_config[c]); + _settings_game.ai_config[c] = new AIConfig(_settings_newgame.ai_config[c], true); } } _settings_game.game_config = nullptr; if (_settings_newgame.game_config != nullptr) { - _settings_game.game_config = new GameConfig(_settings_newgame.game_config); + _settings_game.game_config = new GameConfig(_settings_newgame.game_config, true); } } - /* Set random deviation for scripts. */ - for (auto &ai_config : _settings_game.ai_config) { - if (ai_config != nullptr) ai_config->AddRandomDeviation(); + if (_switch_mode == SM_RESTARTGAME) { + /* Simulate random deviation for scripts to keep the randomizer counters in sync, but don't set the deviated values. */ + for (auto &ai_config : _settings_game.ai_config) { + if (ai_config != nullptr) ai_config->AddRandomDeviation(); + } + if (_settings_game.game_config != nullptr) _settings_game.game_config->AddRandomDeviation(); } - if (_settings_game.game_config != nullptr) _settings_game.game_config->AddRandomDeviation(); } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 334b3bc243..4099270898 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -894,7 +894,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet Debug(net, 9, "Client::join_status = REGISTERING"); _network_join_status = NETWORK_JOIN_STATUS_REGISTERING; ShowJoinStatusWindow(); - Command::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); + Command::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID, false); } } else { /* take control over an existing company */ diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 2590475ca7..930d12bc1b 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1346,7 +1346,7 @@ static void AdminCompanyResetCallback(Window *, bool confirmed) { if (confirmed) { if (NetworkCompanyHasClients(_admin_company_id)) return; - Command::Post(CCA_DELETE, _admin_company_id, CRR_MANUAL, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, _admin_company_id, CRR_MANUAL, INVALID_CLIENT_ID, false); } } @@ -1482,9 +1482,9 @@ private: static void OnClickCompanyNew([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, CompanyID) { if (_network_server) { - Command::Post(CCA_NEW, INVALID_COMPANY, CRR_NONE, _network_own_client_id); + Command::Post(CCA_NEW, INVALID_COMPANY, CRR_NONE, _network_own_client_id, false); } else { - Command::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); + Command::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID, false); } } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index a60ed66ed9..a2674b67f0 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -1590,7 +1590,7 @@ static void NetworkAutoCleanCompanies() /* Is the company empty for autoclean_unprotected-months, and is there no protection? */ if (_settings_client.network.autoclean_unprotected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_unprotected && _network_company_states[c->index].password.empty()) { /* Shut the company down */ - Command::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID, false); IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no password.", c->index + 1); } /* Is the company empty for autoclean_protected-months, and there is a protection? */ @@ -1604,7 +1604,7 @@ static void NetworkAutoCleanCompanies() /* Is the company empty for autoclean_novehicles-months, and has no vehicles? */ if (_settings_client.network.autoclean_novehicles != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_novehicles && !HasBit(has_vehicles, c->index)) { /* Shut the company down */ - Command::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID, false); IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no vehicles.", c->index + 1); } } else { diff --git a/src/openttd.cpp b/src/openttd.cpp index 31db6b0d60..32a555acae 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -96,7 +96,7 @@ void CallWindowGameTickEvent(); bool HandleBootstrap(); extern void AfterLoadCompanyStats(); -extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY); +extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY, bool deviate = true); extern void OSOpenBrowser(const std::string &url); extern void RebuildTownCaches(); extern void ShowOSErrorBox(const char *buf, bool system); @@ -361,12 +361,12 @@ void MakeNewgameSettingsLive() for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { _settings_game.ai_config[c] = nullptr; if (_settings_newgame.ai_config[c] != nullptr) { - _settings_game.ai_config[c] = new AIConfig(_settings_newgame.ai_config[c]); + _settings_game.ai_config[c] = new AIConfig(_settings_newgame.ai_config[c], false); } } _settings_game.game_config = nullptr; if (_settings_newgame.game_config != nullptr) { - _settings_game.game_config = new GameConfig(_settings_newgame.game_config); + _settings_game.game_config = new GameConfig(_settings_newgame.game_config, false); } } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index a9cf5d5049..a7d6f7d153 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -69,7 +69,7 @@ #include "../safeguards.h" -extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY); +extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY, bool deviate = true); /** * Makes a tile canal or water depending on the surroundings. @@ -549,11 +549,11 @@ static void StartScripts() /* Start the AIs. */ for (const Company *c : Company::Iterate()) { - if (Company::IsValidAiID(c->index)) AI::StartNew(c->index, false); + if (Company::IsValidAiID(c->index)) AI::StartNew(c->index, false, false); } /* Start the GameScript. */ - Game::StartNew(); + Game::StartNew(false); ShowScriptDebugWindowIfScriptError(); } diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp index 927c130470..d6820abb03 100644 --- a/src/saveload/ai_sl.cpp +++ b/src/saveload/ai_sl.cpp @@ -87,11 +87,11 @@ struct AIPLChunkHandler : ChunkHandler { /* A random AI. */ config->Change(std::nullopt, -1, false, true); } else { - config->Change(_ai_saveload_name, _ai_saveload_version, false, _ai_saveload_is_random); + config->Change(_ai_saveload_name, _ai_saveload_version, false, _ai_saveload_is_random, false); if (!config->HasScript()) { /* No version of the AI available that can load the data. Try to load the * latest version of the AI instead. */ - config->Change(_ai_saveload_name, -1, false, _ai_saveload_is_random); + config->Change(_ai_saveload_name, -1, false, _ai_saveload_is_random, false); if (!config->HasScript()) { if (_ai_saveload_name.compare("%_dummy") != 0) { Debug(script, 0, "The savegame has an AI by the name '{}', version {} which is no longer available.", _ai_saveload_name, _ai_saveload_version); diff --git a/src/saveload/game_sl.cpp b/src/saveload/game_sl.cpp index 87083c6694..cee86f8a76 100644 --- a/src/saveload/game_sl.cpp +++ b/src/saveload/game_sl.cpp @@ -77,11 +77,11 @@ struct GSDTChunkHandler : ChunkHandler { GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME); if (!_game_saveload_name.empty()) { - config->Change(_game_saveload_name, _game_saveload_version, false, _game_saveload_is_random); + config->Change(_game_saveload_name, _game_saveload_version, false, _game_saveload_is_random, false); if (!config->HasScript()) { /* No version of the GameScript available that can load the data. Try to load the * latest version of the GameScript instead. */ - config->Change(_game_saveload_name, -1, false, _game_saveload_is_random); + config->Change(_game_saveload_name, -1, false, _game_saveload_is_random, false); if (!config->HasScript()) { if (_game_saveload_name.compare("%_dummy") != 0) { Debug(script, 0, "The savegame has an GameScript by the name '{}', version {} which is no longer available.", _game_saveload_name, _game_saveload_version); diff --git a/src/script/api/script_info_docs.hpp b/src/script/api/script_info_docs.hpp index 8278f39f19..c733f04858 100644 --- a/src/script/api/script_info_docs.hpp +++ b/src/script/api/script_info_docs.hpp @@ -228,7 +228,9 @@ public: * actual value of the setting in game will be randomised in the range * [user_configured_value - random_deviation, user_configured_value + random_deviation] (inclusive). * random_deviation sign is ignored and the value is clamped in the range [0, MAX(int32_t)] (inclusive). - * The randomisation will happen just before the Script start. + * The randomisation will happen in the following cases: + * - right before game start, so after the user decided the settings for the script. + * - when a random AI starts. * Not allowed if the CONFIG_BOOLEAN flag is set, otherwise optional. * - step_size The increase/decrease of the value every time the user * clicks one of the up/down arrow buttons. Optional, default is 1. diff --git a/src/script/script_config.cpp b/src/script/script_config.cpp index ee350001b5..0a6d20325d 100644 --- a/src/script/script_config.cpp +++ b/src/script/script_config.cpp @@ -18,7 +18,7 @@ #include "../safeguards.h" -void ScriptConfig::Change(std::optional name, int version, bool force_exact_match, bool is_random) +void ScriptConfig::Change(std::optional name, int version, bool force_exact_match, bool is_random, bool add_random_deviation) { if (name.has_value()) { this->name = std::move(name.value()); @@ -33,12 +33,12 @@ void ScriptConfig::Change(std::optional name, int version, bo this->ClearConfigList(); - if (_game_mode == GM_NORMAL && _switch_mode != SM_LOAD_GAME && this->info != nullptr) { + if (this->info != nullptr && add_random_deviation) { this->AddRandomDeviation(); } } -ScriptConfig::ScriptConfig(const ScriptConfig *config) +ScriptConfig::ScriptConfig(const ScriptConfig *config, bool add_random_deviation) { this->name = config->name; this->info = config->info; @@ -49,6 +49,8 @@ ScriptConfig::ScriptConfig(const ScriptConfig *config) for (const auto &item : config->settings) { this->settings[item.first] = item.second; } + + if (add_random_deviation) this->AddRandomDeviation(); } ScriptConfig::~ScriptConfig() diff --git a/src/script/script_config.hpp b/src/script/script_config.hpp index 58c40189e9..3410f8c1dc 100644 --- a/src/script/script_config.hpp +++ b/src/script/script_config.hpp @@ -63,8 +63,9 @@ public: /** * Create a new Script config that is a copy of an existing config. * @param config The object to copy. + * @param add_random_deviation Whether to add random deviation to script settings that allow it. */ - ScriptConfig(const ScriptConfig *config); + ScriptConfig(const ScriptConfig *config, bool add_random_deviation); /** Delete an Script configuration. */ virtual ~ScriptConfig(); @@ -76,8 +77,9 @@ public: * @param force_exact_match If true try to find the exact same version * as specified. If false any compatible version is ok. * @param is_random Is the Script chosen randomly? + * @param add_random_deviation Apply random deviation to settings that allow it? */ - void Change(std::optional name, int version = -1, bool force_exact_match = false, bool is_random = false); + void Change(std::optional name, int version = -1, bool force_exact_match = false, bool is_random = false, bool add_random_deviation = true); /** * Get the ScriptInfo linked to this ScriptConfig. diff --git a/src/script/script_gui.cpp b/src/script/script_gui.cpp index 9b077e028e..018361750b 100644 --- a/src/script/script_gui.cpp +++ b/src/script/script_gui.cpp @@ -176,7 +176,7 @@ struct ScriptListWindow : public Window { } else { ScriptInfoList::const_iterator it = this->info_list->cbegin(); std::advance(it, this->selected); - GetConfig(slot)->Change(it->second->GetName(), it->second->GetVersion()); + GetConfig(slot)->Change(it->second->GetName(), it->second->GetVersion(), false, false, false); } InvalidateWindowData(WC_GAME_OPTIONS, slot == OWNER_DEITY ? WN_GAME_OPTIONS_GS : WN_GAME_OPTIONS_AI); InvalidateWindowClassesData(WC_SCRIPT_SETTINGS); @@ -1080,12 +1080,14 @@ struct ScriptDebugWindow : public Window { ChangeToScript(OWNER_DEITY, _ctrl_pressed); break; - case WID_SCRD_RELOAD_TOGGLE: + case WID_SCRD_RELOAD_TOGGLE: { if (this->filter.script_debug_company == OWNER_DEITY) break; + bool deviate = AIConfig::GetConfig(this->filter.script_debug_company)->IsRandom(); /* First kill the company of the AI, then start a new one. This should start the current AI again */ - Command::Post(CCA_DELETE, this->filter.script_debug_company, CRR_MANUAL, INVALID_CLIENT_ID); - Command::Post(CCA_NEW_AI, this->filter.script_debug_company, CRR_NONE, INVALID_CLIENT_ID); + Command::Post(CCA_DELETE, this->filter.script_debug_company, CRR_MANUAL, INVALID_CLIENT_ID, false); + Command::Post(CCA_NEW_AI, this->filter.script_debug_company, CRR_NONE, INVALID_CLIENT_ID, deviate); break; + } case WID_SCRD_SETTINGS: ShowScriptSettingsWindow(this->filter.script_debug_company); diff --git a/src/settings.cpp b/src/settings.cpp index e2e3116531..ece62eaacf 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -953,7 +953,7 @@ static void AILoadConfig(const IniFile &ini, const char *grpname) for (const IniItem &item : group->items) { AIConfig *config = AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME); - config->Change(item.name); + config->Change(item.name, -1, false, false, false); if (!config->HasScript()) { if (item.name != "none") { Debug(script, 0, "The AI by the name '{}' was no longer found, and removed from the list.", item.name); @@ -980,7 +980,7 @@ static void GameLoadConfig(const IniFile &ini, const char *grpname) GameConfig *config = GameConfig::GetConfig(AIConfig::SSS_FORCE_NEWGAME); - config->Change(item.name); + config->Change(item.name, -1, false, false, false); if (!config->HasScript()) { if (item.name != "none") { Debug(script, 0, "The GameScript by the name '{}' was no longer found, and removed from the list.", item.name);