Merge branch 'master' into jgrpp

# Conflicts:
#	.github/workflows/ci-build.yml
#	src/ai/ai_gui.cpp
#	src/blitter/32bpp_optimized.cpp
#	src/blitter/32bpp_simple.cpp
#	src/blitter/32bpp_sse2.cpp
#	src/blitter/8bpp_optimized.cpp
#	src/blitter/8bpp_simple.cpp
#	src/blitter/null.cpp
#	src/blitter/null.hpp
#	src/company_gui.cpp
#	src/game/game_gui.cpp
#	src/genworld_gui.cpp
#	src/gfx.cpp
#	src/gfx_func.h
#	src/graph_gui.cpp
#	src/industry_gui.cpp
#	src/linkgraph/linkgraphjob.cpp
#	src/network/network_gui.cpp
#	src/newgrf_debug_gui.cpp
#	src/openttd.cpp
#	src/pathfinder/npf/aystar.h
#	src/road_gui.cpp
#	src/saveload/order_sl.cpp
#	src/saveload/saveload.cpp
#	src/saveload/saveload.h
#	src/script/api/script_log.cpp
#	src/script/api/script_town.cpp
#	src/script/script_gui.cpp
#	src/settings.cpp
#	src/settings_gui.cpp
#	src/settings_table.cpp
#	src/settings_type.h
#	src/smallmap_gui.cpp
#	src/sortlist_type.h
#	src/spritecache.cpp
#	src/spriteloader/grf.cpp
#	src/spriteloader/grf.hpp
#	src/spriteloader/spriteloader.hpp
#	src/station_cmd.cpp
#	src/station_cmd.h
#	src/station_gui.cpp
#	src/strings.cpp
#	src/toolbar_gui.cpp
#	src/town_cmd.cpp
#	src/town_gui.cpp
#	src/vehicle_gui.cpp
#	src/vehicle_gui_base.h
#	src/video/opengl.cpp
#	src/video/opengl.h
#	src/widgets/dropdown.cpp
#	src/widgets/dropdown_type.h
#	src/window_gui.h
This commit is contained in:
Jonathan G Rennison
2023-12-23 13:26:55 +00:00
249 changed files with 2737 additions and 2156 deletions

View File

