1
0
Fork 0

Change: Explicitly control whether to add random deviation

Reloading AIs will only add random deviation if they were a Random AI.

Random deviation is no longer applied after selecting a script during a running game.

"startai" console command applies random deviation depending on the number of arguments:
0 arguments - Only if the slot config is Random AI.
1 argument - Always, no parameters were provided.
2 arguments - Never, user provided parameters.
pull/12011/head
SamuXarick 2024-02-04 20:14:31 +00:00
parent 67b0fec6ed
commit f6ced8d616
23 changed files with 119 additions and 67 deletions

View File

@ -28,8 +28,9 @@ public:
* Start a new AI company. * Start a new AI company.
* @param company At which slot the AI company should start. * @param company At which slot the AI company should start.
* @param rerandomise_ai Whether to rerandomise the configured AI. * @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. * Called every game-tick to let AIs do something.

View File

@ -24,8 +24,8 @@ public:
ScriptConfig() ScriptConfig()
{} {}
AIConfig(const AIConfig *config) : AIConfig(const AIConfig *config, bool add_random_deviation) :
ScriptConfig(config) ScriptConfig(config, add_random_deviation)
{} {}
class AIInfo *GetInfo() const; class AIInfo *GetInfo() const;

View File

@ -33,7 +33,7 @@
return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer); 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)); assert(Company::IsValidID(company));
@ -46,8 +46,9 @@
info = AI::scanner_info->SelectRandomAI(); info = AI::scanner_info->SelectRandomAI();
assert(info != nullptr); assert(info != nullptr);
/* Load default data and store the name in the settings */ /* 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(); config->AnchorUnchangeableSettings();
Backup<CompanyID> cur_company(_current_company, company, FILE_LINE); Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);

View File

@ -20,6 +20,7 @@
#include "network/network_base.h" #include "network/network_base.h"
#include "network/network_admin.h" #include "network/network_admin.h"
#include "ai/ai.hpp" #include "ai/ai.hpp"
#include "ai/ai_config.hpp"
#include "company_manager_face.h" #include "company_manager_face.h"
#include "window_func.h" #include "window_func.h"
#include "strings_func.h" #include "strings_func.h"
@ -577,9 +578,10 @@ void ResetCompanyLivery(Company *c)
* *
* @param is_ai is an AI company? * @param is_ai is an AI company?
* @param company CompanyID to use for the new 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 * @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; if (!Company::CanAllocateItem()) return nullptr;
@ -625,7 +627,7 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY)
BuildOwnerLegend(); BuildOwnerLegend();
InvalidateWindowData(WC_SMALLMAP, 0, 1); 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); AI::BroadcastNewEvent(new ScriptEventCompanyNew(c->index), c->index);
Game::NewEvent(new ScriptEventCompanyNew(c->index)); Game::NewEvent(new ScriptEventCompanyNew(c->index));
@ -646,9 +648,16 @@ TimeoutTimer<TimerGameTick> _new_competitor_timeout(0, []() {
if (n >= _settings_game.difficulty.max_no_competitors) return; 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. /* Send a command to all clients to start up a new AI.
* Works fine for Multiplayer and Singleplayer */ * Works fine for Multiplayer and Singleplayer */
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, cid, CRR_NONE, INVALID_CLIENT_ID, !AIConfig::GetConfig(cid)->HasScript());
}); });
/** Start of a new game. */ /** 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 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) { if (timeout == 0) {
/* count number of competitors */ /* count number of competitors */
uint8_t n = 0; uint8_t n_ais = 0;
for (const Company *cc : Company::Iterate()) { 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++) { for (auto i = 0; i < _settings_game.difficulty.max_no_competitors; i++) {
if (_networking && Company::GetNumItems() >= _settings_client.network.max_companies) break; auto count = CountBits(ais_to_start);
if (n++ >= _settings_game.difficulty.max_no_competitors) break; if (_networking && current_companies + count >= _settings_client.network.max_companies) break;
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); 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<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, (CompanyID)company, CRR_NONE, INVALID_CLIENT_ID, !AIConfig::GetConfig((CompanyID)company)->HasScript());
} }
timeout = 10 * 60 * Ticks::TICKS_PER_SECOND; timeout = 10 * 60 * Ticks::TICKS_PER_SECOND;
} }
@ -857,7 +887,7 @@ void CompanyAdminRemove(CompanyID company_id, CompanyRemoveReason reason)
* @param client_id ClientID * @param client_id ClientID
* @return the cost of this operation or an error * @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); InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0);
@ -921,7 +951,7 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID
/* For network game, just assume deletion happened. */ /* For network game, just assume deletion happened. */
assert(company_id == INVALID_COMPANY || !Company::IsValidID(company_id)); 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); if (c != nullptr) NetworkServerNewCompany(c, nullptr);
break; break;
} }

