/**
* Window to configure which AIs will start.
*/
struct AIConfigWindow : public Window {
CompanyID selected_slot; ///< The currently selected AI slot or \c INVALID_COMPANY.
int line_height; ///< Height of a single AI-name line.
AIConfigWindow() : Window(&_ai_config_desc)
{
this->InitNested(WN_GAME_OPTIONS_AI); // Initializes 'this->line_height' as a side effect.
this->selected_slot = INVALID_COMPANY;
this->OnInvalidateData(0);
}
~AIConfigWindow()
{
DeleteWindowByClass(WC_AI_LIST);
if (_game_mode != GM_NORMAL) DeleteWindowByClass(WC_AI_SETTINGS);
}
virtual void SetStringParameters(int widget) const
{
switch (widget) {
case WID_AIC_CAPTION:
SetDParam(0, (_game_mode != GM_NORMAL) ? STR_AI_CONFIG_CAPTION : STR_AI_CONFIG_CAPTION_INGAME);
break;
case WID_AIC_NUMBER:
SetDParam(0, GetGameSettings().difficulty.max_no_competitors);
break;
case WID_AIC_CHANGE:
switch (selected_slot) {
case OWNER_DEITY:
SetDParam(0, STR_AI_CONFIG_CHANGE_GAMESCRIPT);
break;
case INVALID_COMPANY:
SetDParam(0, STR_AI_CONFIG_CHANGE_NONE);
break;
default:
SetDParam(0, STR_AI_CONFIG_CHANGE_AI);
break;
}
break;
}
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
switch (widget) {
case WID_AIC_GAMELIST: {
this->line_height = max((uint)FONT_HEIGHT_NORMAL, GetSpriteSize(SPR_SQUARE).height) + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
size->height = 1 * this->line_height;
break;
}
case WID_AIC_LIST: {
this->line_height = max((uint)FONT_HEIGHT_NORMAL, GetSpriteSize(SPR_SQUARE).height) + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
size->height = 15 * this->line_height;
break;
}
case WID_AIC_CHANGE: {
size->width = max(GetStringBoundingBox(STR_AI_CONFIG_CHANGE).width + GetStringBoundingBox(STR_AI_CONFIG_CHANGE_GAMESCRIPT).width,
max(GetStringBoundingBox(STR_AI_CONFIG_CHANGE).width + GetStringBoundingBox(STR_AI_CONFIG_CHANGE_NONE).width,
GetStringBoundingBox(STR_AI_CONFIG_CHANGE).width + GetStringBoundingBox(STR_AI_CONFIG_CHANGE_AI).width));
break;
}
case WID_AIC_CONFIGURE: {
size->width = max(GetStringBoundingBox(STR_AI_CONFIG_CONFIGURE).width, GetStringBoundingBox(STR_AI_DEBUG_SETTINGS).width);
break;
}
}
}
/**
* Can the AI config in the given company slot be edited?
* @param slot The slot to query.
* @return True if and only if the given AI Config slot can e edited.
*/
static bool IsEditable(CompanyID slot)
{
if (slot == OWNER_DEITY) return _game_mode != GM_NORMAL || Game::GetInstance() != NULL;
if (_game_mode != GM_NORMAL) {
return slot >= 0 && slot < MAX_COMPANIES;
}
if (Company::IsValidHumanID(slot) || slot < 0) return false;
return slot < MAX_COMPANIES;
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch (widget) {
case WID_AIC_GAMELIST: {
uint widest_cid = 0;
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
SetDParam(0, i + 1);
widest_cid = max(GetStringBoundingBox(STR_JUST_INT).width, widest_cid);
}
Dimension square = GetSpriteSize(SPR_SQUARE);
uint square_y_offset = (line_height - square.height) / 2;
StringID text = STR_AI_CONFIG_NONE;
if (GameConfig::GetConfig()->GetInfo() != NULL) {
SetDParamStr(0, GameConfig::GetConfig()->GetInfo()->GetName());
text = STR_JUST_RAW_STRING;
}
DrawSprite(SPR_SQUARE,
IsEditable(OWNER_DEITY) ? (_game_mode == GM_NORMAL) ? (IsDead(OWNER_DEITY)) ? PALETTE_TO_RED : PALETTE_TO_GREEN : PALETTE_TO_BLUE : PALETTE_TO_GREY,
r.left + WD_MATRIX_LEFT, r.top + square_y_offset);
DrawString(r.left + WD_MATRIX_LEFT + square.width + WD_MATRIX_LEFT + widest_cid + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, r.top + WD_MATRIX_TOP, text,
(this->selected_slot == OWNER_DEITY) ? TC_WHITE : (IsEditable(OWNER_DEITY) ? (_game_mode == GM_NORMAL) ? (IsDead(OWNER_DEITY)) ? TC_RED : TC_GREEN : TC_ORANGE : TC_SILVER));
break;
}
case WID_AIC_LIST: {
int y = r.top;
uint widest_cid = 0;
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
SetDParam(0, i + 1);
widest_cid = max(GetStringBoundingBox(STR_JUST_INT).width, widest_cid);
}
Dimension square = GetSpriteSize(SPR_SQUARE);
uint square_y_offset = (line_height - square.height) / 2;
StringID rai = STR_AI_CONFIG_RANDOM_AI;
uint rai_width = GetStringBoundingBox(rai).width;
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
SetDParam(0, i + 1);
StringID cid = STR_JUST_INT;
DrawString(r.left + WD_MATRIX_LEFT + square.width, r.left + WD_MATRIX_LEFT + square.width + widest_cid, y + WD_MATRIX_TOP, cid, TC_LIGHT_BLUE, SA_RIGHT);
StringID text;
if (_game_mode == GM_NORMAL && Company::IsValidHumanID(i)) {
text = STR_AI_CONFIG_HUMAN_PLAYER;
} else if (AIConfig::GetConfig((CompanyID)i)->GetInfo() != NULL) {
SetDParamStr(0, AIConfig::GetConfig((CompanyID)i)->GetInfo()->GetName());
text = STR_JUST_RAW_STRING;
} else {
text = rai;
}
uint text_width = GetStringBoundingBox(text).width;
DrawSprite(SPR_SQUARE,
IsEditable((CompanyID)i) ? Company::IsValidAiID(i) ? IsDead((CompanyID)i) ? PALETTE_TO_RED : PALETTE_TO_GREEN : _game_mode != GM_NORMAL ? PALETTE_TO_BLUE : PALETTE_TO_ORANGE : PALETTE_TO_GREY,
r.left + WD_MATRIX_LEFT, y + square_y_offset);
DrawString(r.left + WD_MATRIX_LEFT + square.width + WD_MATRIX_LEFT + widest_cid + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT - rai_width - WD_MATRIX_RIGHT, y + WD_MATRIX_TOP, text,
(this->selected_slot == i) ? TC_WHITE : (IsEditable((CompanyID)i) ? Company::IsValidAiID(i) ? IsDead((CompanyID)i) ? TC_RED : TC_GREEN : TC_ORANGE : TC_SILVER));
if (_game_mode == GM_NORMAL && Company::IsValidAiID(i) && AIConfig::GetConfig((CompanyID)i)->IsRandom()) {
DrawString(min(r.left + WD_MATRIX_LEFT + square.width + WD_MATRIX_LEFT + widest_cid + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT - rai_width - WD_MATRIX_RIGHT), r.right - WD_MATRIX_RIGHT, y + WD_MATRIX_TOP, rai,
(this->selected_slot == i) ? TC_WHITE : (IsEditable((CompanyID)i) ? Company::IsValidAiID(i) ? IsDead((CompanyID)i) ? TC_RED : TC_GREEN : TC_ORANGE : TC_SILVER), SA_RIGHT);
}
y += this->line_height;
}
break;
}
}
}
virtual void OnClick(Point pt, int widget, int click_count)
{
if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TFT_END) {
if (this->selected_slot == INVALID_COMPANY || GetConfig(this->selected_slot) == NULL) return;
ShowScriptTextfileWindow((TextfileType)(widget - WID_AIC_TEXTFILE), this->selected_slot);
return;
}
switch (widget) {
case WID_AIC_DECREASE:
case WID_AIC_INCREASE: {
int new_value;
if (widget == WID_AIC_DECREASE) {
new_value = max(0, GetGameSettings().difficulty.max_no_competitors - 1);
} else {
new_value = min(MAX_COMPANIES, GetGameSettings().difficulty.max_no_competitors + 1);
}
IConsoleSetSetting("difficulty.max_no_competitors", new_value);
this->InvalidateData();
break;
}
case WID_AIC_GAMELIST: {
this->selected_slot = OWNER_DEITY;
this->InvalidateData();
if (click_count > 1 && this->selected_slot != INVALID_COMPANY && _game_mode != GM_NORMAL) ShowAIListWindow((CompanyID)this->selected_slot);
break;
}
case WID_AIC_LIST: { // Select a slot
this->selected_slot = (CompanyID)this->GetRowFromWidget(pt.y, widget, 0, this->line_height);
this->InvalidateData();
if (click_count > 1 && this->selected_slot != INVALID_COMPANY) ShowAIListWindow((CompanyID)this->selected_slot);
break;
}
case WID_AIC_MOVE_UP:
if (IsEditable(this->selected_slot) && IsDead(this->selected_slot) && IsEditable((CompanyID)(this->selected_slot - 1)) && IsDead((CompanyID)(this->selected_slot - 1))) {
Swap(GetGameSettings().ai_config[this->selected_slot], GetGameSettings().ai_config[this->selected_slot - 1]);
this->selected_slot--;
this->InvalidateData();
}
break;
case WID_AIC_MOVE_DOWN:
if (IsEditable(this->selected_slot) && IsDead(this->selected_slot) && IsEditable((CompanyID)(this->selected_slot + 1)) && IsDead((CompanyID)(this->selected_slot + 1))) {
Swap(GetGameSettings().ai_config[this->selected_slot], GetGameSettings().ai_config[this->selected_slot + 1]);
this->selected_slot++;
this->InvalidateData();
}
break;
case WID_AIC_CHANGE: // choose other AI
ShowAIListWindow((CompanyID)this->selected_slot);
break;
case WID_AIC_CONFIGURE: // change the settings for an AI
ShowAISettingsWindow((CompanyID)this->selected_slot);
break;
case WID_AIC_CLOSE:
delete this;
break;
case WID_AIC_CONTENT_DOWNLOAD:
if (!_network_available) {
ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR);
} else {
#if defined(ENABLE_NETWORK)
ShowNetworkContentListWindow(NULL, CONTENT_TYPE_AI, CONTENT_TYPE_GAME);
#endif
}
break;
}
}
/**
* Some data on this window has become invalid.
* @param data Information about the changed data.
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
*/
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
{
if (!IsEditable(this->selected_slot)) {
this->selected_slot = INVALID_COMPANY;
}
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);
this->SetWidgetDisabledState(WID_AIC_CHANGE, this->selected_slot == INVALID_COMPANY || _game_mode == GM_NORMAL && (this->selected_slot == OWNER_DEITY || !IsDead(this->selected_slot)));
this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == INVALID_COMPANY || GetConfig(this->selected_slot)->GetConfigList()->size() == 0);
/* Display either Settings or Configure button */
NWidgetCore *configure_button = this->GetWidget<NWidgetCore>(WID_AIC_CONFIGURE);
if (_game_mode == GM_NORMAL && this->selected_slot != INVALID_COMPANY && !IsDead(this->selected_slot)) {
configure_button->SetDataTip(STR_AI_DEBUG_SETTINGS, STR_AI_DEBUG_SETTINGS_TOOLTIP);
} else {
configure_button->SetDataTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP);
}
this->SetWidgetDisabledState(WID_AIC_MOVE_UP, this->selected_slot == INVALID_COMPANY || this->selected_slot == OWNER_DEITY || !IsDead(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot - 1)) || !IsDead((CompanyID)(this->selected_slot - 1)));
this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, this->selected_slot == INVALID_COMPANY || this->selected_slot == OWNER_DEITY || !IsDead(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot + 1)) || !IsDead((CompanyID)(this->selected_slot + 1)));
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
this->SetWidgetDisabledState(WID_AIC_TEXTFILE + tft, this->selected_slot == INVALID_COMPANY || (GetConfig(this->selected_slot)->GetTextfile(tft, this->selected_slot) == NULL));
}
}
};