/** * 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, max(GetSpriteSize(SPR_SQUARE).height, max(GetSpriteSize(SPR_WARNING_SIGN).height, GetSpriteSize(SPR_RANDOM_AI).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, max(GetSpriteSize(SPR_SQUARE).height, max(GetSpriteSize(SPR_WARNING_SIGN).height, GetSpriteSize(SPR_RANDOM_AI).height))) + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; size->height = 15 * this->line_height; break; } case WID_AIC_CHANGE: { uint change_width = 0; static const StringID params[] = {STR_AI_CONFIG_GAMESCRIPT, STR_AI_CONFIG_NONE, STR_AI_CONFIG_AI}; for (int i = 0; i < lengthof(params); i++) { SetDParam(0, params[i]); change_width = max(change_width, GetStringBoundingBox(STR_AI_CONFIG_CHANGE).width); } size->width = change_width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; break; } case WID_AIC_CONFIGURE: { size->width = max(GetStringBoundingBox(STR_AI_CONFIG_CONFIGURE).width, GetStringBoundingBox(STR_AI_DEBUG_SETTINGS).width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; break; } } } /** * Can the AI config in the given company slot be selected? * @param slot The slot to query. * @return True if and only if the given AI Config slot can be selected. */ static bool IsSelectable(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; } /** * Is the AI or GS config in the given slot eligible to start? * @param slot The slot to query. * @return True if and only if the given AI or GS Config slot can start. */ static bool IsEligible(CompanyID slot) { if (slot == OWNER_DEITY) return _game_mode != GM_NORMAL || false; if (Company::IsValidID(slot) || slot < 0 || slot == OWNER_DEITY) return false; int max_slot = GetGameSettings().difficulty.max_no_competitors; for (CompanyID cid = COMPANY_FIRST; cid < (CompanyID)max_slot && cid < MAX_COMPANIES; cid++) { if (Company::IsValidHumanID(cid)) max_slot++; } return slot < max_slot; } virtual void DrawWidget(const Rect &r, int widget) const { switch (widget) { case WID_AIC_GAMELIST: { Dimension square = GetSpriteSize(SPR_SQUARE); uint square_y_offset = (line_height - square.height) / 2; bool rtl = _current_text_dir == TD_RTL; uint square_left = rtl ? r.right - WD_MATRIX_RIGHT - square.width : r.left + WD_MATRIX_LEFT; DrawSprite(SPR_SQUARE, IsEligible(OWNER_DEITY) ? PALETTE_TO_BLUE : IsDead(OWNER_DEITY) ? _game_mode == GM_NORMAL ? PALETTE_TO_GREY : PALETTE_TO_RED : PALETTE_TO_GREEN, square_left, r.top + square_y_offset); 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); } StringID text = STR_AI_CONFIG_NONE; if (GameConfig::GetConfig()->GetInfo() != NULL) { SetDParamStr(0, GameConfig::GetConfig()->GetInfo()->GetName()); text = STR_JUST_RAW_STRING; } uint text_width = GetStringBoundingBox(text).width; uint cid_left = rtl ? square_left - 10 - widest_cid : square_left + square.width + 10; uint cid_right = rtl ? cid_left + widest_cid : cid_left + widest_cid; uint text_left = rtl ? cid_left - 10 - text_width : cid_right + 10; uint text_right = rtl ? text_left + text_width : text_left + text_width; DrawString(text_left, text_right, r.top + WD_MATRIX_TOP, text, (this->selected_slot == OWNER_DEITY) ? TC_WHITE : IsSelectable(OWNER_DEITY) ? 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; Dimension rai = GetSpriteSize(SPR_RANDOM_AI); uint rai_y_offset = (line_height - rai.height) / 2; Dimension warning = GetSpriteSize(SPR_WARNING_SIGN); uint warning_y_offset = (line_height - warning.height) / 2; for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { bool rtl = _current_text_dir == TD_RTL; uint square_left = rtl ? r.right - WD_MATRIX_RIGHT - square.width : r.left + WD_MATRIX_LEFT; DrawSprite(SPR_SQUARE, IsEligible((CompanyID)i) ? _game_mode != GM_NORMAL ? PALETTE_TO_BLUE : PALETTE_TO_ORANGE : Company::IsValidAiID(i) ? IsDead((CompanyID)i) ? PALETTE_TO_RED : PALETTE_TO_GREEN : PALETTE_TO_GREY, square_left, y + square_y_offset); uint cid_left = rtl ? square_left - 10 - widest_cid : square_left + square.width + 10; uint cid_right = rtl ? cid_left + widest_cid : cid_left + widest_cid; SetDParam(0, i + 1); DrawString(cid_left, cid_right, y + WD_MATRIX_TOP, STR_JUST_INT, TC_LIGHT_BLUE); StringID text; 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; } uint text_width = GetStringBoundingBox(text).width; uint text_left = rtl ? cid_left - 10 - text_width : cid_right + 10; uint text_right = rtl ? cid_left - 10 : text_left + text_width; DrawString(text_left, text_right, y + WD_MATRIX_TOP, text, (this->selected_slot == i) ? TC_WHITE : TC_ORANGE); uint rai_left = rtl ? text_left - 10 - rai.width : text_right + 10; uint warning_left = rtl ? text_left - 10 - warning.width : text_right + 10; if (_game_mode == GM_NORMAL && Company::IsValidAiID(i) && AIConfig::GetConfig((CompanyID)i)->IsRandom()) { DrawSprite(SPR_RANDOM_AI, PAL_NONE, rai_left, y + rai_y_offset); } else if (Company::IsValidHumanID(i)) { DrawSprite(SPR_WARNING_SIGN, PAL_NONE, warning_left, y + warning_y_offset); } 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 (IsSelectable(this->selected_slot) && IsDead(this->selected_slot) && IsSelectable((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 (IsSelectable(this->selected_slot) && IsDead(this->selected_slot) && IsSelectable((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; } }