Change: replace per-AI "start_date" with a global "competitors_interval" (#10653)

The per-AI "start_date" is a lot of custom code, and was rarely
used in the way it was meant.

While at it, also ported this part over to the new timer system.
pull/544/head
Patric Stout 1 year ago committed by GitHub
parent 43a7e54067
commit ed83c4b0da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -88,9 +88,9 @@
abs( 21): 21
--AIBase--
Rand(): 2232656694
Rand(): 2514636170
Rand(): 3897038727
Rand(): 2113409458
Rand(): 2000129769
Rand(): 1788051963
RandRange(0): 0
RandRange(0): 0
RandRange(0): 0
@ -99,13 +99,13 @@
RandRange(1): 0
RandRange(2): 0
RandRange(2): 0
RandRange(2): 0
RandRange(1000000): 666804
RandRange(1000000): 624059
RandRange(1000000): 697029
Chance(1, 2): true
RandRange(2): 1
RandRange(1000000): 338687
RandRange(1000000): 274895
RandRange(1000000): 217539
Chance(1, 2): false
Chance(1, 2): true
Chance(1, 2): false
--List--
IsEmpty(): true
@ -420,144 +420,144 @@
1098 => 46116
1099 => 46158
Randomize ListDump:
1 => 688298322
2 => 2585420314
1000 => 1701392078
1001 => 2664118875
1002 => 3408466361
1003 => 4098642324
1004 => 3858929894
1005 => 3774625512
1006 => 2809742492
1007 => 3983931060
1008 => 2791524857
1009 => 4184021601
1010 => 4212142121
1011 => 46859773
1012 => 3095744278
1013 => 3104411371
1014 => 326384434
1015 => 1486817960
1016 => 2883541699
1017 => 3786540442
1018 => 820019294
1019 => 710762995
1020 => 3534100264
1021 => 3585356150
1022 => 732190215
1023 => 236336673
1024 => 740596257
1025 => 1135321785
1026 => 2067474156
1027 => 2899283689
1028 => 4054438597
1029 => 928616892
1030 => 1712486685
1031 => 1994118287
1032 => 1333321243
1033 => 194124284
1034 => 615083294
1035 => 628086450
1036 => 498957825
1037 => 1359697121
1038 => 1888433963
1039 => 941623020
1040 => 2369304004
1041 => 3523427032
1042 => 3236625937
1043 => 182127597
1044 => 646955927
1045 => 2870345582
1046 => 623062612
1047 => 2308011710
1048 => 3026140316
1049 => 3838191076
1051 => 3182411967
1052 => 2762833244
1053 => 1960404034
1054 => 1573325453
1055 => 3978347993
1056 => 699712177
1057 => 863274966
1058 => 1728276475
1059 => 4048271407
1060 => 1919485436
1061 => 111273464
1062 => 125435213
1063 => 155132602
1064 => 4123293220
1065 => 655046914
1066 => 1577399562
1067 => 1028818150
1068 => 447058239
1069 => 3237047027
1070 => 2968751973
1071 => 4096278708
1072 => 1523643051
1073 => 231373233
1074 => 1121759962
1075 => 1449439846
1076 => 2679696543
1077 => 2785673432
1078 => 2116903943
1079 => 672822173
1080 => 3325393385
1081 => 1589904755
1082 => 1148782015
1083 => 663503316
1084 => 933352745
1085 => 577717039
1086 => 402172048
1087 => 1812250453
1088 => 667300501
1089 => 2456141519
1090 => 3438492520
1091 => 420696035
1092 => 2131427774
1093 => 3859663748
1094 => 4134083418
1095 => 1969629634
1096 => 3739173141
1097 => 3459847605
1098 => 2834059387
1099 => 3148043212
1 => 1667006376
2 => 814756458
1000 => 2792131700
1001 => 3417650573
1002 => 1856129988
1003 => 1800973341
1004 => 4197962148
1005 => 2463509731
1006 => 2312121797
1007 => 1357932132
1008 => 1603755907
1009 => 1718096015
1010 => 3850074449
1011 => 2711130211
1012 => 2371249199
1013 => 881020769
1014 => 3366660077
1015 => 808768948
1016 => 3035331984
1017 => 2813590961
1018 => 2745021820
1019 => 3075151719
1020 => 2553774560
1021 => 4267762096
1022 => 3863175846
1023 => 4198397908
1024 => 817599906
1025 => 3149240362
1026 => 3003005979
1027 => 1214815375
1028 => 3784363817
1029 => 3181864540
1030 => 325341059
1031 => 1011889231
1032 => 3142617173
1033 => 1197220206
1034 => 4060510885
1035 => 3596342467
1036 => 219406671
1037 => 3695508783
1038 => 2823603997
1039 => 2625659720
1040 => 4113498476
1041 => 1125297786
1042 => 671905104
1043 => 1231077134
1044 => 892292375
1045 => 2441486929
1046 => 1804593432
1047 => 2536560053
1048 => 1896826021
1049 => 1672512966
1051 => 977884299
1052 => 681948608
1053 => 3853505792
1054 => 4118706553
1055 => 3581698138
1056 => 3073782502
1057 => 1084753140
1058 => 2266056077
1059 => 1239805090
1060 => 1183528423
1061 => 501361238
1062 => 66542127
1063 => 775638990
1064 => 1111474321
1065 => 3465462871
1066 => 2317535037
1067 => 878310882
1068 => 2231368582
1069 => 2353633007
1070 => 179259867
1071 => 1322707275
1072 => 1474105363
1073 => 619989187
1074 => 3221603092
1075 => 2400416540
1076 => 3926392705
1077 => 1122978123
1078 => 3266139701
1079 => 2948697341
1080 => 3262493501
1081 => 2200252596
1082 => 4091101485
1083 => 2797438343
1084 => 2608201933
1085 => 2577605442
1086 => 1178956760
1087 => 3047709109
1088 => 1065186815
1089 => 841440515
1090 => 842182476
1091 => 289059855
1092 => 2114106829
1093 => 436435334
1094 => 111052607
1095 => 81827083
1096 => 1961213887
1097 => 1374385392
1098 => 3255118186
1099 => 2245402931
KeepTop(10):
1 => 688298322
2 => 2585420314
1000 => 1701392078
1001 => 2664118875
1002 => 3408466361
1003 => 4098642324
1004 => 3858929894
1005 => 3774625512
1006 => 2809742492
1007 => 3983931060
1 => 1667006376
2 => 814756458
1000 => 2792131700
1001 => 3417650573
1002 => 1856129988
1003 => 1800973341
1004 => 4197962148
1005 => 2463509731
1006 => 2312121797
1007 => 1357932132
KeepBottom(8):
1000 => 1701392078
1001 => 2664118875
1002 => 3408466361
1003 => 4098642324
1004 => 3858929894
1005 => 3774625512
1006 => 2809742492
1007 => 3983931060
1000 => 2792131700
1001 => 3417650573
1002 => 1856129988
1003 => 1800973341
1004 => 4197962148
1005 => 2463509731
1006 => 2312121797
1007 => 1357932132
RemoveBottom(2):
1000 => 1701392078
1001 => 2664118875
1002 => 3408466361
1003 => 4098642324
1004 => 3858929894
1005 => 3774625512
1000 => 2792131700
1001 => 3417650573
1002 => 1856129988
1003 => 1800973341
1004 => 4197962148
1005 => 2463509731
RemoveTop(2):
1002 => 3408466361
1003 => 4098642324
1004 => 3858929894
1005 => 3774625512
1002 => 1856129988
1003 => 1800973341
1004 => 4197962148
1005 => 2463509731
RemoveList({1003, 1004}):
1002 => 3408466361
1005 => 3774625512
1002 => 1856129988
1005 => 2463509731
KeepList({1003, 1004, 1005}):
1005 => 3774625512
1005 => 2463509731
AddList({1005, 4000, 4001, 4002}):
1005 => 1005
4000 => 8000
@ -588,7 +588,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
SetName(): false
GetLastErrorString(): ERR_NAME_IS_NOT_UNIQUE
GetName(): Regression
GetPresidentName(): J. Green
GetPresidentName(): F. Gribble
SetPresidentName(): true
GetPresidentName(): Regression AI
GetBankBalance(): 100000
@ -9320,12 +9320,12 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetLocation(): 33417
GetEngineType(): 153
GetUnitNumber(): 1
GetAge(): 0
GetAge(): 1
GetMaxAge(): 5490
GetAgeLeft(): 5490
GetAgeLeft(): 5489
GetCurrentSpeed(): 7
GetRunningCost(): 421
GetProfitThisYear(): 0
GetProfitThisYear(): -1
GetProfitLastYear(): 0
GetCurrentValue(): 5947
GetVehicleType(): 1
@ -9335,7 +9335,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsInDepot(): false
GetNumWagons(): 1
GetWagonEngineType(): 153
GetWagonAge(): 0
GetWagonAge(): 1
GetLength(): 8
GetOwner(): 1
BuildVehicle(): 14
@ -9408,11 +9408,11 @@ ERROR: IsEnd() is invalid as Begin() is never called
14 => 1
12 => 1
Age ListDump:
17 => 1
16 => 1
14 => 1
13 => 1
12 => 1
17 => 0
16 => 0
MaxAge ListDump:
16 => 10980
14 => 10980
@ -9420,9 +9420,9 @@ ERROR: IsEnd() is invalid as Begin() is never called
13 => 5490
12 => 5490
AgeLeft ListDump:
16 => 10979
16 => 10980
14 => 10979
17 => 7319
17 => 7320
13 => 5489
12 => 5489
CurrentSpeed ListDump:

Binary file not shown.

@ -19,18 +19,6 @@
*/
class AI {
public:
/**
* The default months AIs start after each other.
*/
enum StartNext {
START_NEXT_EASY = DAYS_IN_YEAR * 2,
START_NEXT_MEDIUM = DAYS_IN_YEAR,
START_NEXT_HARD = DAYS_IN_YEAR / 2,
START_NEXT_MIN = 0,
START_NEXT_MAX = 3600,
START_NEXT_DEVIATION = 60,
};
/**
* Is it possible to start a new AI company?
* @return True if a new AI company can be started.
@ -124,11 +112,6 @@ public:
*/
static void Save(CompanyID company);
/**
* Get the number of days before the next AI should start.
*/
static int GetStartNextTime();
/** Wrapper function for AIScanner::GetAIConsoleList */
static std::string GetConsoleList(bool newest_only = false);
/** Wrapper function for AIScanner::GetAIConsoleLibraryList */

@ -16,32 +16,6 @@
#include "../safeguards.h"
/** Configuration for AI start date, every AI has this setting. */
ScriptConfigItem _start_date_config = {
"start_date",
"", // STR_AI_SETTINGS_START_DELAY
AI::START_NEXT_MIN,
AI::START_NEXT_MAX,
AI::START_NEXT_MEDIUM,
AI::START_NEXT_EASY,
AI::START_NEXT_MEDIUM,
AI::START_NEXT_HARD,
AI::START_NEXT_DEVIATION,
30,
SCRIPTCONFIG_NONE,
nullptr,
false
};
AIConfig::AIConfig(const AIConfig *config) : ScriptConfig(config)
{
/* Override start_date as per AIConfig::AddRandomDeviation().
* This is necessary because the ScriptConfig constructor will instead call
* ScriptConfig::AddRandomDeviation(). */
int start_date = config->GetSetting("start_date");
this->SetSetting("start_date", start_date != 0 ? std::max(1, this->GetSetting("start_date")) : 0);
}
/* static */ AIConfig *AIConfig::GetConfig(CompanyID company, ScriptSettingSource source)
{
AIConfig **config;
@ -69,70 +43,3 @@ bool AIConfig::ResetInfo(bool force_exact_match)
this->info = (ScriptInfo *)AI::FindInfo(this->name, force_exact_match ? this->version : -1, force_exact_match);
return this->info != nullptr;
}
void AIConfig::PushExtraConfigList()
{
this->config_list->push_back(_start_date_config);
}
void AIConfig::ClearConfigList()
{
/* The special casing for start_date is here to ensure that the
* start_date setting won't change even if you chose another Script. */
int start_date = this->GetSetting("start_date");
ScriptConfig::ClearConfigList();
this->SetSetting("start_date", start_date);
}
int AIConfig::GetSetting(const char *name) const
{
if (this->info == nullptr) {
SettingValueList::const_iterator it = this->settings.find(name);
if (it == this->settings.end()) {
assert(strcmp("start_date", name) == 0);
switch (GetGameSettings().script.settings_profile) {
case SP_EASY: return AI::START_NEXT_EASY;
case SP_MEDIUM: return AI::START_NEXT_MEDIUM;
case SP_HARD: return AI::START_NEXT_HARD;
case SP_CUSTOM: return AI::START_NEXT_MEDIUM;
default: NOT_REACHED();
}
}
return (*it).second;
}
return ScriptConfig::GetSetting(name);
}
void AIConfig::SetSetting(const char *name, int value)
{
if (this->info == nullptr) {
if (strcmp("start_date", name) != 0) return;
value = Clamp(value, AI::START_NEXT_MIN, AI::START_NEXT_MAX);
SettingValueList::iterator it = this->settings.find(name);
if (it != this->settings.end()) {
(*it).second = value;
} else {
this->settings[stredup(name)] = value;
}
return;
}
ScriptConfig::SetSetting(name, value);
}
void AIConfig::AddRandomDeviation()
{
int start_date = this->GetSetting("start_date");
ScriptConfig::AddRandomDeviation();
/* start_date = 0 is a special case, where random deviation does not occur.
* If start_date was not already 0, then a minimum value of 1 must apply. */
this->SetSetting("start_date", start_date != 0 ? std::max(1, this->GetSetting("start_date")) : 0);
}

@ -24,14 +24,12 @@ public:
ScriptConfig()
{}
AIConfig(const AIConfig *config);
AIConfig(const AIConfig *config) :
ScriptConfig(config)
{}
class AIInfo *GetInfo() const;
int GetSetting(const char *name) const override;
void SetSetting(const char *name, int value) override;
void AddRandomDeviation() override;
/**
* When ever the AI Scanner is reloaded, all infos become invalid. This
* function tells AIConfig about this.
@ -43,8 +41,6 @@ public:
bool ResetInfo(bool force_exact_match);
protected:
void PushExtraConfigList() override;
void ClearConfigList() override;
ScriptInfo *FindInfo(const char *name, int version, bool force_exact_match) override;
};

@ -289,17 +289,6 @@
}
}
/* static */ int AI::GetStartNextTime()
{
/* Find the first company which doesn't exist yet */
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
if (!Company::IsValidID(c)) return AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->GetSetting("start_date");
}
/* Currently no AI can be started, check again in a year. */
return DAYS_IN_YEAR;
}
/* static */ std::string AI::GetConsoleList(bool newest_only)
{
return AI::scanner_info->GetConsoleList(newest_only);

@ -35,11 +35,17 @@ static const NWidgetPart _nested_ai_config_widgets[] = {
NWidget(WWT_PANEL, COLOUR_MAUVE, WID_AIC_BACKGROUND),
NWidget(NWID_VERTICAL), SetPIP(4, 4, 4),
NWidget(NWID_HORIZONTAL), SetPIP(7, 0, 7),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE), SetDataTip(AWV_DECREASE, STR_NULL),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE), SetDataTip(AWV_INCREASE, STR_NULL),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_NUMBER), SetDataTip(AWV_DECREASE, STR_NULL),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_NUMBER), SetDataTip(AWV_INCREASE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(6, 0),
NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_NUMBER), SetDataTip(STR_AI_CONFIG_MAX_COMPETITORS, STR_NULL), SetFill(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(7, 0, 7),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_INTERVAL), SetDataTip(AWV_DECREASE, STR_NULL),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_INTERVAL), SetDataTip(AWV_INCREASE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(6, 0),
NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_INTERVAL), SetDataTip(STR_AI_CONFIG_COMPETITORS_INTERVAL, STR_NULL), SetFill(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_UP), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_AI_CONFIG_MOVE_UP, STR_AI_CONFIG_MOVE_UP_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_DOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_AI_CONFIG_MOVE_DOWN, STR_AI_CONFIG_MOVE_DOWN_TOOLTIP),
@ -106,14 +112,20 @@ struct AIConfigWindow : public Window {
case WID_AIC_NUMBER:
SetDParam(0, GetGameSettings().difficulty.max_no_competitors);
break;
case WID_AIC_INTERVAL:
SetDParam(0, GetGameSettings().difficulty.competitors_interval);
break;
}
}
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
switch (widget) {
case WID_AIC_DECREASE:
case WID_AIC_INCREASE:
case WID_AIC_DECREASE_NUMBER:
case WID_AIC_INCREASE_NUMBER:
case WID_AIC_DECREASE_INTERVAL:
case WID_AIC_INCREASE_INTERVAL:
*size = maxdim(*size, NWidgetScrollbar::GetHorizontalDimension());
break;
@ -179,10 +191,10 @@ struct AIConfigWindow : public Window {
}
switch (widget) {
case WID_AIC_DECREASE:
case WID_AIC_INCREASE: {
case WID_AIC_DECREASE_NUMBER:
case WID_AIC_INCREASE_NUMBER: {
int new_value;
if (widget == WID_AIC_DECREASE) {
if (widget == WID_AIC_DECREASE_NUMBER) {
new_value = std::max(0, GetGameSettings().difficulty.max_no_competitors - 1);
} else {
new_value = std::min(MAX_COMPANIES - 1, GetGameSettings().difficulty.max_no_competitors + 1);
@ -191,6 +203,18 @@ struct AIConfigWindow : public Window {
break;
}
case WID_AIC_DECREASE_INTERVAL:
case WID_AIC_INCREASE_INTERVAL: {
int new_value;
if (widget == WID_AIC_DECREASE_INTERVAL) {
new_value = std::max(static_cast<int>(MIN_COMPETITORS_INTERVAL), GetGameSettings().difficulty.competitors_interval - 1);
} else {
new_value = std::min(static_cast<int>(MAX_COMPETITORS_INTERVAL), GetGameSettings().difficulty.competitors_interval + 1);
}
IConsoleSetSetting("difficulty.competitors_interval", new_value);
break;
}
case WID_AIC_LIST: { // Select a slot
this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget);
this->InvalidateData();
@ -251,8 +275,10 @@ struct AIConfigWindow : public Window {
if (!gui_scope) return;
this->SetWidgetDisabledState(WID_AIC_DECREASE, GetGameSettings().difficulty.max_no_competitors == 0);
this->SetWidgetDisabledState(WID_AIC_INCREASE, GetGameSettings().difficulty.max_no_competitors == MAX_COMPANIES - 1);
this->SetWidgetDisabledState(WID_AIC_DECREASE_NUMBER, GetGameSettings().difficulty.max_no_competitors == 0);
this->SetWidgetDisabledState(WID_AIC_INCREASE_NUMBER, GetGameSettings().difficulty.max_no_competitors == MAX_COMPANIES - 1);
this->SetWidgetDisabledState(WID_AIC_DECREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MIN_COMPETITORS_INTERVAL);
this->SetWidgetDisabledState(WID_AIC_INCREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MAX_COMPETITORS_INTERVAL);
this->SetWidgetDisabledState(WID_AIC_CHANGE, this->selected_slot == INVALID_COMPANY);
this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == INVALID_COMPANY || AIConfig::GetConfig(this->selected_slot)->GetConfigList()->size() == 0);
this->SetWidgetDisabledState(WID_AIC_MOVE_UP, this->selected_slot == INVALID_COMPANY || !IsEditable((CompanyID)(this->selected_slot - 1)));

@ -69,11 +69,6 @@ template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
SQInteger res = ScriptInfo::Constructor(vm, info);
if (res != 0) return res;
ScriptConfigItem config = _start_date_config;
config.name = stredup(config.name);
config.description = stredup(config.description);
info->config_list.push_front(config);
if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) {
if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
} else {

@ -170,7 +170,6 @@ struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> {
Money CalculateCompanyValue(const Company *c, bool including_loan = true);
Money CalculateCompanyValueExcludingShares(const Company *c, bool including_loan = true);
extern uint _next_competitor_start;
extern uint _cur_company_tick_index;
#endif /* COMPANY_BASE_H */

@ -38,6 +38,7 @@
#include "company_cmd.h"
#include "timer/timer.h"
#include "timer/timer_game_calendar.h"
#include "timer/timer_game_tick.h"
#include "table/strings.h"
@ -50,7 +51,6 @@ CompanyID _local_company; ///< Company controlled by the human player at this
CompanyID _current_company; ///< Company currently doing an action.
Colours _company_colours[MAX_COMPANIES]; ///< NOSAVE: can be determined from company structs.
CompanyManagerFace _company_manager_face; ///< for company manager face storage in openttd.cfg
uint _next_competitor_start; ///< the number of ticks before the next AI is started
uint _cur_company_tick_index; ///< used to generate a name for one company that doesn't have a name yet per tick
CompanyPool _company_pool("Company"); ///< Pool of companies.
@ -599,16 +599,10 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY)
return c;
}
/** Start the next competitor now. */
void StartupCompanies()
{
_next_competitor_start = 0;
}
/** Start a new competitor company if possible. */
static bool MaybeStartNewCompany()
{
if (_networking && Company::GetNumItems() >= _settings_client.network.max_companies) return false;
TimeoutTimer<TimerGameTick> _new_competitor_timeout(0, []() {
if (_game_mode == GM_MENU || !AI::CanStartNew()) return;
if (_networking && Company::GetNumItems() >= _settings_client.network.max_companies) return;
/* count number of competitors */
uint n = 0;
@ -616,13 +610,26 @@ static bool MaybeStartNewCompany()
if (c->is_ai) n++;
}
if (n < (uint)_settings_game.difficulty.max_no_competitors) {
/* Send a command to all clients to start up a new AI.
* Works fine for Multiplayer and Singleplayer */
return Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID );
}
if (n >= (uint)_settings_game.difficulty.max_no_competitors) return;
return false;
/* Send a command to all clients to start up a new AI.
* Works fine for Multiplayer and Singleplayer */
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID);
});
/** Start of a new game. */
void StartupCompanies()
{
/* Ensure the timeout is aborted, so it doesn't fire based on information of the last game. */
_new_competitor_timeout.Abort();
/* If there is no delay till the start of the next competitor, start all competitors at the start of the game. */
if (_settings_game.difficulty.competitors_interval == 0 && _game_mode != GM_MENU && AI::CanStartNew()) {
for (auto i = 0; i < _settings_game.difficulty.max_no_competitors; i++) {
if (_networking && Company::GetNumItems() >= _settings_client.network.max_companies) break;
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID);
}
}
}
/** Initialize the pool of companies. */
@ -722,20 +729,15 @@ void OnTick_Companies()
if (c->bankrupt_asked != 0) HandleBankruptcyTakeover(c);
}
if (_next_competitor_start == 0) {
/* AI::GetStartNextTime() can return 0. */
_next_competitor_start = std::max(1, AI::GetStartNextTime() * DAY_TICKS);
}
if (_new_competitor_timeout.HasFired() && _game_mode != GM_MENU && AI::CanStartNew()) {
int32 timeout = _settings_game.difficulty.competitors_interval * 60 * TICKS_PER_SECOND;
/* If the interval is zero, check every ~10 minutes if a company went bankrupt and needs replacing. */
if (timeout == 0) timeout = 10 * 60 * TICKS_PER_SECOND;
if (_game_mode != GM_MENU && AI::CanStartNew() && --_next_competitor_start == 0) {
/* Allow multiple AIs to possibly start in the same tick. */
do {
if (!MaybeStartNewCompany()) break;
/* Randomize a bit when the AI is actually going to start; ranges from 87.5% .. 112.5% of indicated value. */
timeout += ScriptObject::GetRandomizer(OWNER_NONE).Next(timeout / 4) - timeout / 8;
/* In networking mode, we can only send a command to start but it
* didn't execute yet, so we cannot loop. */
if (_networking) break;
} while (AI::GetStartNextTime() == 0);
_new_competitor_timeout.Reset(std::max(1, timeout));
}
_cur_company_tick_index = (_cur_company_tick_index + 1) % MAX_COMPANIES;

@ -42,6 +42,9 @@ static const uint MAX_LENGTH_COMPANY_NAME_CHARS = 32; ///< The maximum length
static const uint MAX_HISTORY_QUARTERS = 24; ///< The maximum number of quarters kept as performance's history
static const uint MAX_COMPANY_SHARE_OWNERS = 4; ///< The maximum number of shares of a company that can be owned by another company.
static const uint MIN_COMPETITORS_INTERVAL = 0; ///< The minimum interval (in minutes) between competitors.
static const uint MAX_COMPETITORS_INTERVAL = 500; ///< The maximum interval (in minutes) between competitors.
/** Define basic enum properties */
template <> struct EnumPropsT<Owner> : MakeEnumPropsT<Owner, byte, OWNER_BEGIN, OWNER_END, INVALID_OWNER> {};

@ -4592,6 +4592,7 @@ STR_AI_CONFIG_RANDOM_AI :Random AI
STR_AI_CONFIG_NONE :(none)
STR_AI_CONFIG_NAME_VERSION :{RAW_STRING} {YELLOW}v{NUM}
STR_AI_CONFIG_MAX_COMPETITORS :{LTBLUE}Maximum no. competitors: {ORANGE}{COMMA}
STR_AI_CONFIG_COMPETITORS_INTERVAL :{LTBLUE}Interval between starting of competitors: {ORANGE}{COMMA} minute{P "" s}
STR_AI_CONFIG_MOVE_UP :{BLACK}Move Up
STR_AI_CONFIG_MOVE_UP_TOOLTIP :{BLACK}Move selected AI up in the list
@ -4638,7 +4639,6 @@ STR_AI_SETTINGS_CAPTION_GAMESCRIPT :Game Script
STR_AI_SETTINGS_CLOSE :{BLACK}Close
STR_AI_SETTINGS_RESET :{BLACK}Reset
STR_AI_SETTINGS_SETTING :{RAW_STRING}: {ORANGE}{STRING1}
STR_AI_SETTINGS_START_DELAY :Number of days to start this AI after the previous one (give or take): {ORANGE}{STRING1}
# Textfile window

@ -70,6 +70,7 @@
#include "misc_cmd.h"
#include "timer/timer.h"
#include "timer/timer_game_calendar.h"
#include "timer/timer_game_tick.h"
#include "linkgraph/linkgraphschedule.h"
@ -1419,6 +1420,7 @@ void StateGameLoop()
BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
AnimateAnimatedTiles();
TimerManager<TimerGameCalendar>::Elapsed(1);
TimerManager<TimerGameTick>::Elapsed(1);
RunTileLoop();
CallVehicleTicks();
CallLandscapeTick();

@ -58,6 +58,8 @@
#include "../disaster_vehicle.h"
#include "../ship.h"
#include "../water.h"
#include "../timer/timer.h"
#include "../timer/timer_game_tick.h"
#include "saveload_internal.h"
@ -3259,6 +3261,16 @@ bool AfterLoadGame()
for (Station *st : Station::Iterate()) UpdateStationAcceptance(st, false);
}
if (IsSavegameVersionBefore(SLV_AI_START_DATE)) {
/* For older savegames, we don't now the actual interval; so set it to the newgame value. */
_settings_game.difficulty.competitors_interval = _settings_newgame.difficulty.competitors_interval;
/* We did load the "period" of the timer, but not the fired/elapsed. We can deduce that here. */
extern TimeoutTimer<TimerGameTick> _new_competitor_timeout;
_new_competitor_timeout.storage.elapsed = 0;
_new_competitor_timeout.fired = _new_competitor_timeout.period == 0;
}
AfterLoadLabelMaps();
AfterLoadCompanyStats();
AfterLoadStoryBook();

@ -20,6 +20,8 @@
#include "../gfx_func.h"
#include "../core/random_func.hpp"
#include "../fios.h"
#include "../timer/timer.h"
#include "../timer/timer_game_tick.h"
#include "../safeguards.h"
@ -67,6 +69,7 @@ void ResetViewportAfterLoadGame()
}
byte _age_cargo_skip_counter; ///< Skip aging of cargo? Used before savegame version 162.
extern TimeoutTimer<TimerGameTick> _new_competitor_timeout;
static const SaveLoad _date_desc[] = {
SLEG_CONDVAR("date", _date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
@ -81,10 +84,14 @@ static const SaveLoad _date_desc[] = {
SLEG_VAR("random_state[0]", _random.state[0], SLE_UINT32),
SLEG_VAR("random_state[1]", _random.state[1], SLE_UINT32),
SLEG_VAR("company_tick_counter", _cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32),
SLEG_CONDVAR("next_competitor_start", _next_competitor_start, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109),
SLEG_CONDVAR("next_competitor_start", _next_competitor_start, SLE_UINT32, SLV_109, SL_MAX_VERSION),
SLEG_VAR("trees_tick_counter", _trees_tick_ctr, SLE_UINT8),
SLEG_CONDVAR("pause_mode", _pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION),
/* For older savegames, we load the current value as the "period"; afterload will set the "fired" and "elapsed". */
SLEG_CONDVAR("next_competitor_start", _new_competitor_timeout.period, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109),
SLEG_CONDVAR("next_competitor_start", _new_competitor_timeout.period, SLE_UINT32, SLV_109, SLV_AI_START_DATE),
SLEG_CONDVAR("competitors_interval", _new_competitor_timeout.period, SLE_UINT32, SLV_AI_START_DATE, SL_MAX_VERSION),
SLEG_CONDVAR("competitors_interval_elapsed", _new_competitor_timeout.storage.elapsed, SLE_UINT32, SLV_AI_START_DATE, SL_MAX_VERSION),
SLEG_CONDVAR("competitors_interval_fired", _new_competitor_timeout.fired, SLE_BOOL, SLV_AI_START_DATE, SL_MAX_VERSION),
};
static const SaveLoad _date_check_desc[] = {

@ -27,6 +27,8 @@
#include "../company_base.h"
#include "../disaster_vehicle.h"
#include "../core/smallvec_type.hpp"
#include "../timer/timer.h"
#include "../timer/timer_game_tick.h"
#include "saveload_internal.h"
#include "oldloader.h"
#include <array>
@ -489,6 +491,7 @@ static inline uint RemapOrderIndex(uint x)
}
extern std::vector<TileIndex> _animated_tiles;
extern TimeoutTimer<TimerGameTick> _new_competitor_timeout;
extern char *_old_name_array;
static uint32 _old_town_index;
@ -1677,7 +1680,7 @@ static const OldChunks main_chunk[] = {
OCL_ASSERT( OC_TTO, 0x496CE ),
OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_next_competitor_start ),
OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_new_competitor_timeout.period ),
OCL_CNULL( OC_TTO, 2 ), ///< available monorail bitmask

@ -351,6 +351,7 @@ enum SaveLoadVersion : uint16 {
SLV_CONSISTENT_PARTIAL_Z, ///< 306 PR#10570 Conversion from an inconsistent partial Z calculation for slopes, to one that is (more) consistent.
SLV_MORE_CARGO_AGE, ///< 307 PR#10596 Track cargo age for a longer period.
SLV_LINKGRAPH_SECONDS, ///< 308 PR#10610 Store linkgraph update intervals in seconds instead of days.
SLV_AI_START_DATE, ///< 309 PR#10653 Removal of individual AI start dates and added a generic one.
SL_MAX_VERSION, ///< Highest possible saveload version
};

@ -26,7 +26,6 @@ void ScriptConfig::Change(const char *name, int version, bool force_exact_match,
this->is_random = is_random;
if (this->config_list != nullptr) delete this->config_list;
this->config_list = (info == nullptr) ? nullptr : new ScriptConfigItemList();
if (this->config_list != nullptr) this->PushExtraConfigList();
this->to_load_data.reset();
this->ClearConfigList();
@ -79,7 +78,6 @@ const ScriptConfigItemList *ScriptConfig::GetConfigList()
if (this->info != nullptr) return this->info->GetConfigList();
if (this->config_list == nullptr) {
this->config_list = new ScriptConfigItemList();
this->PushExtraConfigList();
}
return this->config_list;
}

@ -51,8 +51,6 @@ struct ScriptConfigItem {
typedef std::list<ScriptConfigItem> ScriptConfigItemList; ///< List of ScriptConfig items.
extern ScriptConfigItem _start_date_config;
/**
* Script settings.
*/
@ -127,12 +125,12 @@ public:
* @return The (default) value of the setting, or -1 if the setting was not
* found.
*/
virtual int GetSetting(const char *name) const;
int GetSetting(const char *name) const;
/**
* Set the value of a setting for this config.
*/
virtual void SetSetting(const char *name, int value);
void SetSetting(const char *name, int value);
/**
* Reset all settings to their default value.
@ -147,7 +145,7 @@ public:
/**
* Randomize all settings the Script requested to be randomized.
*/
virtual void AddRandomDeviation();
void AddRandomDeviation();
/**
* Is this config attached to an Script? In other words, is there a Script
@ -202,16 +200,10 @@ protected:
bool is_random; ///< True if the AI in this slot was randomly chosen.
std::unique_ptr<ScriptInstance::ScriptData> to_load_data; ///< Data to load after the Script start.
/**
* In case you have mandatory non-Script-definable config entries in your
* list, add them to this function.
*/
virtual void PushExtraConfigList() {};
/**
* Routine that clears the config list.
*/
virtual void ClearConfigList();
void ClearConfigList();
/**
* This function should call back to the Scanner in charge of this Config,

@ -379,14 +379,8 @@ struct ScriptSettingsWindow : public Window {
TextColour colour;
uint idx = 0;
if (StrEmpty(config_item.description)) {
if (this->slot != OWNER_DEITY && !strcmp(config_item.name, "start_date")) {
/* Build-in translation */
str = STR_AI_SETTINGS_START_DELAY;
colour = TC_LIGHT_BLUE;
} else {
str = STR_JUST_STRING;
colour = TC_ORANGE;
}
str = STR_JUST_STRING;
colour = TC_ORANGE;
} else {
str = STR_AI_SETTINGS_SETTING;
colour = TC_LIGHT_BLUE;

@ -76,6 +76,7 @@ struct DifficultySettings {
byte competitor_intelligence; ///< Unused value, used to load old savegames.
byte max_no_competitors; ///< the number of competitors (AIs)
uint16 competitors_interval; ///< the interval (in minutes) between adding competitors
byte number_towns; ///< the amount of towns
byte industry_density; ///< The industry density. @see IndustryDensity
uint32 max_loan; ///< the maximum initial loan

@ -57,6 +57,15 @@ interval = 1
post_cb = MaxNoAIsChange
cat = SC_BASIC
[SDT_VAR]
var = difficulty.competitors_interval
type = SLE_UINT16
from = SLV_AI_START_DATE
def = 10
min = MIN_COMPETITORS_INTERVAL
max = MAX_COMPETITORS_INTERVAL
interval = 1
[SDT_VAR]
var = difficulty.competitor_start_time
type = SLE_UINT8

@ -1,6 +1,8 @@
add_files(
timer_game_calendar.cpp
timer_game_calendar.h
timer_game_tick.cpp
timer_game_tick.h
timer_window.cpp
timer_window.h
timer_manager.h

@ -0,0 +1,64 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file timer_game_tick.cpp
* This file implements the timer logic for the tick-based game-timer.
*/
#include "stdafx.h"
#include "timer.h"
#include "timer_game_tick.h"
#include "safeguards.h"
template<>
void IntervalTimer<TimerGameTick>::Elapsed(TimerGameTick::TElapsed delta)
{
if (this->period == 0) return;
this->storage.elapsed += delta;
uint count = 0;
while (this->storage.elapsed >= this->period) {
this->storage.elapsed -= this->period;
count++;
}
if (count > 0) {
this->callback(count);
}
}
template<>
void TimeoutTimer<TimerGameTick>::Elapsed(TimerGameTick::TElapsed delta)
{
if (this->fired) return;
if (this->period == 0) return;
this->storage.elapsed += delta;
if (this->storage.elapsed >= this->period) {
this->callback();
this->fired = true;
}
}
template<>
void TimerManager<TimerGameTick>::Elapsed(TimerGameTick::TElapsed delta)
{
for (auto timer : TimerManager<TimerGameTick>::GetTimers()) {
timer->Elapsed(delta);
}
}
#ifdef WITH_ASSERT
template<>
void TimerManager<TimerGameTick>::Validate(TimerGameTick::TPeriod period)
{
}
#endif /* WITH_ASSERT */

@ -0,0 +1,34 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file timer_game_tick.h Definition of the tick-based game-timer */
#ifndef TIMER_GAME_TICK_H
#define TIMER_GAME_TICK_H
#include "gfx_type.h"
#include <chrono>
/** Estimation of how many ticks fit in a single second. */
static const uint TICKS_PER_SECOND = 1000 / MILLISECONDS_PER_TICK;
/**
* Timer that represents the game-ticks. It will pause when the game is paused.
*
* @note Callbacks are executed in the game-thread.
*/
class TimerGameTick {
public:
using TPeriod = uint;
using TElapsed = uint;
struct TStorage {
uint elapsed;
};
};
#endif /* TIMER_GAME_TICK_H */

@ -15,9 +15,12 @@
/** Widgets of the #AIConfigWindow class. */
enum AIConfigWidgets {
WID_AIC_BACKGROUND, ///< Window background.
WID_AIC_DECREASE, ///< Decrease the number of AIs.
WID_AIC_INCREASE, ///< Increase the number of AIs.
WID_AIC_DECREASE_NUMBER, ///< Decrease the number of AIs.
WID_AIC_INCREASE_NUMBER, ///< Increase the number of AIs.
WID_AIC_NUMBER, ///< Number of AIs.
WID_AIC_DECREASE_INTERVAL,///< Decrease the interval.
WID_AIC_INCREASE_INTERVAL,///< Increase the interval.
WID_AIC_INTERVAL, ///< Interval between time AIs start.
WID_AIC_LIST, ///< List with currently selected AIs.
WID_AIC_SCROLLBAR, ///< Scrollbar to scroll through the selected AIs.
WID_AIC_MOVE_UP, ///< Move up button.

Loading…
Cancel
Save