Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-09-28 02:33:49 +05:00
926 changed files with 37901 additions and 27368 deletions
+77 -134
View File
@@ -27,6 +27,7 @@
#include "game/game_gui.hpp"
#include "gfx_func.h"
#include "core/geometry_func.hpp"
#include "core/string_consumer.hpp"
#include "language.h"
#include "rev.h"
#include "highscore.h"
@@ -119,53 +120,54 @@ struct SelectGameWindow : public Window {
intro_viewport_commands.clear();
/* Regular expression matching the commands: T, spaces, integer, spaces, flags, spaces, integer */
const char *sign_langauge = "^T\\s*([0-9]+)\\s*([-+A-Z0-9]+)\\s*([0-9]+)";
std::regex re(sign_langauge, std::regex_constants::icase);
static const std::string sign_language = "^T\\s*([0-9]+)\\s*([-+A-Z0-9]+)\\s*([0-9]+)";
std::regex re(sign_language, std::regex_constants::icase);
/* List of signs successfully parsed to delete afterwards. */
std::vector<SignID> signs_to_delete;
for (const Sign *sign : Sign::Iterate()) {
std::smatch match;
if (std::regex_search(sign->name, match, re)) {
IntroGameViewportCommand vc;
/* Sequence index from the first matching group. */
vc.command_index = std::stoi(match[1].str());
/* Sign coordinates for positioning. */
vc.position = RemapCoords(sign->x, sign->y, sign->z);
/* Delay from the third matching group. */
vc.delay = std::stoi(match[3].str()) * 1000; // milliseconds
if (!std::regex_search(sign->name, match, re)) continue;
/* Parse flags from second matching group. */
enum IdType : uint8_t {
ID_NONE, ID_VEHICLE
} id_type = ID_NONE;
for (char c : match[2].str()) {
if (isdigit(c)) {
if (id_type == ID_VEHICLE) {
vc.vehicle = static_cast<VehicleID>(vc.vehicle.base() * 10 + (c - '0'));
}
} else {
id_type = ID_NONE;
switch (toupper(c)) {
case '-': vc.zoom_adjust = +1; break;
case '+': vc.zoom_adjust = -1; break;
case 'T': vc.align_v = IntroGameViewportCommand::TOP; break;
case 'M': vc.align_v = IntroGameViewportCommand::MIDDLE; break;
case 'B': vc.align_v = IntroGameViewportCommand::BOTTOM; break;
case 'L': vc.align_h = IntroGameViewportCommand::LEFT; break;
case 'C': vc.align_h = IntroGameViewportCommand::CENTRE; break;
case 'R': vc.align_h = IntroGameViewportCommand::RIGHT; break;
case 'P': vc.pan_to_next = true; break;
case 'V': id_type = ID_VEHICLE; vc.vehicle = VehicleID::Begin(); break;
}
}
}
/* Successfully parsed, store. */
intro_viewport_commands.push_back(vc);
signs_to_delete.push_back(sign->index);
IntroGameViewportCommand vc;
/* Sequence index from the first matching group. */
if (auto value = ParseInteger<int>(match[1].str()); value.has_value()) {
vc.command_index = *value;
} else {
continue;
}
/* Sign coordinates for positioning. */
vc.position = RemapCoords(sign->x, sign->y, sign->z);
/* Delay from the third matching group. */
if (auto value = ParseInteger<uint>(match[3].str()); value.has_value()) {
vc.delay = *value * 1000; // milliseconds
} else {
continue;
}
/* Parse flags from second matching group. */
auto flags = match[2].str();
StringConsumer consumer{flags};
while (consumer.AnyBytesLeft()) {
auto c = consumer.ReadUtf8();
switch (toupper(c)) {
case '-': vc.zoom_adjust = +1; break;
case '+': vc.zoom_adjust = -1; break;
case 'T': vc.align_v = IntroGameViewportCommand::TOP; break;
case 'M': vc.align_v = IntroGameViewportCommand::MIDDLE; break;
case 'B': vc.align_v = IntroGameViewportCommand::BOTTOM; break;
case 'L': vc.align_h = IntroGameViewportCommand::LEFT; break;
case 'C': vc.align_h = IntroGameViewportCommand::CENTRE; break;
case 'R': vc.align_h = IntroGameViewportCommand::RIGHT; break;
case 'P': vc.pan_to_next = true; break;
case 'V': vc.vehicle = static_cast<VehicleID>(consumer.ReadIntegerBase<uint32_t>(10, VehicleID::Invalid().base())); break;
}
}
/* Successfully parsed, store. */
intro_viewport_commands.push_back(vc);
signs_to_delete.push_back(sign->index);
}
/* Sort the commands by sequence index. */
@@ -255,20 +257,6 @@ struct SelectGameWindow : public Window {
if (intro_viewport_commands.size() == 1 && vc.vehicle == VehicleID::Invalid()) intro_viewport_commands.clear();
}
/**
* 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.
*/
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
{
if (!gui_scope) return;
this->SetWidgetLoweredState(WID_SGI_TEMPERATE_LANDSCAPE, _settings_newgame.game_creation.landscape == LandscapeType::Temperate);
this->SetWidgetLoweredState(WID_SGI_ARCTIC_LANDSCAPE, _settings_newgame.game_creation.landscape == LandscapeType::Arctic);
this->SetWidgetLoweredState(WID_SGI_TROPIC_LANDSCAPE, _settings_newgame.game_creation.landscape == LandscapeType::Tropic);
this->SetWidgetLoweredState(WID_SGI_TOYLAND_LANDSCAPE, _settings_newgame.game_creation.landscape == LandscapeType::Toyland);
}
void OnInit() override
{
bool missing_sprites = _missing_extra_graphics > 0 && !IsReleasedVersion();
@@ -291,17 +279,6 @@ struct SelectGameWindow : public Window {
}
}
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
case WID_SGI_TEMPERATE_LANDSCAPE: case WID_SGI_ARCTIC_LANDSCAPE:
case WID_SGI_TROPIC_LANDSCAPE: case WID_SGI_TOYLAND_LANDSCAPE:
size.width += WidgetDimensions::scaled.fullbevel.Horizontal();
size.height += WidgetDimensions::scaled.fullbevel.Vertical();
break;
}
}
void OnResize() override
{
bool changed = false;
@@ -319,23 +296,31 @@ struct SelectGameWindow : public Window {
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
{
/* Do not create a network server when you (just) have closed one of the game
* creation/load windows for the network server. */
if (IsInsideMM(widget, WID_SGI_GENERATE_GAME, WID_SGI_EDIT_SCENARIO + 1)) _is_network_server = false;
switch (widget) {
case WID_SGI_GENERATE_GAME:
_is_network_server = false;
if (citymania::_fn_mod) {
StartNewGameWithoutGUI(GENERATE_NEW_SEED);
} else {
ShowGenerateLandscape();
}
break;
case WID_SGI_LOAD_GAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD); break;
case WID_SGI_PLAY_SCENARIO: ShowSaveLoadDialog(FT_SCENARIO, SLO_LOAD); break;
case WID_SGI_PLAY_HEIGHTMAP: ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_LOAD); break;
case WID_SGI_EDIT_SCENARIO: StartScenarioEditor(); break;
case WID_SGI_LOAD_GAME:
_is_network_server = false;
ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD);
break;
case WID_SGI_PLAY_SCENARIO:
_is_network_server = false;
ShowSaveLoadDialog(FT_SCENARIO, SLO_LOAD);
break;
case WID_SGI_PLAY_HEIGHTMAP:
_is_network_server = false;
ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_LOAD);
break;
case WID_SGI_EDIT_SCENARIO:
_is_network_server = false;
StartScenarioEditor();
break;
case WID_SGI_PLAY_NETWORK:
if (!_network_available) {
@@ -345,16 +330,9 @@ struct SelectGameWindow : public Window {
}
break;
case WID_SGI_TEMPERATE_LANDSCAPE: case WID_SGI_ARCTIC_LANDSCAPE:
case WID_SGI_TROPIC_LANDSCAPE: case WID_SGI_TOYLAND_LANDSCAPE:
SetNewLandscapeType(LandscapeType(widget - WID_SGI_TEMPERATE_LANDSCAPE));
break;
case WID_SGI_OPTIONS: ShowGameOptions(); break;
case WID_SGI_HIGHSCORE: ShowHighscoreTable(); break;
case WID_SGI_HELP: ShowHelpWindow(); break;
case WID_SGI_SETTINGS_OPTIONS:ShowGameSettings(); break;
case WID_SGI_GRF_SETTINGS: ShowNewGRFSettings(true, true, false, _grfconfig_newgame); break;
case WID_SGI_CONTENT_DOWNLOAD:
if (!_network_available) {
ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR);
@@ -362,8 +340,6 @@ struct SelectGameWindow : public Window {
ShowNetworkContentListWindow();
}
break;
case WID_SGI_AI_SETTINGS: ShowAIConfigWindow(); break;
case WID_SGI_GS_SETTINGS: ShowGSConfigWindow(); break;
case WID_SGI_EXIT: HandleExitGameRequest(); break;
}
}
@@ -374,32 +350,18 @@ static constexpr NWidgetPart _nested_select_game_widgets[] = {
NWidget(WWT_PANEL, COLOUR_BROWN),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0), SetPadding(WidgetDimensions::unscaled.sparse),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0),
/* 'New Game' and 'Load Game' buttons */
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GENERATE_GAME), SetStringTip(STR_INTRO_NEW_GAME, STR_INTRO_TOOLTIP_NEW_GAME), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_LOAD_GAME), SetStringTip(STR_INTRO_LOAD_GAME, STR_INTRO_TOOLTIP_LOAD_GAME), SetFill(1, 0),
EndContainer(),
/* 'Play Scenario' and 'Play Heightmap' buttons */
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_PLAY_SCENARIO), SetStringTip(STR_INTRO_PLAY_SCENARIO, STR_INTRO_TOOLTIP_PLAY_SCENARIO), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_PLAY_HEIGHTMAP), SetStringTip(STR_INTRO_PLAY_HEIGHTMAP, STR_INTRO_TOOLTIP_PLAY_HEIGHTMAP), SetFill(1, 0),
EndContainer(),
/* 'Scenario Editor' and 'Multiplayer' buttons */
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_EDIT_SCENARIO), SetStringTip(STR_INTRO_SCENARIO_EDITOR, STR_INTRO_TOOLTIP_SCENARIO_EDITOR), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_PLAY_NETWORK), SetStringTip(STR_INTRO_MULTIPLAYER, STR_INTRO_TOOLTIP_MULTIPLAYER), SetFill(1, 0),
EndContainer(),
/* Single player */
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_GENERATE_GAME), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_LANDSCAPING, STR_INTRO_NEW_GAME, STR_INTRO_TOOLTIP_NEW_GAME), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_PLAY_HEIGHTMAP), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_SHOW_COUNTOURS, STR_INTRO_PLAY_HEIGHTMAP, STR_INTRO_TOOLTIP_PLAY_HEIGHTMAP), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_PLAY_SCENARIO), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_SUBSIDIES, STR_INTRO_PLAY_SCENARIO, STR_INTRO_TOOLTIP_PLAY_SCENARIO), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_LOAD_GAME), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_SAVE, STR_INTRO_LOAD_GAME, STR_INTRO_TOOLTIP_LOAD_GAME), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_HIGHSCORE), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_COMPANY_LEAGUE, STR_INTRO_HIGHSCORE, STR_INTRO_TOOLTIP_HIGHSCORE), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
EndContainer(),
/* Climate selection buttons */
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0), SetPIPRatio(1, 1, 1),
NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, WID_SGI_TEMPERATE_LANDSCAPE), SetSpriteTip(SPR_SELECT_TEMPERATE, STR_INTRO_TOOLTIP_TEMPERATE),
NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, WID_SGI_ARCTIC_LANDSCAPE), SetSpriteTip(SPR_SELECT_SUB_ARCTIC, STR_INTRO_TOOLTIP_SUB_ARCTIC_LANDSCAPE),
NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, WID_SGI_TROPIC_LANDSCAPE), SetSpriteTip(SPR_SELECT_SUB_TROPICAL, STR_INTRO_TOOLTIP_SUB_TROPICAL_LANDSCAPE),
NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, WID_SGI_TOYLAND_LANDSCAPE), SetSpriteTip(SPR_SELECT_TOYLAND, STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE),
/* Multi player */
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_PLAY_NETWORK), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_COMPANY_GENERAL, STR_INTRO_MULTIPLAYER, STR_INTRO_TOOLTIP_MULTIPLAYER), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SGI_BASESET_SELECTION),
@@ -414,42 +376,23 @@ static constexpr NWidgetPart _nested_select_game_widgets[] = {
EndContainer(),
EndContainer(),
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0),
/* 'Game Options' and 'Settings' buttons */
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_OPTIONS), SetStringTip(STR_INTRO_GAME_OPTIONS, STR_INTRO_TOOLTIP_GAME_OPTIONS), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_SETTINGS_OPTIONS), SetStringTip(STR_INTRO_CONFIG_SETTINGS_TREE, STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE), SetFill(1, 0),
EndContainer(),
/* 'AI Settings' and 'Game Script Settings' buttons */
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_AI_SETTINGS), SetStringTip(STR_INTRO_AI_SETTINGS, STR_INTRO_TOOLTIP_AI_SETTINGS), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GS_SETTINGS), SetStringTip(STR_INTRO_GAMESCRIPT_SETTINGS, STR_INTRO_TOOLTIP_GAMESCRIPT_SETTINGS), SetFill(1, 0),
EndContainer(),
/* 'Check Online Content' and 'NewGRF Settings' buttons */
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_CONTENT_DOWNLOAD), SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_GRF_SETTINGS), SetStringTip(STR_INTRO_NEWGRF_SETTINGS, STR_INTRO_TOOLTIP_NEWGRF_SETTINGS), SetFill(1, 0),
EndContainer(),
/* Other */
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_OPTIONS), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_SETTINGS, STR_INTRO_GAME_OPTIONS, STR_INTRO_TOOLTIP_GAME_OPTIONS), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_CONTENT_DOWNLOAD), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_SHOW_VEHICLES, STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_EDIT_SCENARIO), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_SMALLMAP, STR_INTRO_SCENARIO_EDITOR, STR_INTRO_TOOLTIP_SCENARIO_EDITOR), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
NWidget(WWT_PUSHIMGTEXTBTN, COLOUR_ORANGE, WID_SGI_HELP), SetToolbarMinimalSize(1), SetSpriteStringTip(SPR_IMG_QUERY, STR_INTRO_HELP, STR_INTRO_TOOLTIP_HELP), SetAlignment(SA_LEFT | SA_VERT_CENTER), SetFill(1, 0),
EndContainer(),
/* 'Help and Manuals' and 'Highscore Table' buttons */
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_HELP), SetStringTip(STR_INTRO_HELP, STR_INTRO_TOOLTIP_HELP), SetFill(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_HIGHSCORE), SetStringTip(STR_INTRO_HIGHSCORE, STR_INTRO_TOOLTIP_HIGHSCORE), SetFill(1, 0),
EndContainer(),
/* 'Exit' button */
NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_EXIT), SetMinimalSize(128, 0), SetStringTip(STR_INTRO_QUIT, STR_INTRO_TOOLTIP_QUIT),
NWidget(NWID_VERTICAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_EXIT), SetToolbarMinimalSize(1), SetStringTip(STR_INTRO_QUIT, STR_INTRO_TOOLTIP_QUIT),
EndContainer(),
EndContainer(),
EndContainer(),
};
static WindowDesc _select_game_desc(
WDP_CENTER, nullptr, 0, 0,
WDP_CENTER, {}, 0, 0,
WC_SELECT_GAME, WC_NONE,
WindowDefaultFlag::NoClose,
_nested_select_game_widgets