View File

@ -17,7 +17,7 @@
enum ClientID : uint32_t; enum ClientID : uint32_t;
enum Colours : byte; 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 CmdGiveMoney(DoCommandFlag flags, Money money, CompanyID dest_company);
CommandCost CmdRenameCompany(DoCommandFlag flags, const std::string &text); CommandCost CmdRenameCompany(DoCommandFlag flags, const std::string &text);
CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text); CommandCost CmdRenamePresident(DoCommandFlag flags, const std::string &text);

View File

@ -1015,7 +1015,7 @@ DEF_CONSOLE_CMD(ConResetCompany)
} }
/* It is safe to remove this company */ /* It is safe to remove this company */
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, index, CRR_MANUAL, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, index, CRR_MANUAL, INVALID_CLIENT_ID, false);
IConsolePrint(CC_DEFAULT, "Company deleted."); IConsolePrint(CC_DEFAULT, "Company deleted.");
return true; return true;
@ -1350,16 +1350,20 @@ DEF_CONSOLE_CMD(ConStartAI)
return true; return true;
} }
int n = 0; CompanyID cid = COMPANY_FIRST;
/* Find the next free slot */ /* Find the next free slot */
for (const Company *c : Company::Iterate()) { for (const Company *c : Company::Iterate()) {
if (c->index != n) break; if (c->index != cid) break;
n++; cid++;
} }
AIConfig *config = AIConfig::GetConfig((CompanyID)n); AIConfig *config = AIConfig::GetConfig(cid);
bool deviate = argc == 1 ? !config->HasScript() : argc == 2;
if (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, /* 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 * 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]; size_t name_length = e - argv[1];
e++; e++;
int version = atoi(e); version = atoi(e);
config->Change(std::string(argv[1], name_length), version, true); 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 */ /* Start a new AI company */
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, cid, CRR_NONE, INVALID_CLIENT_ID, deviate);
return true; return true;
} }
@ -1420,9 +1426,11 @@ DEF_CONSOLE_CMD(ConReloadAI)
return true; 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 */ /* First kill the company of the AI, then start a new one. This should start the current AI again */
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID, false);
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, company_id, CRR_NONE, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, company_id, CRR_NONE, INVALID_CLIENT_ID, deviate);
IConsolePrint(CC_DEFAULT, "AI reloaded."); IConsolePrint(CC_DEFAULT, "AI reloaded.");
return true; return true;
@ -1459,7 +1467,7 @@ DEF_CONSOLE_CMD(ConStopAI)
} }
/* Now kill the company of the AI. */ /* Now kill the company of the AI. */
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID, false);
IConsolePrint(CC_DEFAULT, "AI stopped, company deleted."); IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
return true; return true;

View File

@ -645,7 +645,7 @@ static void CompanyCheckBankrupt(Company *c)
* player we are sure (the above check) that we are not the local * player we are sure (the above check) that we are not the local
* company and thus we won't be moved. */ * company and thus we won't be moved. */
if (!_networking || _network_server) { if (!_networking || _network_server) {
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_BANKRUPT, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_BANKRUPT, INVALID_CLIENT_ID, false);
return; return;
} }
break; break;

