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