diff --git a/regression/regression/result.txt b/regression/regression/result.txt index 9942857cfb..3dd8b60746 100644 --- a/regression/regression/result.txt +++ b/regression/regression/result.txt @@ -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: diff --git a/regression/regression/test.sav b/regression/regression/test.sav index cf97052c2e..30b68c12cf 100644 Binary files a/regression/regression/test.sav and b/regression/regression/test.sav differ diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp index e090303bd9..667de589eb 100644 --- a/src/ai/ai.hpp +++ b/src/ai/ai.hpp @@ -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 */ diff --git a/src/ai/ai_config.cpp b/src/ai/ai_config.cpp index 98618feb20..a1c6479952 100644 --- a/src/ai/ai_config.cpp +++ b/src/ai/ai_config.cpp @@ -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); -} diff --git a/src/ai/ai_config.hpp b/src/ai/ai_config.hpp index 6c861126e0..a0f98d2187 100644 --- a/src/ai/ai_config.hpp +++ b/src/ai/ai_config.hpp @@ -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; }; diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp index 220e7bdbd2..feeeeb0162 100644 --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -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); diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index c93358828e..edd7e48348 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -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(MIN_COMPETITORS_INTERVAL), GetGameSettings().difficulty.competitors_interval - 1); + } else { + new_value = std::min(static_cast(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))); diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp index b8bc1fb83d..7ca897c713 100644 --- a/src/ai/ai_info.cpp +++ b/src/ai/ai_info.cpp @@ -69,11 +69,6 @@ template <> const char *GetClassName() { 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 { diff --git a/src/company_base.h b/src/company_base.h index bb88e94fd6..d0bbea095c 100644 --- a/src/company_base.h +++ b/src/company_base.h @@ -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 */ diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index fab43052c2..e9639bfcbf 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -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 _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::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::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::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; diff --git a/src/company_type.h b/src/company_type.h index 3bbcf731e6..cdd9a414d6 100644 --- a/src/company_type.h +++ b/src/company_type.h @@ -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 : MakeEnumPropsT {}; diff --git a/src/lang/english.txt b/src/lang/english.txt index 0ea5d374b0..352119a66a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -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 diff --git a/src/openttd.cpp b/src/openttd.cpp index d61a4145ee..c3b7c8fa43 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -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::Elapsed(1); + TimerManager::Elapsed(1); RunTileLoop(); CallVehicleTicks(); CallLandscapeTick(); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 1d3c018a49..8faa144c95 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -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 _new_competitor_timeout; + _new_competitor_timeout.storage.elapsed = 0; + _new_competitor_timeout.fired = _new_competitor_timeout.period == 0; + } + AfterLoadLabelMaps(); AfterLoadCompanyStats(); AfterLoadStoryBook(); diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp index e537bee019..7fdfd5aa2d 100644 --- a/src/saveload/misc_sl.cpp +++ b/src/saveload/misc_sl.cpp @@ -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 _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[] = { diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index b437cbf5f6..783ef54f01 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -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 @@ -489,6 +491,7 @@ static inline uint RemapOrderIndex(uint x) } extern std::vector _animated_tiles; +extern TimeoutTimer _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 diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 60fc0adbe9..24c3ca5b27 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -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 }; diff --git a/src/script/script_config.cpp b/src/script/script_config.cpp index 7f4cfebb6d..29ab7af430 100644 --- a/src/script/script_config.cpp +++ b/src/script/script_config.cpp @@ -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; } diff --git a/src/script/script_config.hpp b/src/script/script_config.hpp index b243f3061f..be788d0526 100644 --- a/src/script/script_config.hpp +++ b/src/script/script_config.hpp @@ -51,8 +51,6 @@ struct ScriptConfigItem { typedef std::list 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 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, diff --git a/src/script/script_gui.cpp b/src/script/script_gui.cpp index bee329596b..d060620af1 100644 --- a/src/script/script_gui.cpp +++ b/src/script/script_gui.cpp @@ -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; diff --git a/src/settings_type.h b/src/settings_type.h index 4b9bc2f447..cae3376102 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -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 diff --git a/src/table/settings/difficulty_settings.ini b/src/table/settings/difficulty_settings.ini index ee68c6d30e..740e83aa54 100644 --- a/src/table/settings/difficulty_settings.ini +++ b/src/table/settings/difficulty_settings.ini @@ -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 diff --git a/src/timer/CMakeLists.txt b/src/timer/CMakeLists.txt index de9e01636f..37612481b8 100644 --- a/src/timer/CMakeLists.txt +++ b/src/timer/CMakeLists.txt @@ -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 diff --git a/src/timer/timer_game_tick.cpp b/src/timer/timer_game_tick.cpp new file mode 100644 index 0000000000..75ed88d63d --- /dev/null +++ b/src/timer/timer_game_tick.cpp @@ -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 . + */ + +/** + * @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::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::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::Elapsed(TimerGameTick::TElapsed delta) +{ + for (auto timer : TimerManager::GetTimers()) { + timer->Elapsed(delta); + } +} + +#ifdef WITH_ASSERT +template<> +void TimerManager::Validate(TimerGameTick::TPeriod period) +{ +} +#endif /* WITH_ASSERT */ diff --git a/src/timer/timer_game_tick.h b/src/timer/timer_game_tick.h new file mode 100644 index 0000000000..6a48a35bfd --- /dev/null +++ b/src/timer/timer_game_tick.h @@ -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 . + */ + +/** @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 + +/** 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 */ diff --git a/src/widgets/ai_widget.h b/src/widgets/ai_widget.h index f50b6632c8..4d6418bcfe 100644 --- a/src/widgets/ai_widget.h +++ b/src/widgets/ai_widget.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.