@@ -709,23 +709,35 @@ static bool SetScriptButtonColour(NWidgetCore &button, bool dead, bool paused)
struct ScriptDebugWindow : public Window {
static const uint MAX_BREAK_STR_STRING_LENGTH = 256; ///< Maximum length of the break string.
static CompanyID script_debug_company; ///< The AI that is (was last) being debugged.
struct FilterState {
std::string break_string; ///< The string to match to the AI output
CompanyID script_debug_company; ///< The AI that is (was last) being debugged.
bool break_check_enabled; ///< Stop an AI when it prints a matching string
bool case_sensitive_break_check; ///< Is the matching done case-sensitive
};
static inline FilterState initial_state = {
"",
INVALID_COMPANY,
true,
false,
};
int redraw_timer; ///< Timer for redrawing the window, otherwise it'll happen every tick.
int last_vscroll_pos; ///< Last position of the scrolling.
bool autoscroll; ///< Whether automatically scrolling should be enabled or not.
bool show_break_box; ///< Whether the break/debug box is visible.
static bool break_check_enabled; ///< Stop an AI when it prints a matching string
static std::string break_string; ///< The string to match to the AI output
QueryString break_editbox; ///< Break editbox
static StringFilter break_string_filter; ///< Log filter for break.
static bool case_sensitive_break_check; ///< Is the matching done case-sensitive
StringFilter break_string_filter; ///< Log filter for break.
int highlight_row; ///< The output row that matches the given string, or -1
Scrollbar *vscroll; ///< Cache of the vertical scrollbar.
Scrollbar *hscroll; ///< Cache of the horizontal scrollbar.
FilterState filter;
ScriptLogTypes::LogData &GetLogData() const
{
if (script_debug_company == OWNER_DEITY) return Game::GetInstance()->GetLogData();
return Company::Get(script_debug_company)->ai_instance->GetLogData();
if (this->filter.script_debug_company == OWNER_DEITY) return Game::GetInstance()->GetLogData();
return Company::Get(this->filter.script_debug_company)->ai_instance->GetLogData();
}
/**
@@ -734,11 +746,11 @@ struct ScriptDebugWindow : public Window {
*/
bool IsDead() const
{
if (script_debug_company == OWNER_DEITY) {
if (this->filter.script_debug_company == OWNER_DEITY) {
GameInstance *game = Game::GetInstance();
return game == nullptr || game->IsDead();
}
return !Company::IsValidAiID(script_debug_company) || Company::Get(script_debug_company)->ai_instance->IsDead();
return !Company::IsValidAiID(this->filter.script_debug_company) || Company::Get(this->filter.script_debug_company)->ai_instance->IsDead();
}
/**
@@ -762,9 +774,9 @@ struct ScriptDebugWindow : public Window {
void SelectValidDebugCompany()
{
/* Check if the currently selected company is still active. */
if (this->IsValidDebugCompany(script_debug_company)) return;
if (this->IsValidDebugCompany(this->filter.script_debug_company)) return;
script_debug_company = INVALID_COMPANY;
this->filter.script_debug_company = INVALID_COMPANY;
for (const Company *c : Company::Iterate()) {
if (c->is_ai) {
@@ -782,15 +794,19 @@ struct ScriptDebugWindow : public Window {
* @param desc The description of the window.
* @param number The window number (actually unused).
*/
ScriptDebugWindow(WindowDesc *desc, WindowNumber number) : Window(desc), break_editbox(MAX_BREAK_STR_STRING_LENGTH)
ScriptDebugWindow(WindowDesc *desc, WindowNumber number, Owner show_company) : Window(desc), break_editbox(MAX_BREAK_STR_STRING_LENGTH)
{
this->filter = ScriptDebugWindow::initial_state;
this->break_string_filter = {&this->filter.case_sensitive_break_check, false};
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_SCRD_SCROLLBAR);
this->vscroll = this->GetScrollbar(WID_SCRD_VSCROLLBAR);
this->hscroll = this->GetScrollbar(WID_SCRD_HSCROLLBAR);
this->show_break_box = _settings_client.gui.ai_developer_tools;
this->GetWidget<NWidgetStacked>(WID_SCRD_BREAK_STRING_WIDGETS)->SetDisplayedPlane(this->show_break_box ? 0 : SZSP_HORIZONTAL);
this->FinishInitNested(number);
if (!this->show_break_box) break_check_enabled = false;
if (!this->show_break_box) this->filter.break_check_enabled = false;
this->last_vscroll_pos = 0;
this->autoscroll = true;
@@ -799,14 +815,28 @@ struct ScriptDebugWindow : public Window {
this->querystrings[WID_SCRD_BREAK_STR_EDIT_BOX] = &this->break_editbox;
SetWidgetsDisabledState(!this->show_break_box, WID_SCRD_BREAK_STR_ON_OFF_BTN, WID_SCRD_BREAK_STR_EDIT_BOX, WID_SCRD_MATCH_CASE_BTN);
this->hscroll->SetStepSize(10); // Speed up horizontal scrollbar
/* Restore the break string value from static variable */
this->break_editbox.text.Assign(this->break_string);
this->break_editbox.text.Assign(this->filter.break_string);
this->SelectValidDebugCompany();
if (show_company == INVALID_COMPANY) {
this->SelectValidDebugCompany();
} else {
this->ChangeToScript(show_company);
}
}
void OnInit() override
{
this->InvalidateData(-1);
}
~ScriptDebugWindow()
{
ScriptDebugWindow::initial_state = this->filter;
}
void UpdateWidgetSize(int widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override
{
if (widget == WID_SCRD_LOG_PANEL) {
@@ -818,92 +848,26 @@ struct ScriptDebugWindow : public Window {
void OnPaint() override
{
this->SelectValidDebugCompany();
this->UpdateLogScroll();
/* Draw standard stuff */
this->DrawWidgets();
if (this->IsShaded()) return; // Don't draw anything when the window is shaded.
bool dirty = false;
/* Paint the company icons */
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
NWidgetCore *button = this->GetWidget<NWidgetCore>(i + WID_SCRD_COMPANY_BUTTON_START);
bool valid = Company::IsValidAiID(i);
/* Check whether the validity of the company changed */
dirty |= (button->IsDisabled() == valid);
/* Mark dead/paused AIs by setting the background colour. */
bool dead = valid && Company::Get(i)->ai_instance->IsDead();
bool paused = valid && Company::Get(i)->ai_instance->IsPaused();
/* Re-paint if the button was updated.
* (note that it is intentional that SetScriptButtonColour is always called) */
dirty |= SetScriptButtonColour(*button, dead, paused);
/* Draw company icon only for valid AI companies */
if (!valid) continue;
byte offset = (i == script_debug_company) ? 1 : 0;
DrawCompanyIcon(i, button->pos_x + button->current_x / 2 - 7 + offset, this->GetWidget<NWidgetBase>(WID_SCRD_COMPANY_BUTTON_START + i)->pos_y + 2 + offset);
}
/* Set button colour for Game Script. */
GameInstance *game = Game::GetInstance();
bool valid = game != nullptr;
bool dead = valid && game->IsDead();
bool paused = valid && game->IsPaused();
NWidgetCore *button = this->GetWidget<NWidgetCore>(WID_SCRD_SCRIPT_GAME);
dirty |= (button->IsDisabled() == valid) || SetScriptButtonColour(*button, dead, paused);
if (dirty) this->InvalidateData(-1);
/* If there are no active companies, don't display anything else. */
if (script_debug_company == INVALID_COMPANY) return;
ScriptLogTypes::LogData &log = this->GetLogData();
int scroll_count = (int)log.size();
if (this->vscroll->GetCount() != scroll_count) {
this->vscroll->SetCount(scroll_count);
/* We need a repaint */
this->SetWidgetDirty(WID_SCRD_SCROLLBAR);
}
if (log.empty()) return;
/* Detect when the user scrolls the window. Enable autoscroll when the
* bottom-most line becomes visible. */
if (this->last_vscroll_pos != this->vscroll->GetPosition()) {
this->autoscroll = this->vscroll->GetPosition() + this->vscroll->GetCapacity() >= (int)log.size();
}
if (this->autoscroll) {
if (this->vscroll->SetPosition((int)log.size())) {
/* We need a repaint */
this->SetWidgetDirty(WID_SCRD_SCROLLBAR);
this->SetWidgetDirty(WID_SCRD_LOG_PANEL);
}
}
this->last_vscroll_pos = this->vscroll->GetPosition();
}
void SetStringParameters(int widget) const override
{
if (widget != WID_SCRD_NAME_TEXT) return;
if (script_debug_company == OWNER_DEITY) {
if (this->filter.script_debug_company == OWNER_DEITY) {
const GameInfo *info = Game::GetInfo();
assert(info != nullptr);
SetDParam(0, STR_AI_DEBUG_NAME_AND_VERSION);
SetDParamStr(1, info->GetName());
SetDParam(2, info->GetVersion());
} else if (script_debug_company == INVALID_COMPANY || !Company::IsValidAiID(script_debug_company)) {
} else if (this->filter.script_debug_company == INVALID_COMPANY || !Company::IsValidAiID(this->filter.script_debug_company)) {
SetDParam(0, STR_EMPTY);
} else {
const AIInfo *info = Company::Get(script_debug_company)->ai_info;
const AIInfo *info = Company::Get(this->filter.script_debug_company)->ai_info;
assert(info != nullptr);
SetDParam(0, STR_AI_DEBUG_NAME_AND_VERSION);
SetDParamStr(1, info->GetName());
@@ -913,9 +877,40 @@ struct ScriptDebugWindow : public Window {
void DrawWidget(const Rect &r, int widget) const override
{
if (script_debug_company == INVALID_COMPANY) return;
switch (widget) {
case WID_SCRD_LOG_PANEL:
this->DrawWidgetLog(r);
break;
if (widget != WID_SCRD_LOG_PANEL) return;
default:
if (IsInsideBS(widget, WID_SCRD_COMPANY_BUTTON_START, MAX_COMPANIES)) {
this->DrawWidgetCompanyButton(r, widget, WID_SCRD_COMPANY_BUTTON_START);
}
break;
}
}
/**
* Draw a company button icon.
* @param r Rect area to draw within.
* @param widget Widget index to start.
* @param start Widget index of first company button.
*/
void DrawWidgetCompanyButton(const Rect &r, int widget, int start) const
{
if (this->IsWidgetDisabled(widget)) return;
CompanyID cid = (CompanyID)(widget - start);
Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
DrawCompanyIcon(cid, CenterBounds(r.left, r.right, sprite_size.width), CenterBounds(r.top, r.bottom, sprite_size.height));
}
/**
* Draw the AI/GS log.
* @param r Rect area to draw within.
*/
void DrawWidgetLog(const Rect &r) const
{
if (this->filter.script_debug_company == INVALID_COMPANY) return;
ScriptLogTypes::LogData &log = this->GetLogData();
if (log.empty()) return;
@@ -941,20 +936,96 @@ struct ScriptDebugWindow : public Window {
if (colour == TC_BLACK) colour = TC_WHITE; // Make black text readable by inverting it to white.
}
DrawString(tr, line.text, colour, SA_LEFT | SA_FORCE);
DrawString(-this->hscroll->GetPosition(), tr.right, tr.top, line.text, colour, SA_LEFT | SA_FORCE);
tr.top += this->resize.step_height;
}
}
/**
* Update the scrollbar and scroll position of the log panel.
*/
void UpdateLogScroll()
{
this->SetWidgetsDisabledState(this->filter.script_debug_company == INVALID_COMPANY, WID_SCRD_VSCROLLBAR, WID_SCRD_HSCROLLBAR);
if (this->filter.script_debug_company == INVALID_COMPANY) return;
ScriptLogTypes::LogData &log = this->GetLogData();
int scroll_count = (int)log.size();
if (this->vscroll->GetCount() != scroll_count) {
this->vscroll->SetCount(scroll_count);
/* We need a repaint */
this->SetWidgetDirty(WID_SCRD_VSCROLLBAR);
}
if (log.empty()) return;
/* Detect when the user scrolls the window. Enable autoscroll when the bottom-most line becomes visible. */
if (this->last_vscroll_pos != this->vscroll->GetPosition()) {
this->autoscroll = this->vscroll->GetPosition() + this->vscroll->GetCapacity() >= (int)log.size();
}
if (this->autoscroll && this->vscroll->SetPosition((int)log.size())) {
/* We need a repaint */
this->SetWidgetDirty(WID_SCRD_VSCROLLBAR);
this->SetWidgetDirty(WID_SCRD_LOG_PANEL);
}
this->last_vscroll_pos = this->vscroll->GetPosition();
}
/**
* Update state of all Company (AI) buttons.
*/
void UpdateAIButtonsState()
{
/* Update company buttons */
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
/* Mark dead/paused AIs by setting the background colour. */
bool valid = Company::IsValidAiID(i);
bool dead = valid && Company::Get(i)->ai_instance->IsDead();
bool paused = valid && Company::Get(i)->ai_instance->IsPaused();
NWidgetCore *button = this->GetWidget<NWidgetCore>(i + WID_SCRD_COMPANY_BUTTON_START);
button->SetDisabled(!valid);
button->SetLowered(this->filter.script_debug_company == i);
SetScriptButtonColour(*button, dead, paused);
}
}
/**
* Update state of game script button.
*/
void UpdateGSButtonState()
{
GameInstance *game = Game::GetInstance();
bool valid = game != nullptr;
bool dead = valid && game->IsDead();
bool paused = valid && game->IsPaused();
NWidgetCore *button = this->GetWidget<NWidgetCore>(WID_SCRD_SCRIPT_GAME);
button->SetDisabled(!valid);
button->SetLowered(this->filter.script_debug_company == OWNER_DEITY);
SetScriptButtonColour(*button, dead, paused);
}
/**
* Change all settings to select another Script.
* @param show_ai The new AI to show.
* @param new_window Open the script in a new window.
*/
void ChangeToScript(CompanyID show_script)
void ChangeToScript(CompanyID show_script, bool new_window = false)
{
if (!this->IsValidDebugCompany(show_script)) return;
script_debug_company = show_script;
if (new_window) {
ScriptDebugWindow::initial_state = this->filter;
ShowScriptDebugWindow(show_script, true);
return;
}
this->filter.script_debug_company = show_script;
this->highlight_row = -1; // The highlight of one Script make little sense for another Script.
@@ -974,16 +1045,16 @@ struct ScriptDebugWindow : public Window {
/* Check which button is clicked */
if (IsInsideMM(widget, WID_SCRD_COMPANY_BUTTON_START, WID_SCRD_COMPANY_BUTTON_END + 1)) {
ChangeToScript((CompanyID)(widget - WID_SCRD_COMPANY_BUTTON_START));
ChangeToScript((CompanyID)(widget - WID_SCRD_COMPANY_BUTTON_START), _ctrl_pressed);
}
switch (widget) {
case WID_SCRD_SCRIPT_GAME:
ChangeToScript(OWNER_DEITY);
ChangeToScript(OWNER_DEITY, _ctrl_pressed);
break;
case WID_SCRD_RELOAD_TOGGLE:
if (script_debug_company == OWNER_DEITY) {
if (this->filter.script_debug_company == OWNER_DEITY) {
if (UserIsAllowedToChangeGameScript()) {
Game::Uninitialize(true);
Game::StartNew();
@@ -992,31 +1063,31 @@ struct ScriptDebugWindow : public Window {
break;
}
/* First kill the company of the AI, then start a new one. This should start the current AI again */
DoCommandP(0, CCA_DELETE | script_debug_company << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
DoCommandP(0, CCA_NEW_AI | script_debug_company << 16, 0, CMD_COMPANY_CTRL);
DoCommandP(0, CCA_DELETE | this->filter.script_debug_company << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
DoCommandP(0, CCA_NEW_AI | this->filter.script_debug_company << 16, 0, CMD_COMPANY_CTRL);
break;
case WID_SCRD_SETTINGS:
ShowScriptSettingsWindow(script_debug_company);
ShowScriptSettingsWindow(this->filter.script_debug_company);
break;
case WID_SCRD_BREAK_STR_ON_OFF_BTN:
this->break_check_enabled = !this->break_check_enabled;
this->filter.break_check_enabled = !this->filter.break_check_enabled;
this->InvalidateData(-1);
break;
case WID_SCRD_MATCH_CASE_BTN:
this->case_sensitive_break_check = !this->case_sensitive_break_check;
this->filter.case_sensitive_break_check = !this->filter.case_sensitive_break_check;
this->InvalidateData(-1);
break;
case WID_SCRD_CONTINUE_BTN:
/* Unpause current AI / game script and mark the corresponding script button dirty. */
if (!this->IsDead()) {
if (script_debug_company == OWNER_DEITY) {
if (this->filter.script_debug_company == OWNER_DEITY) {
Game::Unpause();
} else {
AI::Unpause(script_debug_company);
AI::Unpause(this->filter.script_debug_company);
}
}
@@ -1048,8 +1119,8 @@ struct ScriptDebugWindow : public Window {
if (wid != WID_SCRD_BREAK_STR_EDIT_BOX) return;
/* Save the current string to static member so it can be restored next time the window is opened. */
this->break_string = this->break_editbox.text.buf;
break_string_filter.SetFilterTerm(this->break_string);
this->filter.break_string = this->break_editbox.text.buf;
this->break_string_filter.SetFilterTerm(this->filter.break_string);
}
/**
@@ -1062,7 +1133,9 @@ struct ScriptDebugWindow : public Window {
{
/* If the log message is related to the active company tab, check the break string.
* This needs to be done in gameloop-scope, so the AI is suspended immediately. */
if (!gui_scope && data == script_debug_company && this->IsValidDebugCompany(script_debug_company) && this->break_check_enabled && !this->break_string_filter.IsEmpty()) {
if (!gui_scope && data == this->filter.script_debug_company &&
this->IsValidDebugCompany(this->filter.script_debug_company) &&
this->filter.break_check_enabled && !this->break_string_filter.IsEmpty()) {
/* Get the log instance of the active company */
ScriptLogTypes::LogData &log = this->GetLogData();
@@ -1072,10 +1145,10 @@ struct ScriptDebugWindow : public Window {
if (this->break_string_filter.GetState()) {
/* Pause execution of script. */
if (!this->IsDead()) {
if (script_debug_company == OWNER_DEITY) {
if (this->filter.script_debug_company == OWNER_DEITY) {
Game::Pause();
} else {
AI::Pause(script_debug_company);
AI::Pause(this->filter.script_debug_company);
}
}
@@ -1094,42 +1167,42 @@ struct ScriptDebugWindow : public Window {
this->SelectValidDebugCompany();
this->vscroll->SetCount(script_debug_company != INVALID_COMPANY ? this->GetLogData().size() : 0);
/* Update company buttons */
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
this->SetWidgetDisabledState(i + WID_SCRD_COMPANY_BUTTON_START, !Company::IsValidAiID(i));
this->SetWidgetLoweredState(i + WID_SCRD_COMPANY_BUTTON_START, script_debug_company == i);
uint max_width = 0;
if (this->filter.script_debug_company != INVALID_COMPANY) {
for (auto &line : this->GetLogData()) {
if (line.width == 0 || data == -1) line.width = GetStringBoundingBox(line.text).width;
max_width = std::max(max_width, line.width);
}
}
this->SetWidgetDisabledState(WID_SCRD_SCRIPT_GAME, Game::GetGameInstance() == nullptr);
this->SetWidgetLoweredState(WID_SCRD_SCRIPT_GAME, script_debug_company == OWNER_DEITY);
this->vscroll->SetCount(this->filter.script_debug_company != INVALID_COMPANY ? this->GetLogData().size() : 0);
this->hscroll->SetCount(max_width + WidgetDimensions::scaled.frametext.Horizontal());
this->SetWidgetLoweredState(WID_SCRD_BREAK_STR_ON_OFF_BTN, this->break_check_enabled);
this->SetWidgetLoweredState(WID_SCRD_MATCH_CASE_BTN, this->case_sensitive_break_check);
this->UpdateAIButtonsState();
this->UpdateGSButtonState();
this->SetWidgetDisabledState(WID_SCRD_SETTINGS, script_debug_company == INVALID_COMPANY);
this->SetWidgetLoweredState(WID_SCRD_BREAK_STR_ON_OFF_BTN, this->filter.break_check_enabled);
this->SetWidgetLoweredState(WID_SCRD_MATCH_CASE_BTN, this->filter.case_sensitive_break_check);
this->SetWidgetDisabledState(WID_SCRD_SETTINGS, this->filter.script_debug_company == INVALID_COMPANY);
extern CompanyID _local_company;
this->SetWidgetDisabledState(WID_SCRD_RELOAD_TOGGLE, script_debug_company == INVALID_COMPANY ||
script_debug_company == _local_company || (script_debug_company == OWNER_DEITY && !UserIsAllowedToChangeGameScript()));
this->SetWidgetDisabledState(WID_SCRD_CONTINUE_BTN, script_debug_company == INVALID_COMPANY ||
(script_debug_company == OWNER_DEITY ? !Game::IsPaused() : !AI::IsPaused(script_debug_company)));
this->SetWidgetDisabledState(WID_SCRD_RELOAD_TOGGLE,
this->filter.script_debug_company == INVALID_COMPANY ||
this->filter.script_debug_company == _local_company ||
(this->filter.script_debug_company == OWNER_DEITY && !UserIsAllowedToChangeGameScript()));
this->SetWidgetDisabledState(WID_SCRD_CONTINUE_BTN, this->filter.script_debug_company == INVALID_COMPANY ||
(this->filter.script_debug_company == OWNER_DEITY ? !Game::IsPaused() : !AI::IsPaused(this->filter.script_debug_company)));
}
void OnResize() override
{
this->vscroll->SetCapacityFromWidget(this, WID_SCRD_LOG_PANEL, WidgetDimensions::scaled.framerect.Vertical());
this->hscroll->SetCapacityFromWidget(this, WID_SCRD_LOG_PANEL);
}
static HotkeyList hotkeys;
};
CompanyID ScriptDebugWindow::script_debug_company = INVALID_COMPANY;
std::string ScriptDebugWindow::break_string;
bool ScriptDebugWindow::break_check_enabled = true;
bool ScriptDebugWindow::case_sensitive_break_check = false;
StringFilter ScriptDebugWindow::break_string_filter(&ScriptDebugWindow::case_sensitive_break_check, false);
/** Make a number of rows with buttons for each company for the Script debug window. */
NWidgetBase *MakeCompanyButtonRowsScriptDebug(int *biggest_index)
{
@@ -1197,7 +1270,7 @@ static const NWidgetPart _nested_script_debug_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(NWID_VERTICAL),
/* Log panel */
NWidget(WWT_PANEL, COLOUR_GREY, WID_SCRD_LOG_PANEL), SetMinimalSize(287, 180), SetResize(1, 1), SetScrollbar(WID_SCRD_SCROLLBAR),
NWidget(WWT_PANEL, COLOUR_GREY, WID_SCRD_LOG_PANEL), SetMinimalSize(287, 180), SetResize(1, 1), SetScrollbar(WID_SCRD_VSCROLLBAR),
EndContainer(),
/* Break string widgets */
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCRD_BREAK_STRING_WIDGETS),
@@ -1213,9 +1286,10 @@ static const NWidgetPart _nested_script_debug_widgets[] = {
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCRD_CONTINUE_BTN), SetMinimalSize(100, 0), SetFill(0, 1), SetDataTip(STR_AI_DEBUG_CONTINUE, STR_AI_DEBUG_CONTINUE_TOOLTIP),
EndContainer(),
EndContainer(),
NWidget(NWID_HSCROLLBAR, COLOUR_GREY, WID_SCRD_HSCROLLBAR),
EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCRD_SCROLLBAR),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCRD_VSCROLLBAR),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
EndContainer(),
@@ -1233,14 +1307,32 @@ static WindowDesc _script_debug_desc(__FILE__, __LINE__,
/**
* Open the Script debug window and select the given company.
* @param show_company Display debug information about this AI company.
* @param new_window Show in new window instead of existing window.
*/
Window *ShowScriptDebugWindow(CompanyID show_company)
Window *ShowScriptDebugWindow(CompanyID show_company, bool new_window)
{
if (!_networking || _network_server) {
ScriptDebugWindow *w = (ScriptDebugWindow *)BringWindowToFrontById(WC_SCRIPT_DEBUG, 0);
if (w == nullptr) w = new ScriptDebugWindow(&_script_debug_desc, 0);
if (show_company != INVALID_COMPANY) w->ChangeToScript(show_company);
return w;
int i = 0;
if (new_window) {
/* find next free window number for script debug */
while (FindWindowById(WC_SCRIPT_DEBUG, i) != nullptr) i++;
} else {
/* Find existing window showing show_company. */
for (Window *w : Window::Iterate()) {
if (w->window_class == WC_SCRIPT_DEBUG && static_cast<ScriptDebugWindow *>(w)->filter.script_debug_company == show_company) {
return BringWindowToFrontById(w->window_class, w->window_number);
}
}
/* Maybe there's a window showing a different company which can be switched. */
ScriptDebugWindow *w = static_cast<ScriptDebugWindow *>(FindWindowByClass(WC_SCRIPT_DEBUG));
if (w != nullptr) {
BringWindowToFrontById(w->window_class, w->window_number);
w->ChangeToScript(show_company);
return w;
}
}
return new ScriptDebugWindow(&_script_debug_desc, i, show_company);
} else {
ShowErrorMessage(STR_ERROR_AI_DEBUG_SERVER_ONLY, INVALID_STRING_ID, WL_INFO);
}
@@ -1253,7 +1345,7 @@ Window *ShowScriptDebugWindow(CompanyID show_company)
*/
void InitializeScriptGui()
{
ScriptDebugWindow::script_debug_company = INVALID_COMPANY;
ScriptDebugWindow::initial_state.script_debug_company = INVALID_COMPANY;
}
/** Open the AI debug window if one of the AI scripts has crashed. */