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.
This commit is contained in:
Patric Stout 2023-04-16 20:14:22 +02:00 committed by GitHub
parent 43a7e54067
commit ed83c4b0da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 370 additions and 348 deletions

View File

@ -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.

View File

@ -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 */

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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)));

View File

@ -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 {

View File

@ -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 */

View File

@ -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;

View File

@ -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> {};

View File

@ -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

View File

@ -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();

View File

@ -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();

View File

@ -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[] = {

View File

@ -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

View File

@ -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
};

View File

@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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.