View File

@ -31,8 +31,9 @@ public:
/** /**
* Start up a new GameScript. * 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. * Uninitialize the Game system.

View File

@ -23,8 +23,8 @@ public:
ScriptConfig() ScriptConfig()
{} {}
GameConfig(const GameConfig *config) : GameConfig(const GameConfig *config, bool add_random_deviation) :
ScriptConfig(config) ScriptConfig(config, add_random_deviation)
{} {}
class GameInfo *GetInfo() const; class GameInfo *GetInfo() const;

View File

@ -69,7 +69,7 @@
} }
} }
/* static */ void Game::StartNew() /* static */ void Game::StartNew(bool deviation)
{ {
if (Game::instance != nullptr) return; if (Game::instance != nullptr) return;
@ -83,6 +83,7 @@
GameInfo *info = config->GetInfo(); GameInfo *info = config->GetInfo();
if (info == nullptr) return; if (info == nullptr) return;
if (deviation) config->AddRandomDeviation();
config->AnchorUnchangeableSettings(); config->AnchorUnchangeableSettings();
Backup<CompanyID> cur_company(_current_company, FILE_LINE); Backup<CompanyID> cur_company(_current_company, FILE_LINE);

View File

@ -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++) { for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
_settings_game.ai_config[c] = nullptr; _settings_game.ai_config[c] = nullptr;
if (_settings_newgame.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; _settings_game.game_config = nullptr;
if (_settings_newgame.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. */ if (_switch_mode == SM_RESTARTGAME) {
for (auto &ai_config : _settings_game.ai_config) { /* Simulate random deviation for scripts to keep the randomizer counters in sync, but don't set the deviated values. */
if (ai_config != nullptr) ai_config->AddRandomDeviation(); 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();
} }

View File

@ -894,7 +894,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
Debug(net, 9, "Client::join_status = REGISTERING"); Debug(net, 9, "Client::join_status = REGISTERING");
_network_join_status = NETWORK_JOIN_STATUS_REGISTERING; _network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
ShowJoinStatusWindow(); ShowJoinStatusWindow();
Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID, false);
} }
} else { } else {
/* take control over an existing company */ /* take control over an existing company */

View File

@ -1346,7 +1346,7 @@ static void AdminCompanyResetCallback(Window *, bool confirmed)
{ {
if (confirmed) { if (confirmed) {
if (NetworkCompanyHasClients(_admin_company_id)) return; if (NetworkCompanyHasClients(_admin_company_id)) return;
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, _admin_company_id, CRR_MANUAL, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::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) static void OnClickCompanyNew([[maybe_unused]] NetworkClientListWindow *w, [[maybe_unused]] Point pt, CompanyID)
{ {
if (_network_server) { if (_network_server) {
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW, INVALID_COMPANY, CRR_NONE, _network_own_client_id); Command<CMD_COMPANY_CTRL>::Post(CCA_NEW, INVALID_COMPANY, CRR_NONE, _network_own_client_id, false);
} else { } else {
Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID, false);
} }
} }

View File

@ -1590,7 +1590,7 @@ static void NetworkAutoCleanCompanies()
/* Is the company empty for autoclean_unprotected-months, and is there no protection? */ /* 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()) { 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 */ /* Shut the company down */
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID, false);
IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no password.", c->index + 1); 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? */ /* 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? */ /* 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)) { 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 */ /* Shut the company down */
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID, false);
IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no vehicles.", c->index + 1); IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no vehicles.", c->index + 1);
} }
} else { } else {

View File

@ -96,7 +96,7 @@ void CallWindowGameTickEvent();
bool HandleBootstrap(); bool HandleBootstrap();
extern void AfterLoadCompanyStats(); 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 OSOpenBrowser(const std::string &url);
extern void RebuildTownCaches(); extern void RebuildTownCaches();
extern void ShowOSErrorBox(const char *buf, bool system); extern void ShowOSErrorBox(const char *buf, bool system);
@ -361,12 +361,12 @@ void MakeNewgameSettingsLive()
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
_settings_game.ai_config[c] = nullptr; _settings_game.ai_config[c] = nullptr;
if (_settings_newgame.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; _settings_game.game_config = nullptr;
if (_settings_newgame.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);
} }
} }

