Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codechange: Rework 'start_date' parameter for AIs as a game setting #10330

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/ai/ai.hpp
Expand Up @@ -23,12 +23,11 @@ class AI {
* 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 = DAYS_IN_YEAR * 2,
START_NEXT_MIN = 0,
START_NEXT_MAX = 3600,
START_NEXT_DEVIATION = 60,
START_NEXT_STEP_SIZE = 30
};

/**
Expand Down
94 changes: 0 additions & 94 deletions src/ai/ai_config.cpp
Expand Up @@ -9,39 +9,12 @@

#include "../stdafx.h"
#include "../settings_type.h"
#include "../string_func.h"
#include "ai.hpp"
#include "ai_config.hpp"
#include "ai_info.hpp"

#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;
Expand Down Expand Up @@ -69,70 +42,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);
}
10 changes: 3 additions & 7 deletions src/ai/ai_config.hpp
Expand Up @@ -24,14 +24,12 @@ class AIConfig : public ScriptConfig {
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.
Expand All @@ -43,8 +41,6 @@ class AIConfig : public ScriptConfig {
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;
};

Expand Down
18 changes: 15 additions & 3 deletions src/ai/ai_core.cpp
Expand Up @@ -15,6 +15,7 @@
#include "../network/network.h"
#include "../window_func.h"
#include "../framerate_type.h"
#include "../script/api/script_object.hpp"
#include "ai_scanner.hpp"
#include "ai_instance.hpp"
#include "ai_config.hpp"
Expand Down Expand Up @@ -291,9 +292,20 @@

/* 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");
int start_next = _settings_game.ai.ai_start_next;
if (start_next > 0) {
/* Find the first company which doesn't exist yet */
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
if (!Company::IsValidID(c)) {
/* Add random deviation */
start_next += ScriptObject::GetRandomizer(c).Next(AI::START_NEXT_DEVIATION * 2 + 1) - AI::START_NEXT_DEVIATION;

/* ai_start_next = 0 is a special case, where random deviation does not occur.
* If ai_start_next was not already 0, then a minimum value of 1 must apply. */
start_next = Clamp(start_next, 1, AI::START_NEXT_MAX);
return start_next;
}
}
}

/* Currently no AI can be started, check again in a year. */
Expand Down
65 changes: 65 additions & 0 deletions src/ai/ai_gui.cpp
Expand Up @@ -15,6 +15,7 @@
#include "../settings_func.h"
#include "../network/network_content.h"
#include "../core/geometry_func.hpp"
#include "../textbuf_gui.h"

