/**
* 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.
Scrollbar *vscroll; ///< Cache of the vertical scrollbar.
AIConfigWindow() : Window(&_ai_config_desc)
{
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;
NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_AIC_LIST);
this->vscroll->SetCapacity(nwi->current_y / this->line_height);
this->vscroll->SetCount(MAX_COMPANIES);
this->OnInvalidateData(0);
}
~AIConfigWindow()
{
DeleteWindowByClass(WC_AI_LIST);
DeleteWindowByClass(WC_AI_SETTINGS);
}
virtual void SetStringParameters(int widget) const
{
switch (widget) {
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 = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
size->height = 1 * this->line_height;
break;
case WID_AIC_LIST:
this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
size->height = 15 * this->line_height;
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 - 1;
}
if (Company::IsValidHumanID(slot) || slot < 0) return false;
int max_slot = MAX_COMPANIES;
for (CompanyID cid = COMPANY_FIRST; cid < (CompanyID)max_slot && cid < MAX_COMPANIES; cid++) {
if (Company::IsValidHumanID(cid)) max_slot++;
}
return slot < max_slot;
}
/**
* Check whether the currently selected AI/GS is dead.
* @return true if dead.
*/
static bool IsDead(CompanyID slot)
{
if (slot == OWNER_DEITY) {
return Game::GetInstance()->IsDead();
} else {
return !Company::IsValidAiID(slot) || Company::Get(slot)->ai_instance->IsDead();
}
}
virtual void DrawWidget(const Rect &r, int widget) const
{
switch (widget) {
case WID_AIC_GAMELIST: {
StringID text = STR_AI_CONFIG_NONE;
if (GameConfig::GetConfig()->GetInfo() != NULL) {
SetDParamStr(0, GameConfig::GetConfig()->GetInfo()->GetName());
text = STR_JUST_RAW_STRING;
}
DrawString(r.left + 10, r.right - 10, 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;
for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < MAX_COMPANIES; i++) {
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 = STR_AI_CONFIG_RANDOM_AI;
}
DrawString(r.left + 10, r.right - 10, 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));
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;
}
bool is_orange_slot = IsEditable(this->selected_slot) && !Company::IsValidAiID(this->selected_slot);
bool is_red_slot = IsEditable(this->selected_slot) && Company::IsValidAiID(this->selected_slot) && Company::Get(this->selected_slot)->ai_instance->IsDead();
bool is_orange_slot_above = IsEditable((CompanyID)(this->selected_slot - 1)) && !Company::IsValidAiID(this->selected_slot - 1);
bool is_orange_slot_below = IsEditable((CompanyID)(this->selected_slot + 1)) && !Company::IsValidAiID(this->selected_slot + 1);
bool is_red_slot_above = IsEditable((CompanyID)(this->selected_slot - 1)) && Company::IsValidAiID(this->selected_slot - 1) && Company::Get(this->selected_slot - 1)->ai_instance->IsDead();
bool is_red_slot_below = IsEditable((CompanyID)(this->selected_slot + 1)) && Company::IsValidAiID(this->selected_slot + 1) && Company::Get(this->selected_slot + 1)->ai_instance->IsDead();
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->vscroll->GetScrolledRowFromWidget(pt.y, this, 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 ((is_orange_slot || is_red_slot) && (is_orange_slot_above || is_red_slot_above)) {
Swap(GetGameSettings().ai_config[this->selected_slot], GetGameSettings().ai_config[this->selected_slot - 1]);
this->selected_slot--;
this->vscroll->ScrollTowards(this->selected_slot);
this->InvalidateData();
}
break;
case WID_AIC_MOVE_DOWN:
if ((is_orange_slot || is_red_slot) && (is_orange_slot_below || is_red_slot_below)) {
Swap(GetGameSettings().ai_config[this->selected_slot], GetGameSettings().ai_config[this->selected_slot + 1]);
this->selected_slot++;
this->vscroll->ScrollTowards(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);
bool is_gs_slot = this->selected_slot == OWNER_DEITY;
bool invalid_slot = this->selected_slot == INVALID_COMPANY;
bool is_green_slot = Company::IsValidAiID(this->selected_slot) && !Company::Get(this->selected_slot)->ai_instance->IsDead();
this->SetWidgetDisabledState(WID_AIC_CHANGE, invalid_slot || _game_mode == GM_NORMAL && (is_gs_slot || is_green_slot));
this->SetWidgetDisabledState(WID_AIC_CONFIGURE, invalid_slot || 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 && !invalid_slot && (is_gs_slot && !Game::GetInstance()->IsDead() || is_green_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);
}
bool is_silver_slot_above = !IsEditable((CompanyID)(this->selected_slot - 1));
bool is_green_slot_above = Company::IsValidAiID(this->selected_slot - 1) && !Company::Get(this->selected_slot - 1)->ai_instance->IsDead();
this->SetWidgetDisabledState(WID_AIC_MOVE_UP, is_gs_slot || invalid_slot || is_green_slot || is_silver_slot_above || is_green_slot_above);
bool is_silver_slot_below = !IsEditable((CompanyID)(this->selected_slot + 1));
bool is_green_slot_below = Company::IsValidAiID(this->selected_slot + 1) && !Company::Get(this->selected_slot + 1)->ai_instance->IsDead();
this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, is_gs_slot || invalid_slot || is_green_slot || is_silver_slot_below || is_green_slot_below);
for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
this->SetWidgetDisabledState(WID_AIC_TEXTFILE + tft, invalid_slot || (GetConfig(this->selected_slot)->GetTextfile(tft, this->selected_slot) == NULL));
}
}
};