View File

@ -69,7 +69,7 @@
#include "../safeguards.h" #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. * Makes a tile canal or water depending on the surroundings.
@ -549,11 +549,11 @@ static void StartScripts()
/* Start the AIs. */ /* Start the AIs. */
for (const Company *c : Company::Iterate()) { 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. */ /* Start the GameScript. */
Game::StartNew(); Game::StartNew(false);
ShowScriptDebugWindowIfScriptError(); ShowScriptDebugWindowIfScriptError();
} }

View File

@ -87,11 +87,11 @@ struct AIPLChunkHandler : ChunkHandler {
/* A random AI. */ /* A random AI. */
config->Change(std::nullopt, -1, false, true); config->Change(std::nullopt, -1, false, true);
} else { } 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()) { if (!config->HasScript()) {
/* No version of the AI available that can load the data. Try to load the /* No version of the AI available that can load the data. Try to load the
* latest version of the AI instead. */ * 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 (!config->HasScript()) {
if (_ai_saveload_name.compare("%_dummy") != 0) { 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); Debug(script, 0, "The savegame has an AI by the name '{}', version {} which is no longer available.", _ai_saveload_name, _ai_saveload_version);

View File

@ -77,11 +77,11 @@ struct GSDTChunkHandler : ChunkHandler {
GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME); GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME);
if (!_game_saveload_name.empty()) { 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()) { if (!config->HasScript()) {
/* No version of the GameScript available that can load the data. Try to load the /* No version of the GameScript available that can load the data. Try to load the
* latest version of the GameScript instead. */ * 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 (!config->HasScript()) {
if (_game_saveload_name.compare("%_dummy") != 0) { 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); Debug(script, 0, "The savegame has an GameScript by the name '{}', version {} which is no longer available.", _game_saveload_name, _game_saveload_version);

View File

@ -228,7 +228,9 @@ public:
* actual value of the setting in game will be randomised in the range * actual value of the setting in game will be randomised in the range
* [user_configured_value - random_deviation, user_configured_value + random_deviation] (inclusive). * [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). * 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. * Not allowed if the CONFIG_BOOLEAN flag is set, otherwise optional.
* - step_size The increase/decrease of the value every time the user * - step_size The increase/decrease of the value every time the user
* clicks one of the up/down arrow buttons. Optional, default is 1. * clicks one of the up/down arrow buttons. Optional, default is 1.

View File

@ -18,7 +18,7 @@
#include "../safeguards.h" #include "../safeguards.h"
void ScriptConfig::Change(std::optional<const std::string> name, int version, bool force_exact_match, bool is_random) void ScriptConfig::Change(std::optional<const std::string> name, int version, bool force_exact_match, bool is_random, bool add_random_deviation)
{ {
if (name.has_value()) { if (name.has_value()) {
this->name = std::move(name.value()); this->name = std::move(name.value());
@ -33,12 +33,12 @@ void ScriptConfig::Change(std::optional<const std::string> name, int version, bo
this->ClearConfigList(); this->ClearConfigList();
if (_game_mode == GM_NORMAL && _switch_mode != SM_LOAD_GAME && this->info != nullptr) { if (this->info != nullptr && add_random_deviation) {
this->AddRandomDeviation(); this->AddRandomDeviation();
} }
} }
ScriptConfig::ScriptConfig(const ScriptConfig *config) ScriptConfig::ScriptConfig(const ScriptConfig *config, bool add_random_deviation)
{ {
this->name = config->name; this->name = config->name;
this->info = config->info; this->info = config->info;
@ -49,6 +49,8 @@ ScriptConfig::ScriptConfig(const ScriptConfig *config)
for (const auto &item : config->settings) { for (const auto &item : config->settings) {
this->settings[item.first] = item.second; this->settings[item.first] = item.second;
} }
if (add_random_deviation) this->AddRandomDeviation();
} }
ScriptConfig::~ScriptConfig() ScriptConfig::~ScriptConfig()

View File

@ -63,8 +63,9 @@ public:
/** /**
* Create a new Script config that is a copy of an existing config. * Create a new Script config that is a copy of an existing config.
* @param config The object to copy. * @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. */ /** Delete an Script configuration. */
virtual ~ScriptConfig(); virtual ~ScriptConfig();
@ -76,8 +77,9 @@ public:
* @param force_exact_match If true try to find the exact same version * @param force_exact_match If true try to find the exact same version
* as specified. If false any compatible version is ok. * as specified. If false any compatible version is ok.
* @param is_random Is the Script chosen randomly? * @param is_random Is the Script chosen randomly?
* @param add_random_deviation Apply random deviation to settings that allow it?
*/ */
void Change(std::optional<const std::string> name, int version = -1, bool force_exact_match = false, bool is_random = false); void Change(std::optional<const std::string> 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. * Get the ScriptInfo linked to this ScriptConfig.

View File

@ -176,7 +176,7 @@ struct ScriptListWindow : public Window {
} else { } else {
ScriptInfoList::const_iterator it = this->info_list->cbegin(); ScriptInfoList::const_iterator it = this->info_list->cbegin();
std::advance(it, this->selected); 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); InvalidateWindowData(WC_GAME_OPTIONS, slot == OWNER_DEITY ? WN_GAME_OPTIONS_GS : WN_GAME_OPTIONS_AI);
InvalidateWindowClassesData(WC_SCRIPT_SETTINGS); InvalidateWindowClassesData(WC_SCRIPT_SETTINGS);
@ -1080,12 +1080,14 @@ struct ScriptDebugWindow : public Window {
ChangeToScript(OWNER_DEITY, _ctrl_pressed); ChangeToScript(OWNER_DEITY, _ctrl_pressed);
break; break;
case WID_SCRD_RELOAD_TOGGLE: case WID_SCRD_RELOAD_TOGGLE: {
if (this->filter.script_debug_company == OWNER_DEITY) break; 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 */ /* First kill the company of the AI, then start a new one. This should start the current AI again */
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, this->filter.script_debug_company, CRR_MANUAL, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, this->filter.script_debug_company, CRR_MANUAL, INVALID_CLIENT_ID, false);
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, this->filter.script_debug_company, CRR_NONE, INVALID_CLIENT_ID); Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, this->filter.script_debug_company, CRR_NONE, INVALID_CLIENT_ID, deviate);
break; break;
}
case WID_SCRD_SETTINGS: case WID_SCRD_SETTINGS:
ShowScriptSettingsWindow(this->filter.script_debug_company); ShowScriptSettingsWindow(this->filter.script_debug_company);

View File

@ -953,7 +953,7 @@ static void AILoadConfig(const IniFile &ini, const char *grpname)
for (const IniItem &item : group->items) { for (const IniItem &item : group->items) {
AIConfig *config = AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME); 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 (!config->HasScript()) {
if (item.name != "none") { if (item.name != "none") {
Debug(script, 0, "The AI by the name '{}' was no longer found, and removed from the list.", item.name); 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); GameConfig *config = GameConfig::GetConfig(AIConfig::SSS_FORCE_NEWGAME);
config->Change(item.name); config->Change(item.name, -1, false, false, false);
if (!config->HasScript()) { if (!config->HasScript()) {
if (item.name != "none") { if (item.name != "none") {
Debug(script, 0, "The GameScript by the name '{}' was no longer found, and removed from the list.", item.name); Debug(script, 0, "The GameScript by the name '{}' was no longer found, and removed from the list.", item.name);