#include "ai.hpp"
#include "ai_gui.hpp"
Expand All @@ -34,6 +35,12 @@ static const NWidgetPart _nested_ai_config_widgets[] = {
EndContainer(),
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_SN_DECREASE), SetDataTip(AWV_DECREASE, STR_NULL),
NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_SN_INCREASE), SetDataTip(AWV_INCREASE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(6, 0),
NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_START_NEXT), SetDataTip(STR_AI_CONFIG_START_NEXT, STR_AI_CONFIG_START_NEXT_TOOLTIP), SetFill(1, 0),
EndContainer(),
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),
Expand Down Expand Up @@ -79,6 +86,7 @@ static WindowDesc _ai_config_desc(
*/
struct AIConfigWindow : public Window {
CompanyID selected_slot; ///< The currently selected AI slot or \c INVALID_COMPANY.
AIConfigWidgets widget; ///< corresponding widget to setting for which a value-entering window has been opened.
int line_height; ///< Height of a single AI-name line.
Scrollbar *vscroll; ///< Cache of the vertical scrollbar.

Expand All @@ -87,6 +95,7 @@ struct AIConfigWindow : public Window {
this->InitNested(WN_GAME_OPTIONS_AI); // Initializes 'this->line_height' as a side effect.
this->vscroll = this->GetScrollbar(WID_AIC_SCROLLBAR);
this->selected_slot = INVALID_COMPANY;
this->widget = WID_AIC_BACKGROUND;
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_AIC_LIST);
this->vscroll->SetCapacity(nwi->current_y / this->line_height);
this->vscroll->SetCount(MAX_COMPANIES);
Expand All @@ -103,6 +112,11 @@ struct AIConfigWindow : public Window {
void SetStringParameters(int widget) const override
{
switch (widget) {
case WID_AIC_START_NEXT:
SetDParam(0, STR_CONFIG_SETTING_AI_START_NEXT_VALUE);
SetDParam(1, GetGameSettings().ai.ai_start_next);
break;

case WID_AIC_NUMBER:
SetDParam(0, GetGameSettings().difficulty.max_no_competitors);
break;
Expand All @@ -112,6 +126,8 @@ struct AIConfigWindow : public Window {
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
switch (widget) {
case WID_AIC_SN_DECREASE:
case WID_AIC_SN_INCREASE:
case WID_AIC_DECREASE:
case WID_AIC_INCREASE:
*size = maxdim(*size, NWidgetScrollbar::GetHorizontalDimension());
Expand Down Expand Up @@ -179,8 +195,24 @@ struct AIConfigWindow : public Window {
}

switch (widget) {
case WID_AIC_SN_DECREASE:
case WID_AIC_SN_INCREASE: {
if (this->widget != WID_AIC_START_NEXT) CloseChildWindows(WC_QUERY_STRING);

int new_value;
if (widget == WID_AIC_SN_DECREASE) {
new_value = std::max((int)AI::START_NEXT_MIN, GetGameSettings().ai.ai_start_next - AI::START_NEXT_STEP_SIZE);
} else {
new_value = std::min((int)AI::START_NEXT_MAX, GetGameSettings().ai.ai_start_next + AI::START_NEXT_STEP_SIZE);
}
IConsoleSetSetting("ai.ai_start_next", new_value);
break;
}

case WID_AIC_DECREASE:
case WID_AIC_INCREASE: {
if (this->widget != WID_AIC_NUMBER) CloseChildWindows(WC_QUERY_STRING);

int new_value;
if (widget == WID_AIC_DECREASE) {
new_value = std::max(0, GetGameSettings().difficulty.max_no_competitors - 1);
Expand All @@ -191,6 +223,21 @@ struct AIConfigWindow : public Window {
break;
}

case WID_AIC_START_NEXT:
case WID_AIC_NUMBER: {
/* Display a query box so users can enter a custom value. */
this->widget = (AIConfigWidgets)widget;
int old_val;
if (widget == WID_AIC_NUMBER) {
old_val = GetGameSettings().difficulty.max_no_competitors;
} else {
old_val = GetGameSettings().ai.ai_start_next;
}
SetDParam(0, old_val);
ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, 5, this, CS_NUMERAL, widget == WID_AIC_NUMBER ? QSF_NONE : QSF_ENABLE_DEFAULT);
break;
}

case WID_AIC_LIST: { // Select a slot
this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget);
this->InvalidateData();
Expand Down Expand Up @@ -238,6 +285,22 @@ struct AIConfigWindow : public Window {
}
}

void OnQueryTextFinished(char *str) override
{
/* The user pressed cancel */
if (str == nullptr) return;

int32 value;
if (StrEmpty(str)) {
if (widget == WID_AIC_NUMBER) return;
value = AI::START_NEXT;
} else {
value = atoi(str);
}
IConsoleSetSetting(widget == WID_AIC_NUMBER ? "difficulty.max_no_competitors" : "ai.ai_start_next", value);
this->SetDirty();
}

/**
* Some data on this window has become invalid.
* @param data Information about the changed data.
Expand All @@ -251,6 +314,8 @@ struct AIConfigWindow : public Window {

if (!gui_scope) return;

this->SetWidgetDisabledState(WID_AIC_SN_DECREASE, GetGameSettings().ai.ai_start_next == AI::START_NEXT_MIN);
this->SetWidgetDisabledState(WID_AIC_SN_INCREASE, GetGameSettings().ai.ai_start_next == AI::START_NEXT_MAX);
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_CHANGE, this->selected_slot == INVALID_COMPANY);
Expand Down
5 changes: 0 additions & 5 deletions src/ai/ai_info.cpp
Expand Up @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions src/company_cmd.cpp
Expand Up @@ -727,13 +727,14 @@ void OnTick_Companies()

if (_game_mode != GM_MENU && AI::CanStartNew() && --_next_competitor_start == 0) {
/* Allow multiple AIs to possibly start in the same tick. */
do {
for (;;) {
if (!MaybeStartNewCompany()) break;
if (_settings_game.ai.ai_start_next != 0) break;

/* 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);
}
}

_cur_company_tick_index = (_cur_company_tick_index + 1) % MAX_COMPANIES;
Expand Down
7 changes: 6 additions & 1 deletion src/lang/english.txt
Expand Up @@ -1704,6 +1704,10 @@ STR_CONFIG_SETTING_AI_PROFILE_HARD :Hard
STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :Allow AIs in multiplayer: {STRING2}
STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :Allow AI computer players to participate in multiplayer games

STR_CONFIG_SETTING_AI_START_NEXT :Start delay between AIs (give or take): {STRING2}
STR_CONFIG_SETTING_AI_START_NEXT_HELPTEXT :Number of days to wait before starting an AI, or subsequent AI(s) after the previous one.{}If the number of days is different than zero, a random deviation of 60 days is added, and the actual value in-game will be 'number_of_days + random(-60 days, 60 days)', with a minimum of 1 day and a maximum of 3600 days
STR_CONFIG_SETTING_AI_START_NEXT_VALUE :{COMMA}{NBSP}day{P 0 "" s}

STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :#opcodes before scripts are suspended: {STRING2}
STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Maximum number of computation steps that a script can take in one turn
STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY :Max memory usage per script: {STRING2}
Expand Down Expand Up @@ -4585,6 +4589,8 @@ STR_AI_CONFIG_HUMAN_PLAYER :Human player
STR_AI_CONFIG_RANDOM_AI :Random AI
STR_AI_CONFIG_NONE :(none)
STR_AI_CONFIG_MAX_COMPETITORS :{LTBLUE}Maximum no. competitors: {ORANGE}{COMMA}
STR_AI_CONFIG_START_NEXT :{LTBLUE}Start delay between AIs (give or take): {ORANGE}{STRING1}
STR_AI_CONFIG_START_NEXT_TOOLTIP :{BLACK}Number of days to wait before starting an AI, or subsequent AI(s) after the previous one.{}If the number of days is different than zero, a random deviation of 60 days is added, and the actual value in-game will be 'number_of_days + random(-60 days, 60 days)', with a minimum of 1 day and a maximum of 3600 days.{}If the number of days is zero, the AI(s) will start immediately.

STR_AI_CONFIG_MOVE_UP :{BLACK}Move Up
STR_AI_CONFIG_MOVE_UP_TOOLTIP :{BLACK}Move selected AI up in the list
Expand Down Expand Up @@ -4631,7 +4637,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
Expand Down
3 changes: 0 additions & 3 deletions src/openttd.cpp
Expand Up @@ -389,9 +389,6 @@ void MakeNewgameSettingsLive()
_settings_game.ai_config[c] = nullptr;
if (_settings_newgame.ai_config[c] != nullptr) {
_settings_game.ai_config[c] = new AIConfig(_settings_newgame.ai_config[c]);
if (!AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->HasScript()) {
AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr);
}
Comment on lines -392 to -394
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change feels wrong

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the fix for #6460. The "start_date" parameter for random ais was at times not part of their configuration. This PR undoes that fix because "start_date" is no longer part of an AI configuration.

}
}
_settings_game.game_config = nullptr;
Expand Down