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