diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index ba365bfe4f..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,7 +0,0 @@ -## Version of OpenTTD - -## Expected result - -## Actual result - -## Steps to reproduce diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000000..0af8a89791 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,17 @@ +--- +name: Bugs +about: Found a bug in OpenTTD? +title: "Bug Report" +--- + +## Version of OpenTTD + + +## Expected result + + +## Actual result + + +## Steps to reproduce + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..2fd749087a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: +- name: Suggestions and ideas? + url: https://www.tt-forums.net/viewforum.php?f=32 + about: Have a suggestion or an idea for a cool new feature? Post them on our forum! diff --git a/.github/ISSUE_TEMPLATE/crash.md b/.github/ISSUE_TEMPLATE/crash.md new file mode 100644 index 0000000000..f7d127e930 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/crash.md @@ -0,0 +1,12 @@ +--- +name: Crash +about: Did OpenTTD crash? +title: "Crash Report" +--- + + +## Version of OpenTTD + + +## Steps to reproduce + diff --git a/CMakeLists.txt b/CMakeLists.txt index a9a22686df..3148abc6af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,9 @@ if(NOT BINARY_NAME) set(BINARY_NAME openttd) endif() -project(${BINARY_NAME}) +project(${BINARY_NAME} + VERSION 1.12.0 +) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the build directory. You may need to delete \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" first.") @@ -92,6 +94,9 @@ if(OPTION_TOOLS_ONLY) ${CMAKE_COMMAND} -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR} + -DREV_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR} + -DREV_MINOR=${CMAKE_PROJECT_VERSION_MINOR} + -DREV_BUILD=${CMAKE_PROJECT_VERSION_PATCH} $<$:-DWIN32=TRUE> -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} @@ -463,6 +468,9 @@ add_custom_target(find_version ${CMAKE_COMMAND} -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR} + -DREV_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR} + -DREV_MINOR=${CMAKE_PROJECT_VERSION_MINOR} + -DREV_BUILD=${CMAKE_PROJECT_VERSION_PATCH} -DCONFIGURE_DEFINES="${CFG_DEFS}" $<$:-DWIN32=TRUE> -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake" diff --git a/COMPILING.md b/COMPILING.md index 9e63c3c9bc..1037ecc609 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -2,27 +2,28 @@ ## Required/optional libraries -The following libraries are used by OpenTTD for: +OpenTTD makes use of the following external libraries: -- zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads, +- (encouraged) zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads, heightmaps -- liblzo2: (de)compressing of old (pre 0.3.0) savegames -- liblzma: (de)compressing of savegames (1.1.0 and later) -- libzstd: (de)compressing of savegames (1.11.0 and later) -- libpng: making screenshots and loading heightmaps +- (encouraged) liblzma: (de)compressing of savegames (1.1.0 and later) +- (encouraged) libpng: making screenshots and loading heightmaps +- (optional) liblzo2: (de)compressing of old (pre 0.3.0) savegames +- (optional) libzstd: (de)compressing of multiplayer join savegames, if available + +For Linux, the following additional libraries are used (for non-dedicated only): + +- libSDL2: hardware access (video, sound, mouse) - libfreetype: loading generic fonts and rendering them - libfontconfig: searching for fonts, resolving font names to actual fonts - libicu: handling of right-to-left scripts (e.g. Arabic and Persian) and - natural sorting of strings (Linux only) -- libSDL2: hardware access (video, sound, mouse) (not required for Windows or macOS) + natural sorting of strings OpenTTD does not require any of the libraries to be present, but without liblzma you cannot open most recent savegames and without zlib you cannot open most older savegames or use the content downloading system. -Without libSDL/liballegro on non-Windows and non-macOS machines you have -no graphical user interface; you would be building a dedicated server. -## Windows: +## Windows You need Microsoft Visual Studio 2017 or more recent. @@ -79,6 +80,8 @@ files himself via the `ZERO_CHECK` project. ## All other platforms Minimum required version of CMake is 3.9. +By default this produces a Debug build with assertations enabled. +This is a far slower build than release builds. ```bash mkdir build @@ -90,6 +93,25 @@ make For more information on how to use CMake (including how to make Release builds), we urge you to read [their excellent manual](https://cmake.org/cmake/help/latest/guide/user-interaction/index.html). +## CMake Options + +Via CMake, several options can be influenced to get different types of +builds. + +- `-DCMAKE_BUILD_TYPE=RelWithDebInfo`: build a release build. This is + significant faster than a debug build, but has far less useful information + in case of a crash. +- `-DOPTION_DEDICATED=ON`: build OpenTTD without a GUI. Useful if you are + running a headless server, as it requires less libraries to operate. +- `-DOPTION_USE_ASSERTS=OFF`: disable asserts. Use with care, as assert + statements capture early signs of trouble. Release builds have them + disabled by default. +- `-DOPTION_USE_THREADS=OFF`: disable the use of threads. This will block + the interface in many places, and in general gives a worse experience of + the game. Use with care. +- `-DOPTION_TOOLS_ONLY=ON`: only build tools like `strgen`. Does not build + the game itself. Useful for cross-compiling. + ## Supported compilers Every compiler that is supported by CMake and supports C++17, should be diff --git a/CREDITS.md b/CREDITS.md index 8c20a4ac18..33f8836265 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -14,7 +14,6 @@ - Ingo von Borstel (planetmaker) - General coding, Support (since 1.1) - Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5) - José Soler (Terkhen) - General coding (since 1.0) -- Leif Linse (Zuu) - AI/Game Script (since 1.2) ### Inactive Developers: @@ -28,6 +27,7 @@ - Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5) - Patric Stout (TrueBrain) - NoProgrammer (0.3 - 1.2), sys op (active) - Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3) +- Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6) ### Retired Developers: diff --git a/README.md b/README.md index 93c7221ab3..d31d15c10e 100644 --- a/README.md +++ b/README.md @@ -367,15 +367,13 @@ OpenTTD has a [community-maintained wiki](https://wiki.openttd.org/), including OpenTTD has been ported to several platforms and operating systems. -The currently working platforms are: +The currently supported platforms are: -- FreeBSD (SDL) -- Haiku (SDL) -- Linux (SDL) -- macOS (universal) (Cocoa video and sound drivers) -- OpenBSD (SDL) -- OS/2 (SDL) -- Windows (Win32 GDI (faster) or SDL) +- Linux (SDL (OpenGL and non-OpenGL)) +- macOS (universal) (Cocoa) +- Windows (Win32 GDI / OpenGL) + +Other platforms may also work (in particular various BSD systems), but we don't actively test or maintain these. ### 1.3.1) Legacy support Platforms, languages and compilers change. @@ -400,9 +398,9 @@ For some platforms, you will need to refer to [the installation guide](https://w The free data files, split into OpenGFX for graphics, OpenSFX for sounds and OpenMSX for music can be found at: -- https://www.openttd.org/download-opengfx for OpenGFX -- https://www.openttd.org/download-opensfx for OpenSFX -- https://www.openttd.org/download-openmsx for OpenMSX +- https://www.openttd.org/downloads/opengfx-releases/ for OpenGFX +- https://www.openttd.org/downloads/opensfx-releases/ for OpenSFX +- https://www.openttd.org/downloads/openmsx-releases/ for OpenMSX Please follow the readme of these packages about the installation procedure. The Windows installer can optionally download and install these packages. diff --git a/cmake/scripts/FindVersion.cmake b/cmake/scripts/FindVersion.cmake index a49c49fcc5..09fc3ed29a 100644 --- a/cmake/scripts/FindVersion.cmake +++ b/cmake/scripts/FindVersion.cmake @@ -1,5 +1,15 @@ cmake_minimum_required(VERSION 3.5) +if(NOT REV_MAJOR) + set(REV_MAJOR 0) +endif() +if(NOT REV_MINOR) + set(REV_MINOR 0) +endif() +if(NOT REV_BUILD) + set(REV_BUILD 0) +endif() + # # Finds the current version of the current folder. # diff --git a/docs/company_colour_indexes.html b/docs/company_colour_indexes.html new file mode 100644 index 0000000000..fb9dea7add --- /dev/null +++ b/docs/company_colour_indexes.html @@ -0,0 +1,557 @@ + + + + + OpenTTD Company Colour Indexes + + + + +

Company Colour Indexes

+

Hex / dec indexes into the DOS palette

+

+ Visual representation of values derived from https://github.com/frosch123/TTDViewer/blob/master/src/recolor.xml#L186 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
COLOUR_DARK_BLUE
0xc60xc70xc80xc90xca0xcb0xcc0xcd
198199200201202203204205
COLOUR_PALE_GREEN
0x600x610x620x630x640x650x660x67
96979899100101102103
COLOUR_PINK
0x2a0x2b0x2c0x2d0x2e0x2f0x300x31
4243444546474849
COLOUR_YELLOW
0x3e0x3f0x400x410x420x430x440x45
6263646566676869
COLOUR_RED
0xb30xb40xb50xb60xb70xa40xa50xa6
179180181182183164165166
COLOUR_LIGHT_BLUE
0x9a0x9b0x9c0x9d0x9e0x9f0xa00xa1
154155156157158159160161
COLOUR_GREEN
0x520x530x540x550xce0xcf0xd00xd1
82838485206207208209
COLOUR_DARK_GREEN
0x580x590x5a0x5b0x5c0x5d0x5e0x5f
8889909192939495
COLOUR_BLUE
0x920x930x940x950x960x970x980x99
146147148149150151152153
COLOUR_CREAM
0x720x730x740x750x760x770x780x79
114115116117118119120121
COLOUR_MAUVE
0x800x810x820x830x840x850x860x87
128129130131132133134135
COLOUR_PURPLE
0x880x890x8a0x8b0x8c0x8d0x8e0x8f
136137138139140141142143
COLOUR_ORANGE
0x400xc00xc10xc20xc30xc40xc50x27
6419219319419519619739
COLOUR_BROWN
0x200x210x220x230x240x250x260x27
3233343536373839
COLOUR_GREY
0x40x50x60x70x80x90xa0xb
4567891011
COLOUR_WHITE
0x80x90xa0xb0xc0xd0xe0xf
89101112131415
+ + diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 48d1e2af36..94e362bf51 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -201,7 +201,7 @@ struct AIListWindow : public Window { { switch (widget) { case WID_AIL_LIST: { // Select one of the AIs - int sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_AIL_LIST, 0, this->line_height) - 1; + int sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_AIL_LIST) - 1; if (sel < (int)this->info_list->size()) { this->selected = sel; this->SetDirty(); @@ -795,6 +795,7 @@ struct AIConfigWindow : public Window { case WID_AIC_LIST: this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; + resize->height = this->line_height; size->height = 8 * this->line_height; break; @@ -906,7 +907,7 @@ struct AIConfigWindow : public Window { } case WID_AIC_LIST: { // Select a slot - this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height); + this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget); this->InvalidateData(); if (click_count > 1 && this->selected_slot != INVALID_COMPANY) ShowAIListWindow((CompanyID)this->selected_slot); break; diff --git a/src/autoreplace.cpp b/src/autoreplace.cpp index 59980546ee..e1b73448dd 100644 --- a/src/autoreplace.cpp +++ b/src/autoreplace.cpp @@ -11,6 +11,7 @@ #include "command_func.h" #include "group.h" #include "autoreplace_base.h" +#include "core/bitmath_func.hpp" #include "core/pool_func.hpp" #include "safeguards.h" @@ -64,7 +65,7 @@ void RemoveAllEngineReplacement(EngineRenewList *erl) EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old) { const EngineRenew *er = GetEngineReplacement(erl, engine, group); - if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !Group::Get(group)->replace_protection))) { + if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !HasBit(Group::Get(group)->flags, GroupFlags::GF_REPLACE_PROTECTION)))) { /* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */ er = GetEngineReplacement(erl, engine, ALL_GROUP); } diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index ed1838799a..a021fa9cb2 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -760,6 +760,9 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1 bool wagon_removal = c->settings.renew_keep_length; bool same_type_only = HasBit(p2, 0); + const Group *g = Group::GetIfValid(v->group_id); + if (g != nullptr) wagon_removal = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); + /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */ Vehicle *w = v; bool any_replacements = false; diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index e952dcf190..d16e0916e1 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -375,8 +375,15 @@ public: break; case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { - const Company *c = Company::Get(_local_company); - SetDParam(0, c->settings.renew_keep_length ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); + bool remove_wagon; + const Group *g = Group::GetIfValid(this->sel_group); + if (g != nullptr) { + remove_wagon = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); + } else { + const Company *c = Company::Get(_local_company); + remove_wagon = c->settings.renew_keep_length; + } + SetDParam(0, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); break; } @@ -528,9 +535,16 @@ public: } break; - case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length - DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING); + case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { + const Group *g = Group::GetIfValid(this->sel_group); + if (g != nullptr) { + DoCommandP(0, this->sel_group | (GroupFlags::GF_REPLACE_WAGON_REMOVAL << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_FLAG); + } else { + // toggle renew_keep_length + DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING); + } break; + } case WID_RV_START_REPLACE: { // Start replacing if (this->GetWidget(widget)->ButtonHit(pt)) { diff --git a/src/command.cpp b/src/command.cpp index 8f39553b10..642e14dc86 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -228,7 +228,7 @@ CommandProc CmdCreateGroupFromList; CommandProc CmdAddVehicleGroup; CommandProc CmdAddSharedVehicleGroup; CommandProc CmdRemoveAllVehiclesGroup; -CommandProc CmdSetGroupReplaceProtection; +CommandProc CmdSetGroupFlag; CommandProc CmdSetGroupLivery; CommandProc CmdMoveOrder; @@ -462,7 +462,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdAddVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_VEHICLE_GROUP DEF_CMD(CmdAddSharedVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_SHARE_VEHICLE_GROUP DEF_CMD(CmdRemoveAllVehiclesGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REMOVE_ALL_VEHICLES_GROUP - DEF_CMD(CmdSetGroupReplaceProtection, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_REPLACE_PROTECTION + DEF_CMD(CmdSetGroupFlag, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_FLAG DEF_CMD(CmdSetGroupLivery, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_LIVERY DEF_CMD(CmdMoveOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_MOVE_ORDER DEF_CMD(CmdReverseOrderList, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REVERSE_ORDER_LIST diff --git a/src/command_type.h b/src/command_type.h index d8a7932627..840b4645c1 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -410,7 +410,7 @@ enum Commands { CMD_ADD_VEHICLE_GROUP, ///< add a vehicle to a group CMD_ADD_SHARED_VEHICLE_GROUP, ///< add all other shared vehicles to a group which are missing CMD_REMOVE_ALL_VEHICLES_GROUP, ///< remove all vehicles from a group - CMD_SET_GROUP_REPLACE_PROTECTION, ///< set the autoreplace-protection for a group + CMD_SET_GROUP_FLAG, ///< set/clear a flag for a group CMD_SET_GROUP_LIVERY, ///< set the livery for a group CMD_MOVE_ORDER, ///< move an order diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 99227b127c..53f44892ac 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -246,10 +246,10 @@ static const NWidgetPart _nested_company_finances_widgets[] = { EndContainer(), NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0), NWidget(NWID_VERTICAL), // Vertical column with bank balance amount, loan amount, and total. - NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_NULL, STR_NULL), - NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_NULL, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_LOAN_LINE), SetMinimalSize(0, 2), SetFill(1, 0), - NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_NULL, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN), NWidget(NWID_HORIZONTAL), @@ -299,6 +299,24 @@ struct CompanyFinancesWindow : Window { SetDParam(1, (CompanyID)this->window_number); break; + case WID_CF_BALANCE_VALUE: { + const Company *c = Company::Get((CompanyID)this->window_number); + SetDParam(0, c->money); + break; + } + + case WID_CF_LOAN_VALUE: { + const Company *c = Company::Get((CompanyID)this->window_number); + SetDParam(0, c->current_loan); + break; + } + + case WID_CF_TOTAL_VALUE: { + const Company *c = Company::Get((CompanyID)this->window_number); + SetDParam(0, c->money - c->current_loan); + break; + } + case WID_CF_MAXLOAN_VALUE: SetDParam(0, _economy.max_loan); break; @@ -357,27 +375,6 @@ struct CompanyFinancesWindow : Window { break; } - case WID_CF_BALANCE_VALUE: { - const Company *c = Company::Get((CompanyID)this->window_number); - SetDParam(0, c->money); - DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT); - break; - } - - case WID_CF_LOAN_VALUE: { - const Company *c = Company::Get((CompanyID)this->window_number); - SetDParam(0, c->current_loan); - DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT); - break; - } - - case WID_CF_TOTAL_VALUE: { - const Company *c = Company::Get((CompanyID)this->window_number); - SetDParam(0, c->money - c->current_loan); - DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT); - break; - } - case WID_CF_LOAN_LINE: GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK); break; @@ -1018,7 +1015,7 @@ public: break; case WID_SCL_MATRIX: { - uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX, 0, this->line_height); + uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX); if (row >= this->rows) return; if (this->livery_class < LC_GROUP_RAIL) { @@ -1262,70 +1259,82 @@ static const NWidgetPart _nested_select_company_manager_face_widgets[] = { EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 4), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_MOUSTACHE_EARRING_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP), EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_HAIR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetDataTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_EMPTY, STR_FACE_HAIR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetDataTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EYEBROWS, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetDataTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_EMPTY, STR_FACE_EYEBROWS_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetDataTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetDataTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_EMPTY, STR_FACE_EYECOLOUR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetDataTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetDataTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP_2), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetDataTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_NOSE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetDataTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_EMPTY, STR_FACE_NOSE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetDataTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_MOUSTACHE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetDataTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_EMPTY, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetDataTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_CHIN, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetDataTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_EMPTY, STR_FACE_CHIN_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetDataTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_JACKET, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetDataTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_EMPTY, STR_FACE_JACKET_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetDataTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_COLLAR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetDataTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_EMPTY, STR_FACE_COLLAR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetDataTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EARRING, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetDataTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_TIE_EARRING_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetDataTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP), @@ -1356,9 +1365,6 @@ class SelectCompanyManagerFaceWindow : public Window Dimension yesno_dim; ///< Dimension of a yes/no button of a part in the advanced face window. Dimension number_dim; ///< Dimension of a number widget of a part in the advanced face window. - static const StringID PART_TEXTS_IS_FEMALE[]; ///< Strings depending on #is_female, used to describe parts (2 entries for a part). - static const StringID PART_TEXTS[]; ///< Fixed strings to describe parts of the face. - /** * Draw dynamic a label to the left of the button and a value in the button * @@ -1391,6 +1397,10 @@ class SelectCompanyManagerFaceWindow : public Window this->ge = (GenderEthnicity)GB(this->face, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // get the gender and ethnicity this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female this->is_moust_male = !is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache + + this->GetWidget(WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_MOUSTACHE; + this->GetWidget(WID_SCMF_TIE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_TIE; + this->GetWidget(WID_SCMF_LIPS_MOUSTACHE_TEXT)->widget_data = this->is_moust_male ? STR_FACE_MOUSTACHE : STR_FACE_LIPS; } public: @@ -1452,41 +1462,27 @@ public: void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { switch (widget) { - case WID_SCMF_FACE: { - Dimension face_size = GetSpriteSize(SPR_GRADIENT); - size->width = std::max(size->width, face_size.width); - size->height = std::max(size->height, face_size.height); + case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT: + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING)); + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE)); break; - } - case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT: - case WID_SCMF_TIE_EARRING_TEXT: { - int offset = (widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2; - *size = maxdim(GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset]), GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset + 1])); - size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + case WID_SCMF_TIE_EARRING_TEXT: + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING)); + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_TIE)); break; - } case WID_SCMF_LIPS_MOUSTACHE_TEXT: - *size = maxdim(GetStringBoundingBox(STR_FACE_LIPS), GetStringBoundingBox(STR_FACE_MOUSTACHE)); - size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_LIPS)); + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE)); break; - case WID_SCMF_HAS_GLASSES_TEXT: - case WID_SCMF_HAIR_TEXT: - case WID_SCMF_EYEBROWS_TEXT: - case WID_SCMF_EYECOLOUR_TEXT: - case WID_SCMF_GLASSES_TEXT: - case WID_SCMF_NOSE_TEXT: - case WID_SCMF_CHIN_TEXT: - case WID_SCMF_JACKET_TEXT: - case WID_SCMF_COLLAR_TEXT: - *size = GetStringBoundingBox(PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT]); - size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + case WID_SCMF_FACE: { + Dimension face_size = GetSpriteSize(SPR_GRADIENT); + size->width = std::max(size->width, face_size.width); + size->height = std::max(size->height, face_size.height); break; + } case WID_SCMF_HAS_MOUSTACHE_EARRING: case WID_SCMF_HAS_GLASSES: @@ -1572,30 +1568,6 @@ public: void DrawWidget(const Rect &r, int widget) const override { switch (widget) { - case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT: - case WID_SCMF_TIE_EARRING_TEXT: { - StringID str = PART_TEXTS_IS_FEMALE[(widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2 + this->is_female]; - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_GOLD, SA_RIGHT); - break; - } - - case WID_SCMF_LIPS_MOUSTACHE_TEXT: - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, (this->is_moust_male) ? STR_FACE_MOUSTACHE : STR_FACE_LIPS, TC_GOLD, SA_RIGHT); - break; - - case WID_SCMF_HAS_GLASSES_TEXT: - case WID_SCMF_HAIR_TEXT: - case WID_SCMF_EYEBROWS_TEXT: - case WID_SCMF_EYECOLOUR_TEXT: - case WID_SCMF_GLASSES_TEXT: - case WID_SCMF_NOSE_TEXT: - case WID_SCMF_CHIN_TEXT: - case WID_SCMF_JACKET_TEXT: - case WID_SCMF_COLLAR_TEXT: - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT], TC_GOLD, SA_RIGHT); - break; - - case WID_SCMF_HAS_MOUSTACHE_EARRING: if (this->is_female) { // Only for female faces this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true); @@ -1783,25 +1755,6 @@ public: } }; -/** Both text values of parts of the face that depend on the #is_female boolean value. */ -const StringID SelectCompanyManagerFaceWindow::PART_TEXTS_IS_FEMALE[] = { - STR_FACE_MOUSTACHE, STR_FACE_EARRING, // WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT - STR_FACE_TIE, STR_FACE_EARRING, // WID_SCMF_TIE_EARRING_TEXT -}; - -/** Textual names for parts of the face. */ -const StringID SelectCompanyManagerFaceWindow::PART_TEXTS[] = { - STR_FACE_GLASSES, // WID_SCMF_HAS_GLASSES_TEXT - STR_FACE_HAIR, // WID_SCMF_HAIR_TEXT - STR_FACE_EYEBROWS, // WID_SCMF_EYEBROWS_TEXT - STR_FACE_EYECOLOUR, // WID_SCMF_EYECOLOUR_TEXT - STR_FACE_GLASSES, // WID_SCMF_GLASSES_TEXT - STR_FACE_NOSE, // WID_SCMF_NOSE_TEXT - STR_FACE_CHIN, // WID_SCMF_CHIN_TEXT - STR_FACE_JACKET, // WID_SCMF_JACKET_TEXT - STR_FACE_COLLAR, // WID_SCMF_COLLAR_TEXT -}; - /** Company manager face selection window description */ static WindowDesc _select_company_manager_face_desc( WDP_AUTO, "company_face", 0, 0, diff --git a/src/console.cpp b/src/console.cpp index 170c8ac614..8c12b066a8 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -24,8 +24,17 @@ static const uint ICON_TOKEN_COUNT = 20; ///< Maximum number of tokens in on static const uint ICON_MAX_RECURSE = 10; ///< Maximum number of recursion /* console parser */ -IConsoleCmd *_iconsole_cmds; ///< list of registered commands -IConsoleAlias *_iconsole_aliases; ///< list of registered aliases +/* static */ IConsole::CommandList &IConsole::Commands() +{ + static IConsole::CommandList cmds; + return cmds; +} + +/* static */ IConsole::AliasList &IConsole::Aliases() +{ + static IConsole::AliasList aliases; + return aliases; +} FILE *_iconsole_output_file; @@ -195,49 +204,13 @@ bool GetArgumentInteger(uint32 *value, const char *arg) } /** - * Add an item to an alphabetically sorted list. - * @param base first item of the list - * @param item_new the item to add - */ -template -void IConsoleAddSorted(T **base, T *item_new) -{ - if (*base == nullptr) { - *base = item_new; - return; - } - - T *item_before = nullptr; - T *item = *base; - /* The list is alphabetically sorted, insert the new item at the correct location */ - while (item != nullptr) { - if (strcmp(item->name, item_new->name) > 0) break; // insert here - - item_before = item; - item = item->next; - } - - if (item_before == nullptr) { - *base = item_new; - } else { - item_before->next = item_new; - } - - item_new->next = item; -} - -/** - * Remove underscores from a string; the string will be modified! - * @param[in,out] name String to remove the underscores from. - * @return \a name, with its contents modified. + * Creates a copy of a string with underscores removed from it + * @param name String to remove the underscores from. + * @return A copy of \a name, without underscores. */ -char *RemoveUnderscores(char *name) +std::string RemoveUnderscores(std::string name) { - char *q = name; - for (const char *p = name; *p != '\0'; p++) { - if (*p != '_') *q++ = *p; - } - *q = '\0'; + name.erase(std::remove(name.begin(), name.end(), '_'), name.end()); return name; } @@ -246,16 +219,9 @@ char *RemoveUnderscores(char *name) * @param name name of the command that will be used * @param proc function that will be called upon execution of command */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted) +/* static */ void IConsole::CmdRegister(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted) { - IConsoleCmd *item_new = MallocT(1); - item_new->name = RemoveUnderscores(stredup(name)); - item_new->next = nullptr; - item_new->proc = proc; - item_new->hook = hook; - item_new->unlisted = unlisted; - - IConsoleAddSorted(&_iconsole_cmds, item_new); + IConsole::Commands().try_emplace(RemoveUnderscores(name), name, proc, hook, unlisted); } /** @@ -263,13 +229,10 @@ void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook * * @param name command to be found * @return return Cmdstruct of the found command, or nullptr on failure */ -IConsoleCmd *IConsoleCmdGet(const char *name) +/* static */ IConsoleCmd *IConsole::CmdGet(const std::string &name) { - IConsoleCmd *item; - - for (item = _iconsole_cmds; item != nullptr; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } + auto item = IConsole::Commands().find(RemoveUnderscores(name)); + if (item != IConsole::Commands().end()) return &item->second; return nullptr; } @@ -278,22 +241,10 @@ IConsoleCmd *IConsoleCmdGet(const char *name) * @param name name of the alias that will be used * @param cmd name of the command that 'name' will be alias of */ -void IConsoleAliasRegister(const char *name, const char *cmd) +/* static */ void IConsole::AliasRegister(const std::string &name, const std::string &cmd) { - if (IConsoleAliasGet(name) != nullptr) { - IConsoleError("an alias with this name already exists; insertion aborted"); - return; - } - - char *new_alias = RemoveUnderscores(stredup(name)); - char *cmd_aliased = stredup(cmd); - IConsoleAlias *item_new = MallocT(1); - - item_new->next = nullptr; - item_new->cmdline = cmd_aliased; - item_new->name = new_alias; - - IConsoleAddSorted(&_iconsole_aliases, item_new); + auto result = IConsole::Aliases().try_emplace(RemoveUnderscores(name), name, cmd); + if (!result.second) IConsoleError("an alias with this name already exists; insertion aborted"); } /** @@ -301,16 +252,13 @@ void IConsoleAliasRegister(const char *name, const char *cmd) * @param name alias to be found * @return return Aliasstruct of the found alias, or nullptr on failure */ -IConsoleAlias *IConsoleAliasGet(const char *name) +/* static */ IConsoleAlias *IConsole::AliasGet(const std::string &name) { - IConsoleAlias *item; - - for (item = _iconsole_aliases; item != nullptr; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } - + auto item = IConsole::Aliases().find(RemoveUnderscores(name)); + if (item != IConsole::Aliases().end()) return &item->second; return nullptr; } + /** * An alias is just another name for a command, or for more commands * Execute it as well. @@ -330,7 +278,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char return; } - for (const char *cmdptr = alias->cmdline; *cmdptr != '\0'; cmdptr++) { + for (const char *cmdptr = alias->cmdline.c_str(); *cmdptr != '\0'; cmdptr++) { switch (*cmdptr) { case '\'': // ' will double for "" alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); @@ -373,7 +321,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char if (param < 0 || param >= tokencount) { IConsoleError("too many or wrong amount of parameters passed to alias, aborting"); - IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name, alias->cmdline); + IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name.c_str(), alias->cmdline.c_str()); return; } @@ -501,8 +449,7 @@ void IConsoleCmdExecTokens(uint token_count, char *tokens[], const uint recurse_ * First try commands, then aliases. Execute * the found action taking into account its hooking code */ - RemoveUnderscores(tokens[0]); - IConsoleCmd *cmd = IConsoleCmdGet(tokens[0]); + IConsoleCmd *cmd = IConsole::CmdGet(tokens[0]); if (cmd != nullptr) { ConsoleHookResult chr = (cmd->hook == nullptr ? CHR_ALLOW : cmd->hook(true)); switch (chr) { @@ -518,7 +465,7 @@ void IConsoleCmdExecTokens(uint token_count, char *tokens[], const uint recurse_ } token_count--; - IConsoleAlias *alias = IConsoleAliasGet(tokens[0]); + IConsoleAlias *alias = IConsole::AliasGet(tokens[0]); if (alias != nullptr) { IConsoleAliasExec(alias, token_count, &tokens[1], recurse_count + 1); return; diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index c8843d1874..f45137aedd 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -792,7 +792,14 @@ DEF_CONSOLE_CMD(ConClientNickChange) return true; } - if (!NetworkServerChangeClientName(client_id, argv[2])) { + char *client_name = argv[2]; + StrTrimInPlace(client_name); + if (!NetworkIsValidClientName(client_name)) { + IConsoleError("Cannot give a client an empty name"); + return true; + } + + if (!NetworkServerChangeClientName(client_id, client_name)) { IConsoleError("Cannot give a client a duplicate name"); } @@ -1477,12 +1484,11 @@ DEF_CONSOLE_CMD(ConAlias) if (argc < 3) return false; - alias = IConsoleAliasGet(argv[1]); + alias = IConsole::AliasGet(argv[1]); if (alias == nullptr) { - IConsoleAliasRegister(argv[1], argv[2]); + IConsole::AliasRegister(argv[1], argv[2]); } else { - free(alias->cmdline); - alias->cmdline = stredup(argv[2]); + alias->cmdline = argv[2]; } return true; } @@ -1603,13 +1609,13 @@ DEF_CONSOLE_CMD(ConInfoCmd) if (argc < 2) return false; - const IConsoleCmd *cmd = IConsoleCmdGet(argv[1]); + const IConsoleCmd *cmd = IConsole::CmdGet(argv[1]); if (cmd == nullptr) { IConsoleError("the given command was not found"); return true; } - IConsolePrintF(CC_DEFAULT, "command name: %s", cmd->name); + IConsolePrintF(CC_DEFAULT, "command name: %s", cmd->name.c_str()); IConsolePrintF(CC_DEFAULT, "command proc: %p", cmd->proc); if (cmd->hook != nullptr) IConsoleWarning("command is hooked"); @@ -1668,21 +1674,20 @@ DEF_CONSOLE_CMD(ConHelp) const IConsoleCmd *cmd; const IConsoleAlias *alias; - RemoveUnderscores(argv[1]); - cmd = IConsoleCmdGet(argv[1]); + cmd = IConsole::CmdGet(argv[1]); if (cmd != nullptr) { cmd->proc(0, nullptr); return true; } - alias = IConsoleAliasGet(argv[1]); + alias = IConsole::AliasGet(argv[1]); if (alias != nullptr) { - cmd = IConsoleCmdGet(alias->cmdline); + cmd = IConsole::CmdGet(alias->cmdline); if (cmd != nullptr) { cmd->proc(0, nullptr); return true; } - IConsolePrintF(CC_ERROR, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline); + IConsolePrintF(CC_ERROR, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline.c_str()); return true; } @@ -1709,9 +1714,10 @@ DEF_CONSOLE_CMD(ConListCommands) return true; } - for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != nullptr; cmd = cmd->next) { - if (argv[1] == nullptr || strstr(cmd->name, argv[1]) != nullptr) { - if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) IConsolePrintF(CC_DEFAULT, "%s", cmd->name); + for (auto &it : IConsole::Commands()) { + const IConsoleCmd *cmd = &it.second; + if (argv[1] == nullptr || cmd->name.find(argv[1]) != std::string::npos) { + if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) IConsolePrintF(CC_DEFAULT, "%s", cmd->name.c_str()); } } @@ -1725,9 +1731,10 @@ DEF_CONSOLE_CMD(ConListAliases) return true; } - for (const IConsoleAlias *alias = _iconsole_aliases; alias != nullptr; alias = alias->next) { - if (argv[1] == nullptr || strstr(alias->name, argv[1]) != nullptr) { - IConsolePrintF(CC_DEFAULT, "%s => %s", alias->name, alias->cmdline); + for (auto &it : IConsole::Aliases()) { + const IConsoleAlias *alias = &it.second; + if (argv[1] == nullptr || alias->name.find(argv[1]) != std::string::npos) { + IConsolePrintF(CC_DEFAULT, "%s => %s", alias->name.c_str(), alias->cmdline.c_str()); } } @@ -3185,9 +3192,9 @@ DEF_CONSOLE_CMD(ConIfHourMinute) static void IConsoleDebugLibRegister() { - IConsoleCmdRegister("resettile", ConResetTile); - IConsoleAliasRegister("dbg_echo", "echo %A; echo %B"); - IConsoleAliasRegister("dbg_echo2", "echo %!"); + IConsole::CmdRegister("resettile", ConResetTile); + IConsole::AliasRegister("dbg_echo", "echo %A; echo %B"); + IConsole::AliasRegister("dbg_echo2", "echo %!"); } #endif @@ -3284,199 +3291,199 @@ DEF_CONSOLE_CMD(ConDumpInfo) void IConsoleStdLibRegister() { - IConsoleCmdRegister("debug_level", ConDebugLevel); - IConsoleCmdRegister("echo", ConEcho); - IConsoleCmdRegister("echoc", ConEchoC); - IConsoleCmdRegister("exec", ConExec); - IConsoleCmdRegister("exit", ConExit); - IConsoleCmdRegister("part", ConPart); - IConsoleCmdRegister("help", ConHelp); - IConsoleCmdRegister("info_cmd", ConInfoCmd); - IConsoleCmdRegister("list_cmds", ConListCommands); - IConsoleCmdRegister("list_aliases", ConListAliases); - IConsoleCmdRegister("newgame", ConNewGame); - IConsoleCmdRegister("restart", ConRestart); - IConsoleCmdRegister("reload", ConReload); - IConsoleCmdRegister("getseed", ConGetSeed); - IConsoleCmdRegister("getdate", ConGetDate); - IConsoleCmdRegister("getsysdate", ConGetSysDate); - IConsoleCmdRegister("quit", ConExit); - IConsoleCmdRegister("resetengines", ConResetEngines, ConHookNoNetwork); - IConsoleCmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork); - IConsoleCmdRegister("return", ConReturn); - IConsoleCmdRegister("screenshot", ConScreenShot); - IConsoleCmdRegister("minimap", ConMinimap); - IConsoleCmdRegister("script", ConScript); - IConsoleCmdRegister("scrollto", ConScrollToTile); - IConsoleCmdRegister("highlight_tile", ConHighlightTile); - IConsoleAliasRegister("scrollto_highlight", "scrollto %+; highlight_tile %+"); - IConsoleCmdRegister("alias", ConAlias); - IConsoleCmdRegister("load", ConLoad); - IConsoleCmdRegister("rm", ConRemove); - IConsoleCmdRegister("save", ConSave); - IConsoleCmdRegister("saveconfig", ConSaveConfig); - IConsoleCmdRegister("ls", ConListFiles); - IConsoleCmdRegister("cd", ConChangeDirectory); - IConsoleCmdRegister("pwd", ConPrintWorkingDirectory); - IConsoleCmdRegister("clear", ConClearBuffer); - IConsoleCmdRegister("setting", ConSetting); - IConsoleCmdRegister("setting_newgame", ConSettingNewgame); - IConsoleCmdRegister("list_settings",ConListSettings); - IConsoleCmdRegister("gamelog", ConGamelogPrint); - IConsoleCmdRegister("rescan_newgrf", ConRescanNewGRF); - - IConsoleAliasRegister("dir", "ls"); - IConsoleAliasRegister("del", "rm %+"); - IConsoleAliasRegister("newmap", "newgame"); - IConsoleAliasRegister("patch", "setting %+"); - IConsoleAliasRegister("set", "setting %+"); - IConsoleAliasRegister("set_newgame", "setting_newgame %+"); - IConsoleAliasRegister("list_patches", "list_settings %+"); - IConsoleAliasRegister("developer", "setting developer %+"); - - IConsoleCmdRegister("list_ai_libs", ConListAILibs); - IConsoleCmdRegister("list_ai", ConListAI); - IConsoleCmdRegister("reload_ai", ConReloadAI); - IConsoleCmdRegister("rescan_ai", ConRescanAI); - IConsoleCmdRegister("start_ai", ConStartAI); - IConsoleCmdRegister("stop_ai", ConStopAI); - - IConsoleCmdRegister("list_game", ConListGame); - IConsoleCmdRegister("list_game_libs", ConListGameLibs); - IConsoleCmdRegister("rescan_game", ConRescanGame); - - IConsoleCmdRegister("companies", ConCompanies); - IConsoleAliasRegister("players", "companies"); + IConsole::CmdRegister("debug_level", ConDebugLevel); + IConsole::CmdRegister("echo", ConEcho); + IConsole::CmdRegister("echoc", ConEchoC); + IConsole::CmdRegister("exec", ConExec); + IConsole::CmdRegister("exit", ConExit); + IConsole::CmdRegister("part", ConPart); + IConsole::CmdRegister("help", ConHelp); + IConsole::CmdRegister("info_cmd", ConInfoCmd); + IConsole::CmdRegister("list_cmds", ConListCommands); + IConsole::CmdRegister("list_aliases", ConListAliases); + IConsole::CmdRegister("newgame", ConNewGame); + IConsole::CmdRegister("restart", ConRestart); + IConsole::CmdRegister("reload", ConReload); + IConsole::CmdRegister("getseed", ConGetSeed); + IConsole::CmdRegister("getdate", ConGetDate); + IConsole::CmdRegister("getsysdate", ConGetSysDate); + IConsole::CmdRegister("quit", ConExit); + IConsole::CmdRegister("resetengines", ConResetEngines, ConHookNoNetwork); + IConsole::CmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork); + IConsole::CmdRegister("return", ConReturn); + IConsole::CmdRegister("screenshot", ConScreenShot); + IConsole::CmdRegister("minimap", ConMinimap); + IConsole::CmdRegister("script", ConScript); + IConsole::CmdRegister("scrollto", ConScrollToTile); + IConsole::CmdRegister("highlight_tile", ConHighlightTile); + IConsole::AliasRegister("scrollto_highlight", "scrollto %+; highlight_tile %+"); + IConsole::CmdRegister("alias", ConAlias); + IConsole::CmdRegister("load", ConLoad); + IConsole::CmdRegister("rm", ConRemove); + IConsole::CmdRegister("save", ConSave); + IConsole::CmdRegister("saveconfig", ConSaveConfig); + IConsole::CmdRegister("ls", ConListFiles); + IConsole::CmdRegister("cd", ConChangeDirectory); + IConsole::CmdRegister("pwd", ConPrintWorkingDirectory); + IConsole::CmdRegister("clear", ConClearBuffer); + IConsole::CmdRegister("setting", ConSetting); + IConsole::CmdRegister("setting_newgame", ConSettingNewgame); + IConsole::CmdRegister("list_settings", ConListSettings); + IConsole::CmdRegister("gamelog", ConGamelogPrint); + IConsole::CmdRegister("rescan_newgrf", ConRescanNewGRF); + + IConsole::AliasRegister("dir", "ls"); + IConsole::AliasRegister("del", "rm %+"); + IConsole::AliasRegister("newmap", "newgame"); + IConsole::AliasRegister("patch", "setting %+"); + IConsole::AliasRegister("set", "setting %+"); + IConsole::AliasRegister("set_newgame", "setting_newgame %+"); + IConsole::AliasRegister("list_patches", "list_settings %+"); + IConsole::AliasRegister("developer", "setting developer %+"); + + IConsole::CmdRegister("list_ai_libs", ConListAILibs); + IConsole::CmdRegister("list_ai", ConListAI); + IConsole::CmdRegister("reload_ai", ConReloadAI); + IConsole::CmdRegister("rescan_ai", ConRescanAI); + IConsole::CmdRegister("start_ai", ConStartAI); + IConsole::CmdRegister("stop_ai", ConStopAI); + + IConsole::CmdRegister("list_game", ConListGame); + IConsole::CmdRegister("list_game_libs", ConListGameLibs); + IConsole::CmdRegister("rescan_game", ConRescanGame); + + IConsole::CmdRegister("companies", ConCompanies); + IConsole::AliasRegister("players", "companies"); /* networking functions */ /* Content downloading is only available with ZLIB */ #if defined(WITH_ZLIB) - IConsoleCmdRegister("content", ConContent); + IConsole::CmdRegister("content", ConContent); #endif /* defined(WITH_ZLIB) */ /*** Networking commands ***/ - IConsoleCmdRegister("say", ConSay, ConHookNeedNetwork); - IConsoleCmdRegister("say_company", ConSayCompany, ConHookNeedNetwork); - IConsoleAliasRegister("say_player", "say_company %+"); - IConsoleCmdRegister("say_client", ConSayClient, ConHookNeedNetwork); - - IConsoleCmdRegister("connect", ConNetworkConnect, ConHookClientOnly); - IConsoleCmdRegister("clients", ConNetworkClients, ConHookNeedNetwork); - IConsoleCmdRegister("status", ConStatus, ConHookServerOnly); - IConsoleCmdRegister("server_info", ConServerInfo, ConHookServerOnly); - IConsoleAliasRegister("info", "server_info"); - IConsoleCmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly); - IConsoleCmdRegister("rcon", ConRcon, ConHookNeedNetwork); - IConsoleCmdRegister("settings_access", ConSettingsAccess, ConHookNeedNetwork); - - IConsoleCmdRegister("join", ConJoinCompany, ConHookNeedNetwork); - IConsoleAliasRegister("spectate", "join 255"); - IConsoleCmdRegister("move", ConMoveClient, ConHookServerOnly); - IConsoleCmdRegister("reset_company", ConResetCompany, ConHookServerOnly); - IConsoleAliasRegister("clean_company", "reset_company %A"); - IConsoleCmdRegister("client_name", ConClientNickChange, ConHookServerOnly); - IConsoleCmdRegister("kick", ConKick, ConHookServerOnly); - IConsoleCmdRegister("ban", ConBan, ConHookServerOnly); - IConsoleCmdRegister("unban", ConUnBan, ConHookServerOnly); - IConsoleCmdRegister("banlist", ConBanList, ConHookServerOnly); - - IConsoleCmdRegister("pause", ConPauseGame, ConHookServerOnly); - IConsoleCmdRegister("unpause", ConUnpauseGame, ConHookServerOnly); - - IConsoleCmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork); - IConsoleAliasRegister("company_password", "company_pw %+"); - IConsoleCmdRegister("company_pw_hash", ConCompanyPasswordHash, ConHookServerOnly); - IConsoleAliasRegister("company_password_hash", "company_pw %+"); - IConsoleCmdRegister("company_pw_hashes", ConCompanyPasswordHashes, ConHookServerOnly); - IConsoleAliasRegister("company_password_hashes", "company_pw_hashes"); - - IConsoleAliasRegister("net_frame_freq", "setting frame_freq %+"); - IConsoleAliasRegister("net_sync_freq", "setting sync_freq %+"); - IConsoleAliasRegister("server_pw", "setting server_password %+"); - IConsoleAliasRegister("server_password", "setting server_password %+"); - IConsoleAliasRegister("rcon_pw", "setting rcon_password %+"); - IConsoleAliasRegister("rcon_password", "setting rcon_password %+"); - IConsoleAliasRegister("settings_pw", "setting settings_password %+"); - IConsoleAliasRegister("settings_password", "setting settings_password %+"); - IConsoleAliasRegister("name", "setting client_name %+"); - IConsoleAliasRegister("server_name", "setting server_name %+"); - IConsoleAliasRegister("server_port", "setting server_port %+"); - IConsoleAliasRegister("server_advertise", "setting server_advertise %+"); - IConsoleAliasRegister("max_clients", "setting max_clients %+"); - IConsoleAliasRegister("max_companies", "setting max_companies %+"); - IConsoleAliasRegister("max_spectators", "setting max_spectators %+"); - IConsoleAliasRegister("max_join_time", "setting max_join_time %+"); - IConsoleAliasRegister("pause_on_join", "setting pause_on_join %+"); - IConsoleAliasRegister("autoclean_companies", "setting autoclean_companies %+"); - IConsoleAliasRegister("autoclean_protected", "setting autoclean_protected %+"); - IConsoleAliasRegister("autoclean_unprotected", "setting autoclean_unprotected %+"); - IConsoleAliasRegister("restart_game_year", "setting restart_game_year %+"); - IConsoleAliasRegister("min_players", "setting min_active_clients %+"); - IConsoleAliasRegister("reload_cfg", "setting reload_cfg %+"); + IConsole::CmdRegister("say", ConSay, ConHookNeedNetwork); + IConsole::CmdRegister("say_company", ConSayCompany, ConHookNeedNetwork); + IConsole::AliasRegister("say_player", "say_company %+"); + IConsole::CmdRegister("say_client", ConSayClient, ConHookNeedNetwork); + + IConsole::CmdRegister("connect", ConNetworkConnect, ConHookClientOnly); + IConsole::CmdRegister("clients", ConNetworkClients, ConHookNeedNetwork); + IConsole::CmdRegister("status", ConStatus, ConHookServerOnly); + IConsole::CmdRegister("server_info", ConServerInfo, ConHookServerOnly); + IConsole::AliasRegister("info", "server_info"); + IConsole::CmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly); + IConsole::CmdRegister("rcon", ConRcon, ConHookNeedNetwork); + IConsole::CmdRegister("settings_access", ConSettingsAccess, ConHookNeedNetwork); + + IConsole::CmdRegister("join", ConJoinCompany, ConHookNeedNetwork); + IConsole::AliasRegister("spectate", "join 255"); + IConsole::CmdRegister("move", ConMoveClient, ConHookServerOnly); + IConsole::CmdRegister("reset_company", ConResetCompany, ConHookServerOnly); + IConsole::AliasRegister("clean_company", "reset_company %A"); + IConsole::CmdRegister("client_name", ConClientNickChange, ConHookServerOnly); + IConsole::CmdRegister("kick", ConKick, ConHookServerOnly); + IConsole::CmdRegister("ban", ConBan, ConHookServerOnly); + IConsole::CmdRegister("unban", ConUnBan, ConHookServerOnly); + IConsole::CmdRegister("banlist", ConBanList, ConHookServerOnly); + + IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOnly); + IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOnly); + + IConsole::CmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork); + IConsole::AliasRegister("company_password", "company_pw %+"); + IConsole::CmdRegister("company_pw_hash", ConCompanyPasswordHash, ConHookServerOnly); + IConsole::AliasRegister("company_password_hash", "company_pw %+"); + IConsole::CmdRegister("company_pw_hashes", ConCompanyPasswordHashes, ConHookServerOnly); + IConsole::AliasRegister("company_password_hashes", "company_pw_hashes"); + + IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+"); + IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+"); + IConsole::AliasRegister("server_pw", "setting server_password %+"); + IConsole::AliasRegister("server_password", "setting server_password %+"); + IConsole::AliasRegister("rcon_pw", "setting rcon_password %+"); + IConsole::AliasRegister("rcon_password", "setting rcon_password %+"); + IConsole::AliasRegister("settings_pw", "setting settings_password %+"); + IConsole::AliasRegister("settings_password", "setting settings_password %+"); + IConsole::AliasRegister("name", "setting client_name %+"); + IConsole::AliasRegister("server_name", "setting server_name %+"); + IConsole::AliasRegister("server_port", "setting server_port %+"); + IConsole::AliasRegister("server_advertise", "setting server_advertise %+"); + IConsole::AliasRegister("max_clients", "setting max_clients %+"); + IConsole::AliasRegister("max_companies", "setting max_companies %+"); + IConsole::AliasRegister("max_spectators", "setting max_spectators %+"); + IConsole::AliasRegister("max_join_time", "setting max_join_time %+"); + IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+"); + IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+"); + IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+"); + IConsole::AliasRegister("autoclean_unprotected", "setting autoclean_unprotected %+"); + IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+"); + IConsole::AliasRegister("min_players", "setting min_active_clients %+"); + IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+"); /* conditionals */ - IConsoleCmdRegister("if_year", ConIfYear); - IConsoleCmdRegister("if_month", ConIfMonth); - IConsoleCmdRegister("if_day", ConIfDay); - IConsoleCmdRegister("if_hour", ConIfHour); - IConsoleCmdRegister("if_minute", ConIfMinute); - IConsoleCmdRegister("if_hour_minute", ConIfHourMinute); + IConsole::CmdRegister("if_year", ConIfYear); + IConsole::CmdRegister("if_month", ConIfMonth); + IConsole::CmdRegister("if_day", ConIfDay); + IConsole::CmdRegister("if_hour", ConIfHour); + IConsole::CmdRegister("if_minute", ConIfMinute); + IConsole::CmdRegister("if_hour_minute", ConIfHourMinute); /* debugging stuff */ #ifdef _DEBUG IConsoleDebugLibRegister(); #endif - IConsoleCmdRegister("fps", ConFramerate); - IConsoleCmdRegister("fps_wnd", ConFramerateWindow); - - IConsoleCmdRegister("find_non_realistic_braking_signal", ConFindNonRealisticBrakingSignal); - - IConsoleCmdRegister("getfulldate", ConGetFullDate, nullptr, true); - IConsoleCmdRegister("dump_command_log", ConDumpCommandLog, nullptr, true); - IConsoleCmdRegister("dump_desync_msgs", ConDumpDesyncMsgLog, nullptr, true); - IConsoleCmdRegister("dump_inflation", ConDumpInflation, nullptr, true); - IConsoleCmdRegister("dump_cpdp_stats", ConDumpCpdpStats, nullptr, true); - IConsoleCmdRegister("dump_veh_stats", ConVehicleStats, nullptr, true); - IConsoleCmdRegister("dump_map_stats", ConMapStats, nullptr, true); - IConsoleCmdRegister("dump_st_flow_stats", ConStFlowStats, nullptr, true); - IConsoleCmdRegister("dump_game_events", ConDumpGameEvents, nullptr, true); - IConsoleCmdRegister("dump_load_debug_log", ConDumpLoadDebugLog, nullptr, true); - IConsoleCmdRegister("dump_load_debug_config", ConDumpLoadDebugConfig, nullptr, true); - IConsoleCmdRegister("dump_linkgraph_jobs", ConDumpLinkgraphJobs, nullptr, true); - IConsoleCmdRegister("dump_road_types", ConDumpRoadTypes, nullptr, true); - IConsoleCmdRegister("dump_rail_types", ConDumpRailTypes, nullptr, true); - IConsoleCmdRegister("dump_bridge_types", ConDumpBridgeTypes, nullptr, true); - IConsoleCmdRegister("dump_cargo_types", ConDumpCargoTypes, nullptr, true); - IConsoleCmdRegister("dump_tile", ConDumpTile, nullptr, true); - IConsoleCmdRegister("check_caches", ConCheckCaches, nullptr, true); - IConsoleCmdRegister("show_town_window", ConShowTownWindow, nullptr, true); - IConsoleCmdRegister("show_station_window", ConShowStationWindow, nullptr, true); - IConsoleCmdRegister("show_industry_window", ConShowIndustryWindow, nullptr, true); - IConsoleCmdRegister("viewport_debug", ConViewportDebug, nullptr, true); - IConsoleCmdRegister("viewport_mark_dirty", ConViewportMarkDirty, nullptr, true); - IConsoleCmdRegister("viewport_mark_dirty_st_overlay", ConViewportMarkStationOverlayDirty, nullptr, true); - IConsoleCmdRegister("gfx_debug", ConGfxDebug, nullptr, true); - IConsoleCmdRegister("csleep", ConCSleep, nullptr, true); - IConsoleCmdRegister("recalculate_road_cached_one_way_states", ConRecalculateRoadCachedOneWayStates, ConHookNoNetwork, true); - IConsoleCmdRegister("misc_debug", ConMiscDebug, nullptr, true); + IConsole::CmdRegister("fps", ConFramerate); + IConsole::CmdRegister("fps_wnd", ConFramerateWindow); + + IConsole::CmdRegister("find_non_realistic_braking_signal", ConFindNonRealisticBrakingSignal); + + IConsole::CmdRegister("getfulldate", ConGetFullDate, nullptr, true); + IConsole::CmdRegister("dump_command_log", ConDumpCommandLog, nullptr, true); + IConsole::CmdRegister("dump_desync_msgs", ConDumpDesyncMsgLog, nullptr, true); + IConsole::CmdRegister("dump_inflation", ConDumpInflation, nullptr, true); + IConsole::CmdRegister("dump_cpdp_stats", ConDumpCpdpStats, nullptr, true); + IConsole::CmdRegister("dump_veh_stats", ConVehicleStats, nullptr, true); + IConsole::CmdRegister("dump_map_stats", ConMapStats, nullptr, true); + IConsole::CmdRegister("dump_st_flow_stats", ConStFlowStats, nullptr, true); + IConsole::CmdRegister("dump_game_events", ConDumpGameEvents, nullptr, true); + IConsole::CmdRegister("dump_load_debug_log", ConDumpLoadDebugLog, nullptr, true); + IConsole::CmdRegister("dump_load_debug_config", ConDumpLoadDebugConfig, nullptr, true); + IConsole::CmdRegister("dump_linkgraph_jobs", ConDumpLinkgraphJobs, nullptr, true); + IConsole::CmdRegister("dump_road_types", ConDumpRoadTypes, nullptr, true); + IConsole::CmdRegister("dump_rail_types", ConDumpRailTypes, nullptr, true); + IConsole::CmdRegister("dump_bridge_types", ConDumpBridgeTypes, nullptr, true); + IConsole::CmdRegister("dump_cargo_types", ConDumpCargoTypes, nullptr, true); + IConsole::CmdRegister("dump_tile", ConDumpTile, nullptr, true); + IConsole::CmdRegister("check_caches", ConCheckCaches, nullptr, true); + IConsole::CmdRegister("show_town_window", ConShowTownWindow, nullptr, true); + IConsole::CmdRegister("show_station_window", ConShowStationWindow, nullptr, true); + IConsole::CmdRegister("show_industry_window", ConShowIndustryWindow, nullptr, true); + IConsole::CmdRegister("viewport_debug", ConViewportDebug, nullptr, true); + IConsole::CmdRegister("viewport_mark_dirty", ConViewportMarkDirty, nullptr, true); + IConsole::CmdRegister("viewport_mark_dirty_st_overlay", ConViewportMarkStationOverlayDirty, nullptr, true); + IConsole::CmdRegister("gfx_debug", ConGfxDebug, nullptr, true); + IConsole::CmdRegister("csleep", ConCSleep, nullptr, true); + IConsole::CmdRegister("recalculate_road_cached_one_way_states", ConRecalculateRoadCachedOneWayStates, ConHookNoNetwork, true); + IConsole::CmdRegister("misc_debug", ConMiscDebug, nullptr, true); /* NewGRF development stuff */ - IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool); - IConsoleCmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool); - IConsoleCmdRegister("dump_info", ConDumpInfo); - IConsoleCmdRegister("do_disaster", ConDoDisaster, ConHookNewGRFDeveloperTool, true); - IConsoleCmdRegister("bankrupt_company", ConBankruptCompany, ConHookNewGRFDeveloperTool, true); - IConsoleCmdRegister("delete_company", ConDeleteCompany, ConHookNewGRFDeveloperTool, true); - IConsoleCmdRegister("road_type_flag_ctl", ConRoadTypeFlagCtl, ConHookNewGRFDeveloperTool, true); - IConsoleCmdRegister("rail_type_map_colour_ctl", ConRailTypeMapColourCtl, ConHookNewGRFDeveloperTool, true); - IConsoleCmdRegister("switch_baseset", ConSwitchBaseset, ConHookNewGRFDeveloperTool, true); + IConsole::CmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool); + IConsole::CmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool); + IConsole::CmdRegister("dump_info", ConDumpInfo); + IConsole::CmdRegister("do_disaster", ConDoDisaster, ConHookNewGRFDeveloperTool, true); + IConsole::CmdRegister("bankrupt_company", ConBankruptCompany, ConHookNewGRFDeveloperTool, true); + IConsole::CmdRegister("delete_company", ConDeleteCompany, ConHookNewGRFDeveloperTool, true); + IConsole::CmdRegister("road_type_flag_ctl", ConRoadTypeFlagCtl, ConHookNewGRFDeveloperTool, true); + IConsole::CmdRegister("rail_type_map_colour_ctl", ConRailTypeMapColourCtl, ConHookNewGRFDeveloperTool, true); + IConsole::CmdRegister("switch_baseset", ConSwitchBaseset, ConHookNewGRFDeveloperTool, true); /* Bug workarounds */ - IConsoleCmdRegister("jgrpp_bug_workaround_unblock_heliports", ConResetBlockedHeliports, ConHookNoNetwork, true); - IConsoleCmdRegister("merge_linkgraph_jobs_asap", ConMergeLinkgraphJobsAsap, ConHookNoNetwork, true); + IConsole::CmdRegister("jgrpp_bug_workaround_unblock_heliports", ConResetBlockedHeliports, ConHookNoNetwork, true); + IConsole::CmdRegister("merge_linkgraph_jobs_asap", ConMergeLinkgraphJobsAsap, ConHookNoNetwork, true); #ifdef _DEBUG - IConsoleCmdRegister("delete_vehicle_id", ConDeleteVehicleID, ConHookNoNetwork, true); + IConsole::CmdRegister("delete_vehicle_id", ConDeleteVehicleID, ConHookNoNetwork, true); #endif } diff --git a/src/console_gui.cpp b/src/console_gui.cpp index 1e71e7e49b..55bff34872 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -524,11 +524,9 @@ static void IConsoleTabCompletion() return; } } - size_t length = cmdptr - input; - char *prefix = (char*)alloca(length + 1); - strecpy(prefix, input, prefix + length); - RemoveUnderscores(prefix); - size_t prefix_length = strlen(prefix); + extern std::string RemoveUnderscores(std::string name); + std::string prefix = RemoveUnderscores(std::string(input, cmdptr - input)); + size_t prefix_length = prefix.size(); if (prefix_length == 0) return; @@ -536,26 +534,28 @@ static void IConsoleTabCompletion() char *b = buffer; uint matches = 0; std::string common_prefix; - for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != nullptr; cmd = cmd->next) { - if (strncmp(cmd->name, prefix, prefix_length) == 0) { + for (auto &it : IConsole::Commands()) { + const char *cmd_name = it.first.c_str(); + const IConsoleCmd *cmd = &it.second; + if (strncmp(cmd_name, prefix.c_str(), prefix_length) == 0) { if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) { if (matches == 0) { - common_prefix = cmd->name; + common_prefix = it.first; } else { const char *cp = common_prefix.c_str(); - const char *cmdp = cmd->name; + const char *cmdp = cmd_name; while (true) { const char *end = cmdp; WChar a = Utf8Consume(cp); WChar b = Utf8Consume(cmdp); if (a == 0 || b == 0 || a != b) { - common_prefix.resize(end - cmd->name); + common_prefix.resize(end - cmd_name); break; } } } matches++; - b += seprintf(b, lastof(buffer), "%s ", cmd->name); + b += seprintf(b, lastof(buffer), "%s ", cmd_name); } } } diff --git a/src/console_internal.h b/src/console_internal.h index c4c41bb293..9ac4e12759 100644 --- a/src/console_internal.h +++ b/src/console_internal.h @@ -11,6 +11,7 @@ #define CONSOLE_INTERNAL_H #include "gfx_type.h" +#include static const uint ICON_CMDLN_SIZE = 1024; ///< maximum length of a typed in command static const uint ICON_MAX_STREAMSIZE = 2048; ///< maximum length of a totally expanded command @@ -33,9 +34,9 @@ enum ConsoleHookResult { typedef bool IConsoleCmdProc(byte argc, char *argv[]); typedef ConsoleHookResult IConsoleHook(bool echo); struct IConsoleCmd { - char *name; ///< name of command - IConsoleCmd *next; ///< next command in list + IConsoleCmd(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted) : name(name), proc(proc), hook(hook), unlisted(unlisted) {} + std::string name; ///< name of command IConsoleCmdProc *proc; ///< process executed when command is typed IConsoleHook *hook; ///< any special trigger action that needs executing bool unlisted; @@ -54,25 +55,31 @@ struct IConsoleCmd { * - ";" allows for combining commands (see example 'ng') */ struct IConsoleAlias { - char *name; ///< name of the alias - IConsoleAlias *next; ///< next alias in list + IConsoleAlias(const std::string &name, const std::string &cmdline) : name(name), cmdline(cmdline) {} - char *cmdline; ///< command(s) that is/are being aliased + std::string name; ///< name of the alias + std::string cmdline; ///< command(s) that is/are being aliased }; -/* console parser */ -extern IConsoleCmd *_iconsole_cmds; ///< List of registered commands. -extern IConsoleAlias *_iconsole_aliases; ///< List of registered aliases. +struct IConsole +{ + typedef std::map CommandList; + typedef std::map AliasList; + + /* console parser */ + static CommandList &Commands(); + static AliasList &Aliases(); + + /* Commands */ + static void CmdRegister(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook = nullptr, bool unlisted = false); + static IConsoleCmd *CmdGet(const std::string &name); + static void AliasRegister(const std::string &name, const std::string &cmd); + static IConsoleAlias *AliasGet(const std::string &name); +}; /* console functions */ void IConsoleClearBuffer(); -/* Commands */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook = nullptr, bool unlisted = false); -void IConsoleAliasRegister(const char *name, const char *cmd); -IConsoleCmd *IConsoleCmdGet(const char *name); -IConsoleAlias *IConsoleAliasGet(const char *name); - /* console std lib (register ingame commands/aliases) */ void IConsoleStdLibRegister(); @@ -82,6 +89,5 @@ bool GetArgumentInteger(uint32 *value, const char *arg); void IConsoleGUIInit(); void IConsoleGUIFree(); void IConsoleGUIPrint(TextColour colour_code, char *string); -char *RemoveUnderscores(char *name); #endif /* CONSOLE_INTERNAL_H */ diff --git a/src/core/geometry_type.hpp b/src/core/geometry_type.hpp index e923eb3229..564dba60a8 100644 --- a/src/core/geometry_type.hpp +++ b/src/core/geometry_type.hpp @@ -80,10 +80,4 @@ struct PointDimension { int height; }; -/** A pair of two integers */ -struct Pair { - int a; - int b; -}; - #endif /* GEOMETRY_TYPE_HPP */ diff --git a/src/framerate_gui.cpp b/src/framerate_gui.cpp index 9bd5a2e8e2..28f8886e13 100644 --- a/src/framerate_gui.cpp +++ b/src/framerate_gui.cpp @@ -682,7 +682,7 @@ struct FramerateWindow : Window { case WID_FRW_TIMES_AVERAGE: { /* Open time graph windows when clicking detail measurement lines */ const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR); - int line = sb->GetScrolledRowFromWidget(pt.y - FONT_HEIGHT_NORMAL - VSPACING, this, widget, VSPACING, FONT_HEIGHT_NORMAL); + int line = sb->GetScrolledRowFromWidget(pt.y, this, widget, VSPACING + FONT_HEIGHT_NORMAL); if (line != INT_MAX) { line++; /* Find the visible line that was clicked */ diff --git a/src/gfx_func.h b/src/gfx_func.h index 070e5effe0..35edc93c77 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -92,24 +92,6 @@ Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr); void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); -/** How to align the to-be drawn text. */ -enum StringAlignment { - SA_LEFT = 0 << 0, ///< Left align the text. - SA_HOR_CENTER = 1 << 0, ///< Horizontally center the text. - SA_RIGHT = 2 << 0, ///< Right align the text (must be a single bit). - SA_HOR_MASK = 3 << 0, ///< Mask for horizontal alignment. - - SA_TOP = 0 << 2, ///< Top align the text. - SA_VERT_CENTER = 1 << 2, ///< Vertically center the text. - SA_BOTTOM = 2 << 2, ///< Bottom align the text. - SA_VERT_MASK = 3 << 2, ///< Mask for vertical alignment. - - SA_CENTER = SA_HOR_CENTER | SA_VERT_CENTER, ///< Center both horizontally and vertically. - - SA_FORCE = 1 << 4, ///< Force the alignment, i.e. don't swap for RTL languages. -}; -DECLARE_ENUM_AS_BIT_SET(StringAlignment) - int DrawString(int left, int right, int top, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawString(int left, int right, int top, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); diff --git a/src/gfx_type.h b/src/gfx_type.h index 54b08119cd..377278f466 100644 --- a/src/gfx_type.h +++ b/src/gfx_type.h @@ -324,4 +324,22 @@ enum Support8bpp { S8BPP_HARDWARE, ///< Full 8bpp support by OS and hardware. }; + /** How to align the to-be drawn text. */ +enum StringAlignment { + SA_LEFT = 0 << 0, ///< Left align the text. + SA_HOR_CENTER = 1 << 0, ///< Horizontally center the text. + SA_RIGHT = 2 << 0, ///< Right align the text (must be a single bit). + SA_HOR_MASK = 3 << 0, ///< Mask for horizontal alignment. + + SA_TOP = 0 << 2, ///< Top align the text. + SA_VERT_CENTER = 1 << 2, ///< Vertically center the text. + SA_BOTTOM = 2 << 2, ///< Bottom align the text. + SA_VERT_MASK = 3 << 2, ///< Mask for vertical alignment. + + SA_CENTER = SA_HOR_CENTER | SA_VERT_CENTER, ///< Center both horizontally and vertically. + + SA_FORCE = 1 << 4, ///< Force the alignment, i.e. don't swap for RTL languages. +}; +DECLARE_ENUM_AS_BIT_SET(StringAlignment) + #endif /* GFX_TYPE_H */ diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 86ea64df6d..72ff12520f 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -992,7 +992,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { } case WID_CPR_MATRIX: { - uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CPR_MATRIX, 0, this->line_height); + uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CPR_MATRIX); if (row >= this->vscroll->GetCount()) return; const CargoSpec *cs; diff --git a/src/group.h b/src/group.h index c944764dd2..f296ee844f 100644 --- a/src/group.h +++ b/src/group.h @@ -62,13 +62,19 @@ struct GroupStatistics { static void UpdateAutoreplace(CompanyID company); }; +enum GroupFlags : uint8 { + GF_REPLACE_PROTECTION, ///< If set to true, the global autoreplace has no effect on the group + GF_REPLACE_WAGON_REMOVAL, ///< If set, autoreplace will perform wagon removal on vehicles in this group. + GF_END, +}; + /** Group data. */ struct Group : GroupPool::PoolItem<&_group_pool> { std::string name; ///< Group Name Owner owner; ///< Group Owner VehicleType vehicle_type; ///< Vehicle type of the group - bool replace_protection; ///< If set to true, the global autoreplace have no effect on the group + uint8 flags; ///< Group flags Livery livery; ///< Custom colour scheme for vehicles in this group GroupStatistics statistics; ///< NOSAVE: Statistics and caches on the vehicles in the group. diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 4abb3f2075..36c8255b8f 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -324,7 +324,6 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (flags & DC_EXEC) { Group *g = new Group(_current_company); - g->replace_protection = false; g->vehicle_type = vt; g->parent = INVALID_GROUP; @@ -332,10 +331,12 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 const Company *c = Company::Get(_current_company); g->livery.colour1 = c->livery[LS_DEFAULT].colour1; g->livery.colour2 = c->livery[LS_DEFAULT].colour2; + if (c->settings.renew_keep_length) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); } else { g->parent = pg->index; g->livery.colour1 = pg->livery.colour1; g->livery.colour2 = pg->livery.colour2; + g->flags = pg->flags; } _new_group_id = g->index; @@ -728,42 +729,50 @@ CommandCost CmdSetGroupLivery(TileIndex tile, DoCommandFlag flags, uint32 p1, ui } /** - * Set replace protection for a group and its sub-groups. + * Set group flag for a group and its sub-groups. * @param g initial group. - * @param protect 1 to set or 0 to clear protection. + * @param set 1 to set or 0 to clear protection. */ -static void SetGroupReplaceProtection(Group *g, bool protect) +static void SetGroupFlag(Group *g, GroupFlags flag, bool set, bool children) { - g->replace_protection = protect; + if (set) { + SetBit(g->flags, flag); + } else { + ClrBit(g->flags, flag); + } + + if (!children) return; for (Group *pg : Group::Iterate()) { - if (pg->parent == g->index) SetGroupReplaceProtection(pg, protect); + if (pg->parent == g->index) SetGroupFlag(pg, flag, set, true); } } /** - * (Un)set global replace protection from a group + * (Un)set group flag from a group * @param tile unused * @param flags type of operation * @param p1 index of group array - * - p1 bit 0-15 : GroupID + * - p1 bit 0-15 : GroupID + * - p1 bit 16-18 : Flag to set, by value not bit. * @param p2 * - p2 bit 0 : 1 to set or 0 to clear protection. * - p2 bit 1 : 1 to apply to sub-groups. * @param text unused * @return the cost of this operation or an error */ -CommandCost CmdSetGroupReplaceProtection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +CommandCost CmdSetGroupFlag(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { - Group *g = Group::GetIfValid(p1); + Group *g = Group::GetIfValid(GB(p1, 0, 16)); if (g == nullptr || g->owner != _current_company) return CMD_ERROR; + /* GroupFlags are stored in as an 8 bit bitfield but passed here by value, + * so 3 bits is sufficient to cover each possible value. */ + GroupFlags flag = (GroupFlags)GB(p1, 16, 3); + if (flag >= GroupFlags::GF_END) return CMD_ERROR; + if (flags & DC_EXEC) { - if (HasBit(p2, 1)) { - SetGroupReplaceProtection(g, HasBit(p2, 0)); - } else { - g->replace_protection = HasBit(p2, 0); - } + SetGroupFlag(g, flag, HasBit(p2, 0), HasBit(p2, 1)); SetWindowDirty(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack()); InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type); diff --git a/src/group_gui.cpp b/src/group_gui.cpp index e848c4d9af..2a62bdc120 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -618,7 +618,7 @@ public: /* If not a default group and the group has replace protection, show an enabled replace sprite. */ uint16 protect_sprite = SPR_GROUP_REPLACE_OFF_TRAIN; - if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && Group::Get(this->vli.index)->replace_protection) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN; + if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && HasBit(Group::Get(this->vli.index)->flags, GroupFlags::GF_REPLACE_PROTECTION)) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN; this->GetWidget(WID_GL_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype; /* Set text of "group by" dropdown widget. */ @@ -675,7 +675,7 @@ public: assert(g->owner == this->owner); - DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i] * LEVEL_WIDTH, g->replace_protection, g->folded || (i + 1 < (int)this->groups.size() && indents[i + 1] > this->indents[i])); + DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i] * LEVEL_WIDTH, HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION), g->folded || (i + 1 < (int)this->groups.size() && indents[i + 1] > this->indents[i])); y1 += this->tiny_step_height; } @@ -755,7 +755,7 @@ public: break; case WID_GL_LIST_GROUP: { // Matrix Group - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); if (id_g >= this->groups.size()) return; if (groups[id_g]->folded || (id_g + 1 < this->groups.size() && this->indents[id_g + 1] > this->indents[id_g])) { @@ -891,7 +891,7 @@ public: case WID_GL_REPLACE_PROTECTION: { const Group *g = Group::GetIfValid(this->vli.index); if (g != nullptr) { - DoCommandP(0, this->vli.index, (g->replace_protection ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_REPLACE_PROTECTION); + DoCommandP(0, this->vli.index | (GroupFlags::GF_REPLACE_PROTECTION << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_FLAG); } break; } @@ -921,7 +921,7 @@ public: break; case WID_GL_LIST_GROUP: { // Matrix group - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); GroupID new_g = id_g >= this->groups.size() ? INVALID_GROUP : this->groups[id_g]->index; if (this->group_sel != new_g && g->parent != new_g) { @@ -954,7 +954,7 @@ public: this->group_over = INVALID_GROUP; this->SetDirty(); - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index; DoCommandP(0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE), new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr); @@ -1124,7 +1124,7 @@ public: break; case WID_GL_LIST_GROUP: { // ... the list of custom groups. - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); new_group_over = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index; break; } diff --git a/src/lang/CMakeLists.txt b/src/lang/CMakeLists.txt index 3041ef27d2..9a9c278ef7 100644 --- a/src/lang/CMakeLists.txt +++ b/src/lang/CMakeLists.txt @@ -11,6 +11,7 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/brazilian_portuguese.txt ${CMAKE_CURRENT_SOURCE_DIR}/bulgarian.txt ${CMAKE_CURRENT_SOURCE_DIR}/catalan.txt + ${CMAKE_CURRENT_SOURCE_DIR}/chuvash.txt ${CMAKE_CURRENT_SOURCE_DIR}/croatian.txt ${CMAKE_CURRENT_SOURCE_DIR}/czech.txt ${CMAKE_CURRENT_SOURCE_DIR}/danish.txt @@ -22,13 +23,16 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/faroese.txt ${CMAKE_CURRENT_SOURCE_DIR}/finnish.txt ${CMAKE_CURRENT_SOURCE_DIR}/french.txt + ${CMAKE_CURRENT_SOURCE_DIR}/frisian.txt ${CMAKE_CURRENT_SOURCE_DIR}/gaelic.txt ${CMAKE_CURRENT_SOURCE_DIR}/galician.txt ${CMAKE_CURRENT_SOURCE_DIR}/german.txt ${CMAKE_CURRENT_SOURCE_DIR}/greek.txt ${CMAKE_CURRENT_SOURCE_DIR}/hebrew.txt + ${CMAKE_CURRENT_SOURCE_DIR}/hindi.txt ${CMAKE_CURRENT_SOURCE_DIR}/hungarian.txt ${CMAKE_CURRENT_SOURCE_DIR}/icelandic.txt + ${CMAKE_CURRENT_SOURCE_DIR}/ido.txt ${CMAKE_CURRENT_SOURCE_DIR}/indonesian.txt ${CMAKE_CURRENT_SOURCE_DIR}/irish.txt ${CMAKE_CURRENT_SOURCE_DIR}/italian.txt @@ -38,9 +42,13 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/latvian.txt ${CMAKE_CURRENT_SOURCE_DIR}/lithuanian.txt ${CMAKE_CURRENT_SOURCE_DIR}/luxembourgish.txt + ${CMAKE_CURRENT_SOURCE_DIR}/macedonian.txt ${CMAKE_CURRENT_SOURCE_DIR}/malay.txt + ${CMAKE_CURRENT_SOURCE_DIR}/maltese.txt + ${CMAKE_CURRENT_SOURCE_DIR}/marathi.txt ${CMAKE_CURRENT_SOURCE_DIR}/norwegian_bokmal.txt ${CMAKE_CURRENT_SOURCE_DIR}/norwegian_nynorsk.txt + ${CMAKE_CURRENT_SOURCE_DIR}/persian.txt ${CMAKE_CURRENT_SOURCE_DIR}/polish.txt ${CMAKE_CURRENT_SOURCE_DIR}/portuguese.txt ${CMAKE_CURRENT_SOURCE_DIR}/romanian.txt @@ -56,6 +64,7 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/thai.txt ${CMAKE_CURRENT_SOURCE_DIR}/traditional_chinese.txt ${CMAKE_CURRENT_SOURCE_DIR}/turkish.txt + ${CMAKE_CURRENT_SOURCE_DIR}/urdu.txt ${CMAKE_CURRENT_SOURCE_DIR}/ukrainian.txt ${CMAKE_CURRENT_SOURCE_DIR}/vietnamese.txt ${CMAKE_CURRENT_SOURCE_DIR}/welsh.txt diff --git a/src/lang/unfinished/chuvash.txt b/src/lang/chuvash.txt similarity index 100% rename from src/lang/unfinished/chuvash.txt rename to src/lang/chuvash.txt diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index da5a607e44..d889c36e40 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Selecteer dit vakje om OpenTTD hardwareversnelling te laten gebruiken. De gewijzigde instelling wordt pas van kracht nadat het spel opnieuw is gestart. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}De instelling wordt pas van kracht als het spel opnieuw is gestart +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Selecteer dit vakje om het scherm verticaal te synchroniseren. De wijziging gaat pas in nadat je het spel opnieuw hebt opgestart. Werkt alleen als hardwareversnelling is ingeschakeld STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Menupuntgrootte STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kiest de grootte van bedieningselementen @@ -1140,6 +1142,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Instelli STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtertekst: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Alles uitvouwen STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Alles inklappen +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Alle waarden terugstellen STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(geen uitleg beschikbaar) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Standaardwaarde: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Instellingstype: {ORANGE}{STRING} @@ -1148,6 +1151,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Spelinstellinge STR_CONFIG_SETTING_TYPE_GAME_INGAME :Spelinstellingen (opgeslagen in bestand; alleen van invloed op huidig spel) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op nieuwe spellen) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op huidig bedrijf) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Voorzichtig! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Met deze actie herstel je alle spelinstellingen naar hun standaardwaarden.{}Weet je zeker dat je wilt doorgaan? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categorie: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type: @@ -2493,7 +2498,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Haven bo STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Boei plaatsen, deze kan gebruikt worden voor extra tussenstops. Shift schakelt tussen bouwen/inschatting van de kosten STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Aquaduct bouwen. Shift schakelt tussen bouwen/inschatting van de kosten. STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Hiermee plaats je watermassa's.{}Maakt een kanaal, tenzij je Ctrl vasthoudt op zeeniveau, dan overstroomt de omgeving. -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Hiermee maak je rivieren +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Hiermee maak je rivieren. Ctrl selecteert het gebied diagonaal # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Richting van dok diff --git a/src/lang/english.txt b/src/lang/english.txt index cb88ab72bc..af0c9618b7 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Every 12 months STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{RAW_STRING} ({NUM}% completed) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode @@ -2553,6 +2554,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Could no STR_NETWORK_ERROR_CLIENT_START :{WHITE}Could not connect STR_NETWORK_ERROR_TIMEOUT :{WHITE}Connection #{NUM} timed out STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}A protocol error was detected and the connection was closed +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Your player name has not been set. The name can be set at the top of the Multiplayer window STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}The revision of this client does not match the server's revision STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong password STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full @@ -2565,6 +2567,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}You took STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your computer is too slow to keep up with the server STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error @@ -2587,6 +2590,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :received no pas STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :general timeout STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :downloading map took too long STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :processing map took too long +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :invalid client name ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible connection loss diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index e3f0304c42..9fd458d97c 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Every 12 months STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completed) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 1fba60c884..01d3121d35 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Kerran vuodessa STR_GAME_OPTIONS_LANGUAGE :{BLACK}Kieli STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Valitse käyttöliittymän kieli +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}{NBSP}% valmiina) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Koko näyttö STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Valitse tämä pelataksesi kokoruututilassa diff --git a/src/lang/unfinished/frisian.txt b/src/lang/frisian.txt similarity index 100% rename from src/lang/unfinished/frisian.txt rename to src/lang/frisian.txt diff --git a/src/lang/hindi.txt b/src/lang/hindi.txt new file mode 100644 index 0000000000..d8e185057f --- /dev/null +++ b/src/lang/hindi.txt @@ -0,0 +1,1057 @@ +##name Hindi +##ownname हिन्दी +##isocode hi_IN +##plural 0 +##textdir ltr +##digitsep , +##digitsepcur , +##decimalsep . +##winlangid 0x0439 +##grflangid 0x17 + + +# This file is part of OpenTTD. +# OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. +# OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + + +##id 0x0000 +STR_NULL : +STR_EMPTY : + +# Cargo related strings +# Plural cargo name +STR_CARGO_PLURAL_NOTHING : +STR_CARGO_PLURAL_COAL :कोयला + +# Singular cargo name +STR_CARGO_SINGULAR_NOTHING : +STR_CARGO_SINGULAR_MAIZE :मक्का +STR_CARGO_SINGULAR_SWEETS :मिठाई + +# Quantity of cargo +STR_QUANTITY_NOTHING : + +# Two letter abbreviation of cargo name +STR_ABBREV_NOTHING : + +# 'Mode' of transport for cargoes + +# Colours, do not shuffle +STR_COLOUR_PINK :गुलाबी + +# Units used in OpenTTD + + + + + + + + +# Common window strings + + + +# Show engines button + + +# Query window + +# On screen keyboard window + +# Measurement tooltip + + +# These are used in buttons +STR_SORT_BY_CAPTION_DATE :{BLACK}दिनाँक +# These are used in dropdowns +STR_SORT_BY_PRODUCTION :उत्पादन +STR_SORT_BY_PROFIT_LAST_YEAR :पिछले वर्ष का लाभ +STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR :इस वर्ष का कुल लाभ +STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :पिछले वर्ष का औसत लाभ + +# Group by options for vehicle list + +# Tooltips for the main toolbar + +# Extra tooltips for the scenario editor toolbar + +############ range for SE file menu starts +STR_SCENEDIT_FILE_MENU_SEPARATOR : +############ range for SE file menu starts + +############ range for settings menu starts +############ range ends here + +############ range for file menu starts +STR_FILE_MENU_SEPARATOR : +############ range ends here + +# map menu + +############ range for town menu starts +############ range ends here + +############ range for subsidies menu starts +############ range ends here + +############ range for graph menu starts +############ range ends here + +############ range for company league menu starts +############ range ends here + +############ range for industry menu starts +############ range ends here + +############ range for railway construction menu starts +############ range ends here + +############ range for road construction menu starts +############ range ends here + +############ range for waterways construction menu starts +############ range ends here + +############ range for airport construction menu starts +############ range ends here + +############ range for landscaping menu starts +############ range ends here + +############ range for music menu starts +############ range ends here + +############ range for message menu starts +############ range ends here + +############ range for about menu starts +STR_ABOUT_MENU_SEPARATOR : +############ range ends here + +############ range for ordinal numbers used for the place in the highscore window +STR_ORDINAL_NUMBER_2ND :द्वितीय +############ range for ordinal numbers ends + +############ range for days starts +############ range for days ends + +############ range for months starts +STR_MONTH_ABBREV_JAN :जन +STR_MONTH_ABBREV_NOV :नव + +############ range for months ends + +# Graph window +STR_GRAPH_X_LABEL_MONTH :{TINY_FONT}{STRING} +STR_GRAPH_Y_LABEL :{TINY_FONT}{STRING} +STR_GRAPH_Y_LABEL_NUMBER :{TINY_FONT}{COMMA} + + +STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLACK}{STRING} + + +# Graph key window + +# Company league window + +# Performance detail window +############ Those following lines need to be in this order!! +STR_PERFORMANCE_DETAIL_VEHICLES :{BLACK}वाहन: +############ End of order list + +# Music window +STR_MUSIC_TRACK_DIGIT :{TINY_FONT}{DKGREEN}{ZEROFILL_NUM} + +# Playlist window + +# Highscore window + +# Smallmap window + + + +STR_SMALLMAP_LINKSTATS :{TINY_FONT}{STRING} +STR_SMALLMAP_COMPANY :{TINY_FONT}{COMPANY} +STR_SMALLMAP_TOWN :{TINY_FONT}{WHITE}{TOWN} + +# Status bar messages + +# News message history + +STR_NEWS_CUSTOM_ITEM :{BIG_FONT}{BLACK}{STRING} + + + + + + + + + + + +# Order review system / warnings + + + +STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLACK}{ENGINE} + + + + + +# Extra view window + +# Game options window + +############ start of currency region +STR_GAME_OPTIONS_CURRENCY_HKD :हाँग काँग डॉलर (एचकेडी) +############ end of currency region + +STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :दाईं ओर वाहन चलाएँ + + +############ start of townname region +STR_GAME_OPTIONS_TOWN_NAME_FRENCH :फ़्रेंच +STR_GAME_OPTIONS_TOWN_NAME_POLISH :पोलिश +STR_GAME_OPTIONS_TOWN_NAME_TURKISH :तुर्की +STR_GAME_OPTIONS_TOWN_NAME_ITALIAN :इटैलियन +############ end of townname region + + +############ start of autosave dropdown +STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_MONTH :प्रत्येक माह +############ end of autosave dropdown + + + + + + + + + + + + + + + + +# Custom currency window + + + + + + + + + +STR_AI_SPEED_SLOW :धीमा + + + + + + + + +# Settings tree window + + + + +STR_CONFIG_SETTING_COMPANIES_OFF :बन्द + + + + +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} + + + +STR_CONFIG_SETTING_ORDER_REVIEW_OFF :नहीं + + + + + + + + +STR_CONFIG_SETTING_SOUND_NEWS :समाचार पत्र: {STRING} + + + + + + + + +STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} + + + +STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :२x + + + + + + + + + + + + +# Config errors + +# Video initalization errors + +# Intro window + + + + + + + +# Quit window +STR_QUIT_YES :{BLACK}हाँ + +# Abandon game + +# Cheat window + +# Livery window + + + +# Face selection window + + +# Network server list + +STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x{COMMA} + + + + + + +# Start new multiplayer server + + +STR_NETWORK_START_SERVER_ADVERTISED :हाँ + + +# Network game lobby + + +STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR :{SILVER}उद्घाटन: {WHITE}{NUM} + + + +# Network connecting window + +############ Leave those lines in this order!! + +############ End of leave-in-this-order + + + +# Network company list added strings + +# Network client list + + +# Network set password + +# Network company info join/password + +# Network chat + + +# Network messages + +############ Leave those lines in this order!! +############ End of leave-in-this-order + + +# Network related errors +############ Leave those lines in this order!! +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_3 :खेल अभी भी ठहरा हुआ है ({STRING}, {STRING}, {STRING}) +############ End of leave-in-this-order + +# Content downloading window + +# Order of these is important! + +# Content downloading progress window + +# Content downloading error messages + + + +# Transparency settings window + +# Linkgraph legend window + +# Linkgraph legend window and linkgraph legend in smallmap + +# Base for station construction window(s) + +# Join station window + + +# Generic toolbar + +# Rail construction toolbar + + + +# Rail depot construction window + +# Rail waypoint construction window + +# Rail station construction window + + + +# Signal window + +# Bridge selection window + + +# Road construction toolbar + + +# Road depot construction window + +# Road vehicle station construction window + +# Waterways toolbar (last two for SE only) + +# Ship depot construction window + +# Dock construction window + +# Airport toolbar + +# Airport construction window + + + + +# Landscaping toolbar + +# Object construction window + + +# Tree planting window (last eight for SE only) + +# Land generation window (SE) + + +# Town generation window (SE) + + +STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON :{BLACK}लघु +STR_FOUND_TOWN_CITY :{BLACK}शहर + + +# Fund new industry window + +# Industry cargoes window + +# Land area window + +# Description of land area of different tiles + + + +# Houses come directly from their building names + + + + +# Industries come directly from their industry names + + + + + + +# About OpenTTD window + +# Framerate display window +STR_FRAMERATE_BYTES_GOOD :{LTBLUE}{BYTES} +STR_FRAMERATE_BYTES_WARN :{YELLOW}{BYTES} +STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} +############ Leave those lines in this order!! +############ End of leave-in-this-order +############ Leave those lines in this order!! +############ End of leave-in-this-order + + +# Save/load game/scenario + + +# World generation + +# Strings for map borders at game generation +STR_MAPGEN_BORDER_WATER :{BLACK}जल + + + +# SE Map generation + + +# Map generation progress +STR_GENERATION_PROGRESS_NUM :{BLACK}{NUM} / {NUM} + +# NewGRF settings + + + + + +# NewGRF save preset window + +# NewGRF parameters window + +# NewGRF inspect window + + + +# Sprite aligner window + + +# NewGRF (self) generated warnings/errors +STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING} + +# NewGRF related 'general' warnings + + + +# NewGRF status + +# NewGRF 'it's broken' warnings + + +# 'User removed essential NewGRFs'-placeholders for stuff without specs + +# Placeholders for other invalid stuff, e.g. vehicles that have gone (Game Script). + +# NewGRF scanning window + +# Sign list window + +# Sign window + + +# Town directory window + +# Town view window +STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} +STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP :{BLACK}स्थानीय प्राधिकारी के बारे में जानकारी दिखाएँ + +STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}फैलाएँ + + +# Town local authority window + + + +# Goal window +STR_GOALS_TEXT :{ORANGE}{STRING} +STR_GOALS_PROGRESS :{ORANGE}{STRING} +STR_GOALS_PROGRESS_COMPLETE :{GREEN}{STRING} + +# Goal question window + +############ Start of Goal Question button list +############ End of Goal Question button list + +# Subsidies window + +# Story book window +STR_STORY_BOOK_TITLE :{YELLOW}{STRING} +STR_STORY_BOOK_NEXT_PAGE_TOOLTIP :{BLACK}अगले पृष्ठ पर जाएँ + +# Station list window +STR_STATION_LIST_STATION :{YELLOW}{STATION} {STATION_FEATURES} +STR_STATION_LIST_WAYPOINT :{YELLOW}{WAYPOINT} + +# Station view window +STR_STATION_VIEW_CAPTION :{WHITE}{STATION} {STATION_FEATURES} +STR_STATION_VIEW_WAITING_CARGO :{WHITE}{CARGO_LONG} + + + + + + +############ range for rating starts +############ range for rating ends + + + + + +# Waypoint/buoy view window +STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOINT} + + +# Finances window +STR_FINANCES_YEAR :{WHITE}{NUM} +STR_FINANCES_POSITIVE_INCOME :{BLACK}+{CURRENCY_LONG} +STR_FINANCES_TOTAL_CURRENCY :{BLACK}{CURRENCY_LONG} +STR_FINANCES_BORROW_BUTTON :{BLACK}{CURRENCY_LONG} उधार + +# Company view +STR_COMPANY_VIEW_CAPTION :{WHITE}{COMPANY} {BLACK}{COMPANY_NUM} +STR_COMPANY_VIEW_PRESIDENT_MANAGER_TITLE :{WHITE}{PRESIDENT_NAME}{}{GOLD}(प्रबन्धक) + + + + + + + +# Company infrastructure window + +# Industry directory +STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}उद्योग +STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} +STR_INDUSTRY_DIRECTORY_ITEM_PROD1 :{ORANGE}{INDUSTRY} {STRING} + +# Industry view +STR_INDUSTRY_VIEW_CAPTION :{WHITE}{INDUSTRY} + + +STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING}{BLACK}{3:STRING} + + +# Vehicle lists + + + + + + + + +# Group window + + + + + + + + +# Build vehicle window + + +############ range for vehicle availability starts +############ range for vehicle availability ends + + + + + + + + + + + + + +# Depot window +STR_DEPOT_CAPTION :{WHITE}{DEPOT} + + +STR_DEPOT_VEHICLE_TOOLTIP :{BLACK}{ENGINE}{STRING} + + + + + + + + + + + + + + + +# Engine preview window + + + +STR_ENGINE_PREVIEW_SHIP :जहाज + + +# Autoreplace window + + + +STR_REPLACE_VEHICLES_STOP :वाहन प्रतिस्थापित करना बन्द करें + + + + + +# Vehicle view +STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} + + + + + + + + + + + +# Messages in the start stop button in the vehicle view + + +# Vehicle stopped/started animations + +# Vehicle details + + +# The next two need to stay in this order + + + + + + + + +# Extra buttons for train details windows + + + + + +# Vehicle refit + + + + +# Order view + +STR_ORDER_TEXT :{STRING} {STRING} {STRING} + + +# Order bottom buttons + + + + + + +# Conditional order variables, must follow order of OrderConditionVariable enum + +STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS :के बराबर है + + + + + + +# String parts to build the order string + + +STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT :{STRING} {STRING} {STRING} +STR_ORDER_GO_TO_DEPOT_FORMAT :{STRING} {DEPOT} + + +STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING} + + + + + + + + + +# Time table window + + + + + + + + + + + + + + + +# Date window (for timetable) +STR_DATE_MONTH_TOOLTIP :{BLACK}महीना चुनें + + +# AI debug window +STR_AI_DEBUG_NAME_AND_VERSION :{BLACK}{STRING} (v{NUM}) + + +# AI configuration window + + + +STR_AI_CONFIG_CHANGE_NONE : + +# Available AIs window + + + + +# AI Parameters + + +# Textfile window + + +# Vehicle loading indicators + +# Income 'floats' +STR_INCOME_FLOAT_COST :{RED}मूल्य: {CURRENCY_LONG} + +# Saveload messages + +# Map generation messages + + + + + +# Soundset messages + +# Screenshot related messages + + +# Error message titles +STR_ERROR_MESSAGE_CAPTION :{YELLOW}सन्देश + +# Generic construction errors + +# Local authority errors + +# Levelling errors +STR_ERROR_TOO_HIGH :{WHITE}... बहुत ऊँचा + +# Company related errors + + +# Town related errors + +# Industry related errors + + +# Station construction related errors + + +# Station destruction related errors + + +# Waypoint related errors + + + +# Depot related errors + + + + + + +# Autoreplace related errors + +# Rail construction errors + + +# Road construction errors + +# Waterway construction errors + +# Tree related errors + +# Bridge related errors + +# Tunnel related errors + +# Object related errors + +# Group related errors + +# Generic vehicle errors + + +STR_ERROR_CAN_T_RENAME_SHIP :{WHITE}जहाज का नामकरण नहीं कर सकते... + + + + + + + + + + +# Specific vehicle errors + + + +# Order related errors + + +# Timetable related errors + +# Sign related errors + +# Translatable comment for OpenTTD's desktop shortcut + +# Translatable descriptions in media/baseset/*.ob* files + +##id 0x2000 +# Town building names + +##id 0x4800 +# industry names +STR_INDUSTRY_NAME_POWER_STATION :बिजलीघर +STR_INDUSTRY_NAME_BANK_TROPIC_ARCTIC :बैंक + +############ WARNING, using range 0x6000 for strings that are stored in the savegame +############ These strings may never get a new id, or savegames will break! +##id 0x6000 +STR_SV_EMPTY : + +STR_SV_STNAME :{STRING} +STR_SV_STNAME_AIRPORT :{STRING} हवाई अड्डा +STR_SV_STNAME_BUOY :{STRING} +STR_SV_STNAME_WAYPOINT :{STRING} +##id 0x6020 +############ end of savegame specific region! + +##id 0x8000 +# Vehicle names +STR_VEHICLE_NAME_TRAIN_WAGON_RAIL_COAL_CAR :कोयला वाहन +STR_VEHICLE_NAME_TRAIN_WAGON_RAIL_FRUIT_TRUCK :फल वाहन +STR_VEHICLE_NAME_TRAIN_WAGON_MAGLEV_BUBBLE_VAN :बबल वैन +STR_VEHICLE_NAME_ROAD_VEHICLE_PLODDYPHUT_MKIII_BUS :प्लॉडीपीहट एमके३ बस +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_COTSWALD_LB_3 :बेकवेल कॉट्सवॉल्ड एलबी-३ +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_LUCKETT_LB_9 :बेकवेल लकेट एलबी-९ +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_LUCKETT_LB80 :बेकवेल लकेट एलबी८० +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_LUCKETT_LB_11 :बेकवेल लकेट एलबी-११ +STR_VEHICLE_NAME_AIRCRAFT_DARWIN_600 :डार्विन ‌६०० + +##id 0x8800 +# Formatting of some strings +STR_FORMAT_DATE_SHORT :{STRING} {NUM} +STR_FORMAT_DATE_LONG :{STRING} {STRING} {NUM} + +STR_FORMAT_INDUSTRY_NAME :{TOWN} {STRING} + + + + +# Viewport strings +STR_VIEWPORT_TOWN :{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} + +STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} +STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} + +STR_VIEWPORT_STATION :{STATION} {STATION_FEATURES} +STR_VIEWPORT_STATION_TINY :{TINY_FONT}{STATION} + +STR_VIEWPORT_WAYPOINT :{WAYPOINT} +STR_VIEWPORT_WAYPOINT_TINY :{TINY_FONT}{WAYPOINT} + +# Simple strings to get specific types of data +STR_COMPANY_NAME :{COMPANY} +STR_COMPANY_NAME_COMPANY_NUM :{COMPANY} {COMPANY_NUM} +STR_DEPOT_NAME :{DEPOT} +STR_ENGINE_NAME :{ENGINE} +STR_GROUP_NAME :{GROUP} +STR_INDUSTRY_NAME :{INDUSTRY} +STR_PRESIDENT_NAME :{PRESIDENT_NAME} +STR_SIGN_NAME :{SIGN} +STR_STATION_NAME :{STATION} +STR_TOWN_NAME :{TOWN} +STR_VEHICLE_NAME :{VEHICLE} +STR_WAYPOINT_NAME :{WAYPOINT} + +STR_JUST_CARGO :{CARGO_LONG} +STR_JUST_CHECKMARK :{CHECKMARK} +STR_JUST_COMMA :{COMMA} +STR_JUST_CURRENCY_SHORT :{CURRENCY_SHORT} +STR_JUST_CURRENCY_LONG :{CURRENCY_LONG} +STR_JUST_CARGO_LIST :{CARGO_LIST} +STR_JUST_INT :{NUM} +STR_JUST_DATE_TINY :{DATE_TINY} +STR_JUST_DATE_SHORT :{DATE_SHORT} +STR_JUST_DATE_LONG :{DATE_LONG} +STR_JUST_DATE_ISO :{DATE_ISO} +STR_JUST_STRING :{STRING} +STR_JUST_STRING_STRING :{STRING}{STRING} +STR_JUST_RAW_STRING :{STRING} +STR_JUST_BIG_RAW_STRING :{BIG_FONT}{STRING} + +# Slightly 'raw' stringcodes with colour or size +STR_BLACK_COMMA :{BLACK}{COMMA} +STR_TINY_BLACK_COMA :{TINY_FONT}{BLACK}{COMMA} +STR_TINY_COMMA :{TINY_FONT}{COMMA} +STR_BLUE_COMMA :{BLUE}{COMMA} +STR_RED_COMMA :{RED}{COMMA} +STR_WHITE_COMMA :{WHITE}{COMMA} +STR_TINY_BLACK_DECIMAL :{TINY_FONT}{BLACK}{DECIMAL} +STR_COMPANY_MONEY :{WHITE}{CURRENCY_LONG} +STR_BLACK_DATE_LONG :{BLACK}{DATE_LONG} +STR_WHITE_DATE_LONG :{WHITE}{DATE_LONG} +STR_SHORT_DATE :{WHITE}{DATE_TINY} +STR_DATE_LONG_SMALL :{TINY_FONT}{BLACK}{DATE_LONG} +STR_TINY_GROUP :{TINY_FONT}{GROUP} +STR_BLACK_INT :{BLACK}{NUM} +STR_ORANGE_INT :{ORANGE}{NUM} +STR_WHITE_SIGN :{WHITE}{SIGN} +STR_TINY_BLACK_STATION :{TINY_FONT}{BLACK}{STATION} +STR_BLACK_STRING :{BLACK}{STRING} +STR_BLACK_RAW_STRING :{BLACK}{STRING} +STR_ORANGE_STRING :{ORANGE}{STRING} +STR_LTBLUE_STRING :{LTBLUE}{STRING} +STR_WHITE_STRING :{WHITE}{STRING} +STR_ORANGE_STRING1_WHITE :{ORANGE}{STRING}{WHITE} +STR_ORANGE_STRING1_LTBLUE :{ORANGE}{STRING}{LTBLUE} +STR_TINY_BLACK_HEIGHT :{TINY_FONT}{BLACK}{HEIGHT} +STR_TINY_BLACK_VEHICLE :{TINY_FONT}{BLACK}{VEHICLE} +STR_TINY_RIGHT_ARROW :{TINY_FONT}{RIGHT_ARROW} + + +STR_TRAIN :{BLACK}{TRAIN} +STR_BUS :{BLACK}{BUS} +STR_LORRY :{BLACK}{LORRY} +STR_PLANE :{BLACK}{PLANE} +STR_SHIP :{BLACK}{SHIP} + diff --git a/src/lang/unfinished/ido.txt b/src/lang/ido.txt similarity index 100% rename from src/lang/unfinished/ido.txt rename to src/lang/ido.txt diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 3961516e1e..e06bead70c 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :12개월마다 STR_GAME_OPTIONS_LANGUAGE :{BLACK}언어 STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}사용할 언어를 선택하세요 +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% 번역됨) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}전체화면 STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}OpenTTD를 전체화면으로 플레이하려면 클릭하세요. @@ -1298,7 +1299,7 @@ STR_CONFIG_SETTING_MULTIPINDTOWN_HELPTEXT :일반적으로 STR_CONFIG_SETTING_SIGNALSIDE :신호기 보이기: {STRING} STR_CONFIG_SETTING_SIGNALSIDE_HELPTEXT :선로의 어느 쪽에 신호기를 설치할 지 선택합니다. STR_CONFIG_SETTING_SIGNALSIDE_LEFT :왼쪽에 -STR_CONFIG_SETTING_SIGNALSIDE_DRIVING_SIDE :진행 방향에 +STR_CONFIG_SETTING_SIGNALSIDE_DRIVING_SIDE :자동차 통행 방향에 STR_CONFIG_SETTING_SIGNALSIDE_RIGHT :오른쪽에 STR_CONFIG_SETTING_SHOWFINANCES :연말에 자동으로 재정 창을 띄우기: {STRING} STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :이 설정을 켜면. 회사의 재정 상태를 확인하기 쉽도록 매년 말에 재정 창이 자동으로 뜹니다. diff --git a/src/lang/unfinished/macedonian.txt b/src/lang/macedonian.txt similarity index 100% rename from src/lang/unfinished/macedonian.txt rename to src/lang/macedonian.txt diff --git a/src/lang/unfinished/maltese.txt b/src/lang/maltese.txt similarity index 100% rename from src/lang/unfinished/maltese.txt rename to src/lang/maltese.txt diff --git a/src/lang/unfinished/marathi.txt b/src/lang/marathi.txt similarity index 100% rename from src/lang/unfinished/marathi.txt rename to src/lang/marathi.txt diff --git a/src/lang/unfinished/persian.txt b/src/lang/persian.txt similarity index 100% rename from src/lang/unfinished/persian.txt rename to src/lang/persian.txt diff --git a/src/lang/polish.txt b/src/lang/polish.txt index b8ad7b63a7..86abe00467 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -1374,6 +1374,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Co 12 miesięcy STR_GAME_OPTIONS_LANGUAGE :{BLACK}Język STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Wybierz język interfejsu +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} (ukończono {NUM}%) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pełny ekran STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Zaznacz, jeśli chcesz grać w OpenTTD w trybie pełnoekranowym diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 7b09b1dfed..6740bca0af 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -1140,6 +1140,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Каждый г STR_GAME_OPTIONS_LANGUAGE :{BLACK}Язык STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Язык пользовательского интерфейса +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% перевед{P ён ено ено}) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Полноэкранный режим STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Включить/выключить полноэкранный режим @@ -1150,11 +1151,11 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :Другое STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Аппаратное ускорение -STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить аппаратное ускорение в OpenTTD. После этого игру потребуется перезапустить. +STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Включить/выключить аппаратное ускорение. После этого игру потребуется перезапустить. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Эта настройка будет применена только после перезапуска игры STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Вертикальная синхронизация -STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить вертикальную синхронизацию. После этого игру потребуется перезапустить. Работает только при включённом аппаратном ускорении. +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Включить/выключить вертикальную синхронизацию. После этого игру потребуется перезапустить. Работает только при включённом аппаратном ускорении. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Размер элементов интерфейса STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Выберите размер элементов интерфейса diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index bf754eb8ef..e5bccc0194 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -995,6 +995,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecciona el idioma a emplear en interfaz del juego +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completo) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marca esta opción para jugar OpenTTD a pantalla completa diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index abab3b94ff..33c6cdd560 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -995,6 +995,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Elegir el idioma de la interfaz +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completado) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Jugar OpenTTD en pantalla completa @@ -1008,6 +1009,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelerac STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Activar esta casilla para intentar emplear la aceleración por hardware. Este cambio solo tiene efecto tras reiniciar el juego STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Esta configuración solo tendrá efecto después de reiniciar el juego +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Activar esta casilla para realizar sincronización vertical de pantalla (VSync). Este cambio solo tiene efecto tras reiniciar el juego y si la aceleración por hardware está activada. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño de la interfaz STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Elegir el tamaño de los elementos de la interfaz diff --git a/src/lang/unfinished/urdu.txt b/src/lang/urdu.txt similarity index 100% rename from src/lang/unfinished/urdu.txt rename to src/lang/urdu.txt diff --git a/src/language.h b/src/language.h index faac595613..9d2499068b 100644 --- a/src/language.h +++ b/src/language.h @@ -58,6 +58,7 @@ struct LanguagePackHeader { char cases[MAX_NUM_CASES][CASE_GENDER_LEN]; ///< the cases used by this translation bool IsValid() const; + bool IsReasonablyFinished() const; /** * Get the index for the given gender. diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 841f785c16..2194a77702 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -483,7 +483,6 @@ static const char * const _credits[] = { u8" Ingo von Borstel (planetmaker) - General, Support (since 1.1)", u8" Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)", u8" Jos\u00e9 Soler (Terkhen) - General coding (since 1.0)", - u8" Leif Linse (Zuu) - AI/Game Script (since 1.2)", u8"", u8"Inactive Developers:", u8" Jean-Fran\u00e7ois Claeys (Belugas) - GUI, NewGRF and more (0.4.5 - 1.0)", @@ -496,6 +495,7 @@ static const char * const _credits[] = { u8" Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)", u8" Patric Stout (TrueBrain) - NoAI, NoGo, Network (0.3 - 1.2), sys op (active)", u8" Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)", + u8" Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6)", u8"", u8"Retired Developers:", u8" Tam\u00e1s Farag\u00f3 (Darkvater) - Ex-Lead coder (0.3 - 0.5)", diff --git a/src/music/midifile.cpp b/src/music/midifile.cpp index 4bdd3e4e39..dcd2cefd8d 100644 --- a/src/music/midifile.cpp +++ b/src/music/midifile.cpp @@ -1142,7 +1142,7 @@ static void RegisterConsoleMidiCommands() { static bool registered = false; if (!registered) { - IConsoleCmdRegister("dumpsmf", CmdDumpSMF); + IConsole::CmdRegister("dumpsmf", CmdDumpSMF); registered = true; } } diff --git a/src/network/network.cpp b/src/network/network.cpp index a3b820ceb8..1e71e39c49 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -340,6 +340,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err) STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER, STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP, STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN, + STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME, }; static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END); @@ -722,6 +723,8 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c if (address.GetPort() == 0) return; + if (!NetworkValidateClientName()) return; + strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host)); _settings_client.network.last_port = address.GetPort(); _network_join_as = join_as; diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 138efd0a94..f6b71a74bd 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -282,7 +282,6 @@ static void SendChat(const char *buf, DestType type, int dest) /** Window to enter the chat message in. */ struct NetworkChatWindow : public Window { DestType dtype; ///< The type of destination. - StringID dest_string; ///< String representation of the destination. int dest; ///< The identifier of the destination. QueryString message_editbox; ///< Message editbox. @@ -307,9 +306,10 @@ struct NetworkChatWindow : public Window { STR_NULL, }; assert((uint)this->dtype < lengthof(chat_captions)); - this->dest_string = chat_captions[this->dtype]; - this->InitNested(type); + this->CreateNestedTree(); + this->GetWidget(WID_NC_DESTINATION)->widget_data = chat_captions[this->dtype]; + this->FinishInitNested(type); this->SetFocusedWidget(WID_NC_TEXTBOX); InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); @@ -464,27 +464,13 @@ struct NetworkChatWindow : public Window { return pt; } - void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override + void SetStringParameters(int widget) const override { if (widget != WID_NC_DESTINATION) return; if (this->dtype == DESTTYPE_CLIENT) { SetDParamStr(0, NetworkClientInfo::GetByClientID((ClientID)this->dest)->client_name); } - Dimension d = GetStringBoundingBox(this->dest_string); - d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; - *size = maxdim(*size, d); - } - - void DrawWidget(const Rect &r, int widget) const override - { - if (widget != WID_NC_DESTINATION) return; - - if (this->dtype == DESTTYPE_CLIENT) { - SetDParamStr(0, NetworkClientInfo::GetByClientID((ClientID)this->dest)->client_name); - } - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, this->dest_string, TC_BLACK, SA_RIGHT); } void OnClick(Point pt, int widget, int click_count) override @@ -532,7 +518,7 @@ static const NWidgetPart _nested_chat_window_widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_GREY, WID_NC_CLOSE), NWidget(WWT_PANEL, COLOUR_GREY, WID_NC_BACKGROUND), NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXT, COLOUR_GREY, WID_NC_DESTINATION), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NULL, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_NC_DESTINATION), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetTextColour(TC_BLACK), SetAlignment(SA_TOP | SA_RIGHT), SetDataTip(STR_NULL, STR_NULL), NWidget(WWT_EDITBOX, COLOUR_GREY, WID_NC_TEXTBOX), SetMinimalSize(100, 12), SetPadding(1, 0, 1, 0), SetResize(1, 0), SetDataTip(STR_NETWORK_CHAT_OSKTITLE, STR_NULL), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NC_SENDBUTTON), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NETWORK_CHAT_SEND, STR_NULL), diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 59f0d13c5e..1a1cfbb43a 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -736,6 +736,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET; if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; + /* The server validates the name when receiving it from clients, so when it is wrong + * here something went really wrong. In the best case the packet got malformed on its + * way too us, in the worst case the server is broken or compromised. */ + if (!NetworkIsValidClientName(name)) return NETWORK_RECV_STATUS_MALFORMED_PACKET; ci = NetworkClientInfo::GetByClientID(client_id); if (ci != nullptr) { @@ -781,26 +785,27 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { static const StringID network_error_strings[] = { - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NEWGRF_MISMATCH - STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_AUTHORIZED - STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_EXPECTED - STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE - STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD - STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH - STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED - STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER - STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL - STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS - STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD - STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER - STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP - STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NEWGRF_MISMATCH + STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_AUTHORIZED + STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_EXPECTED + STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE + STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD + STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH + STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED + STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER + STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL + STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS + STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD + STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER + STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP + STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN + STR_NETWORK_ERROR_INVALID_CLIENT_NAME, // NETWORK_ERROR_INVALID_CLIENT_NAME }; static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END); @@ -1414,6 +1419,56 @@ void NetworkClientsToSpectators(CompanyID cid) cur_company.Restore(); } +/** + * Check whether the given client name is deemed valid for use in network games. + * An empty name (null or '') is not valid as that is essentially no name at all. + * A name starting with white space is not valid for tab completion purposes. + * @param client_name The client name to check for validity. + * @return True iff the name is valid. + */ +bool NetworkIsValidClientName(const char *client_name) +{ + if (StrEmpty(client_name)) return false; + if (*client_name == ' ') return false; + return true; +} + +/** + * Trim the given client name in place, i.e. remove leading and trailing spaces. + * After the trim check whether the client name is valid. A client name is valid + * whenever the name is not empty and does not start with spaces. This check is + * done via \c NetworkIsValidClientName. + * When the client name is valid, this function returns true. + * When the client name is not valid a GUI error message is shown telling the + * user to set the client name and this function returns false. + * + * This function is not suitable for ensuring a valid client name at the server + * as the error message will then be shown to the host instead of the client. + * @param client_name The client name to validate. It will be trimmed of leading + * and trailing spaces. + * @return True iff the client name is valid. + */ +bool NetworkValidateClientName(char *client_name) +{ + StrTrimInPlace(client_name); + if (NetworkIsValidClientName(client_name)) return true; + + ShowErrorMessage(STR_NETWORK_ERROR_BAD_PLAYER_NAME, INVALID_STRING_ID, WL_ERROR); + return false; +} + +/** + * Convenience method for NetworkValidateClientName on _settings_client.network.client_name. + * It trims the client name and checks whether it is empty. When it is empty + * an error message is shown to the GUI user. + * See \c NetworkValidateClientName(char*) for details about the functionality. + * @return True iff the client name is valid. + */ +bool NetworkValidateClientName() +{ + return NetworkValidateClientName(_settings_client.network.client_name); +} + /** * Send the server our name. */ @@ -1422,6 +1477,11 @@ void NetworkUpdateClientName() NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(_network_own_client_id); if (ci == nullptr) return; + /* There is no validation on string settings, it is actually a post change callback. + * This method is called from that post change callback. So, when the client name is + * changed via the console there is no easy way to prevent an invalid name. Though, + * we can prevent it getting sent here. */ + if (!NetworkValidateClientName()) return; /* Don't change the name if it is the same as the old name */ if (strcmp(ci->client_name, _settings_client.network.client_name) != 0) { diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index 4662791257..0cd711877d 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -863,55 +863,31 @@ public: EventState OnKeyPress(WChar key, uint16 keycode) override { - switch (keycode) { - case WKC_UP: - /* scroll up by one */ - if (this->list_pos > 0) this->list_pos--; - break; - case WKC_DOWN: - /* scroll down by one */ - if (this->list_pos < (int)this->content.size() - 1) this->list_pos++; - break; - case WKC_PAGEUP: - /* scroll up a page */ - this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity(); - break; - case WKC_PAGEDOWN: - /* scroll down a page */ - this->list_pos = std::min(this->list_pos + this->vscroll->GetCapacity(), (int)this->content.size() - 1); - break; - case WKC_HOME: - /* jump to beginning */ - this->list_pos = 0; - break; - case WKC_END: - /* jump to end */ - this->list_pos = (int)this->content.size() - 1; - break; - - case WKC_SPACE: - case WKC_RETURN: - if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) { - if (this->selected != nullptr) { - _network_content_client.ToggleSelectedState(this->selected); - this->content.ForceResort(); - this->InvalidateData(); - } - if (this->filter_data.types.any()) { - this->content.ForceRebuild(); - this->InvalidateData(); + if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_NOT_HANDLED) { + switch (keycode) { + case WKC_SPACE: + case WKC_RETURN: + if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) { + if (this->selected != nullptr) { + _network_content_client.ToggleSelectedState(this->selected); + this->content.ForceResort(); + this->InvalidateData(); + } + if (this->filter_data.types.any()) { + this->content.ForceRebuild(); + this->InvalidateData(); + } + return ES_HANDLED; } - return ES_HANDLED; - } - /* space is pressed and filter is focused. */ - FALLTHROUGH; + /* space is pressed and filter is focused. */ + FALLTHROUGH; - default: - return ES_NOT_HANDLED; + default: + return ES_NOT_HANDLED; + } } if (this->content.size() == 0) { - this->list_pos = 0; // above stuff may result in "-1". if (this->UpdateFilterState()) { this->content.ForceRebuild(); this->InvalidateData(); diff --git a/src/network/network_func.h b/src/network/network_func.h index 2625d3d275..448b434fde 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -36,6 +36,9 @@ extern StringList _network_host_list; extern StringList _network_ban_list; byte NetworkSpectatorCount(); +bool NetworkIsValidClientName(const char *client_name); +bool NetworkValidateClientName(); +bool NetworkValidateClientName(char *client_name); void NetworkUpdateClientName(); bool NetworkCompanyHasClients(CompanyID company); const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 7d535ed250..6afe58b3f5 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -68,8 +68,8 @@ void UpdateNetworkGameWindow() } typedef GUIList GUIGameServerList; -typedef uint16 ServerListPosition; -static const ServerListPosition SLP_INVALID = 0xFFFF; +typedef int ServerListPosition; +static const ServerListPosition SLP_INVALID = -1; /** Full blown container to make it behave exactly as we want :) */ class NWidgetServerListHeader : public NWidgetContainer { @@ -786,39 +786,8 @@ public: EventState state = ES_NOT_HANDLED; /* handle up, down, pageup, pagedown, home and end */ - if (keycode == WKC_UP || keycode == WKC_DOWN || keycode == WKC_PAGEUP || keycode == WKC_PAGEDOWN || keycode == WKC_HOME || keycode == WKC_END) { - if (this->servers.size() == 0) return ES_HANDLED; - switch (keycode) { - case WKC_UP: - /* scroll up by one */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - if (this->list_pos > 0) this->list_pos--; - break; - case WKC_DOWN: - /* scroll down by one */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - if (this->list_pos < this->servers.size() - 1) this->list_pos++; - break; - case WKC_PAGEUP: - /* scroll up a page */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity(); - break; - case WKC_PAGEDOWN: - /* scroll down a page */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - this->list_pos = std::min(this->list_pos + this->vscroll->GetCapacity(), (int)this->servers.size() - 1); - break; - case WKC_HOME: - /* jump to beginning */ - this->list_pos = 0; - break; - case WKC_END: - /* jump to end */ - this->list_pos = (ServerListPosition)this->servers.size() - 1; - break; - default: NOT_REACHED(); - } + if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_HANDLED) { + if (this->list_pos == SLP_INVALID) return ES_HANDLED; this->server = this->servers[this->list_pos]; @@ -854,12 +823,9 @@ public: } case WID_NG_CLIENT: - /* Make sure the name does not start with a space, so TAB completion works */ - if (!StrEmpty(this->name_editbox.text.buf) && this->name_editbox.text.buf[0] != ' ') { - strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name)); - } else { - strecpy(_settings_client.network.client_name, "Player", lastof(_settings_client.network.client_name)); - } + /* Validation of the name will happen once the user tries to join or start a game, as getting + * error messages while typing (e.g. when you clear the name) defeats the purpose of the check. */ + strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name)); break; } } @@ -1292,6 +1258,8 @@ static WindowDesc _network_start_server_window_desc( static void ShowNetworkStartServerWindow() { + if (!NetworkValidateClientName()) return; + DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); @@ -1584,6 +1552,8 @@ static WindowDesc _network_lobby_window_desc( */ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) { + if (!NetworkValidateClientName()) return; + DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index a657647ed4..6492e3d423 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -987,8 +987,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) break; } - /* We need a valid name.. make it Player */ - if (StrEmpty(name)) strecpy(name, "Player", lastof(name)); + if (!NetworkIsValidClientName(name)) { + /* An invalid client name was given. However, the client ensures the name + * is valid before it is sent over the network, so something went horribly + * wrong. This is probably someone trying to troll us. */ + return this->SendError(NETWORK_ERROR_INVALID_CLIENT_NAME); + } if (!NetworkFindName(name, lastof(name))) { // Change name if duplicate /* We could not create a name for this client */ @@ -1558,6 +1562,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; if (ci != nullptr) { + if (!NetworkIsValidClientName(client_name)) { + /* An invalid client name was given. However, the client ensures the name + * is valid before it is sent over the network, so something went horribly + * wrong. This is probably someone trying to troll us. */ + return this->SendError(NETWORK_ERROR_INVALID_CLIENT_NAME); + } + /* Display change */ if (NetworkFindName(client_name, lastof(client_name))) { NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, client_name); diff --git a/src/network/network_type.h b/src/network/network_type.h index c7960cfe78..8608253a3b 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -133,6 +133,7 @@ enum NetworkErrorCode { NETWORK_ERROR_TIMEOUT_COMPUTER, NETWORK_ERROR_TIMEOUT_MAP, NETWORK_ERROR_TIMEOUT_JOIN, + NETWORK_ERROR_INVALID_CLIENT_NAME, NETWORK_ERROR_END, }; diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index b8ab749622..506fc3a68f 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1327,42 +1327,8 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { { if (!this->editable) return ES_NOT_HANDLED; - switch (keycode) { - case WKC_UP: - /* scroll up by one */ - if (this->avail_pos > 0) this->avail_pos--; - break; - - case WKC_DOWN: - /* scroll down by one */ - if (this->avail_pos < (int)this->avails.size() - 1) this->avail_pos++; - break; - - case WKC_PAGEUP: - /* scroll up a page */ - this->avail_pos = (this->avail_pos < this->vscroll2->GetCapacity()) ? 0 : this->avail_pos - this->vscroll2->GetCapacity(); - break; - - case WKC_PAGEDOWN: - /* scroll down a page */ - this->avail_pos = std::min(this->avail_pos + this->vscroll2->GetCapacity(), (int)this->avails.size() - 1); - break; - - case WKC_HOME: - /* jump to beginning */ - this->avail_pos = 0; - break; - - case WKC_END: - /* jump to end */ - this->avail_pos = (uint)this->avails.size() - 1; - break; - - default: - return ES_NOT_HANDLED; - } + if (this->vscroll2->UpdateListPositionOnKeyPress(this->avail_pos, keycode) == ES_NOT_HANDLED) return ES_NOT_HANDLED; - if (this->avails.size() == 0) this->avail_pos = -1; if (this->avail_pos >= 0) { this->active_sel = nullptr; DeleteWindowByClass(WC_GRF_PARAMETERS); diff --git a/src/news_gui.cpp b/src/news_gui.cpp index bca3a35753..023353f36e 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -422,7 +422,7 @@ struct NewsWindow : Window { { switch (widget) { case WID_N_CAPTION: - DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, STR_NEWS_MESSAGE_CAPTION); + DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, TC_FROMSTRING, STR_NEWS_MESSAGE_CAPTION, SA_HOR_CENTER); break; case WID_N_PANEL: @@ -1205,7 +1205,7 @@ struct MessageHistoryWindow : Window { NewsItem *ni = _latest_news; if (ni == nullptr) return; - for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WD_FRAMERECT_TOP, this->line_height); n > 0; n--) { + for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WD_FRAMERECT_TOP); n > 0; n--) { ni = ni->prev; if (ni == nullptr) return; } diff --git a/src/os/windows/ottdres.rc.in b/src/os/windows/ottdres.rc.in index 741fa0e105..31309382cf 100644 --- a/src/os/windows/ottdres.rc.in +++ b/src/os/windows/ottdres.rc.in @@ -77,8 +77,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,11,0,${REV_ISODATE} - PRODUCTVERSION 1,11,0,${REV_ISODATE} + FILEVERSION ${REV_MAJOR},${REV_MINOR},${REV_BUILD},${REV_ISODATE} + PRODUCTVERSION ${REV_MAJOR},${REV_MINOR},${REV_BUILD},${REV_ISODATE} FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 28d16283cd..a1775fdccb 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1578,7 +1578,7 @@ public: break; case WID_BRAS_NEWST_LIST: { - int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BRAS_NEWST_LIST, 0, this->line_height); + int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BRAS_NEWST_LIST); if (y >= (int)this->station_classes.size()) return; StationClassID station_class_id = this->station_classes[y]; if (_railstation.station_class != station_class_id) { diff --git a/src/rev.cpp.in b/src/rev.cpp.in index 3f0cd4e9bb..bc0dc264e9 100644 --- a/src/rev.cpp.in +++ b/src/rev.cpp.in @@ -91,4 +91,4 @@ const byte _openttd_revision_tagged = ${REV_ISTAG}; * version, thus making comparisons on specific revisions easy. */ /** ${REV_ISSTABLETAG} removed */ -const uint32 _openttd_newgrf_version = 1 << 28 | 12 << 24 | 0 << 20 | 0 << 19 | 28004; +const uint32 _openttd_newgrf_version = ${REV_MAJOR} << 28 | ${REV_MINOR} << 24 | ${REV_BUILD} << 20 | 0 << 19 | 28004; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d2b6454f4a..612b7cf43f 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3743,6 +3743,23 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_GROUP_REPLACE_WAGON_REMOVAL)) { + /* Propagate wagon removal flag for compatibility */ + /* Temporary bitmask of company wagon removal setting */ + uint16 wagon_removal = 0; + for (const Company *c : Company::Iterate()) { + if (c->settings.renew_keep_length) SetBit(wagon_removal, c->index); + } + for (Group *g : Group::Iterate()) { + if (g->flags != 0) { + /* Convert old replace_protection value to flag. */ + g->flags = 0; + SetBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); + } + if (HasBit(wagon_removal, g->owner)) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); + } + } + /* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */ Station::RecomputeCatchmentForAll(); diff --git a/src/saveload/group_sl.cpp b/src/saveload/group_sl.cpp index cae313ff83..c5f7e2b507 100644 --- a/src/saveload/group_sl.cpp +++ b/src/saveload/group_sl.cpp @@ -21,7 +21,7 @@ static const SaveLoad _group_desc[] = { SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle SLE_VAR(Group, owner, SLE_UINT8), SLE_VAR(Group, vehicle_type, SLE_UINT8), - SLE_VAR(Group, replace_protection, SLE_BOOL), + SLE_VAR(Group, flags, SLE_UINT8), SLE_CONDVAR(Group, livery.in_use, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), SLE_CONDVAR(Group, livery.colour1, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), SLE_CONDVAR(Group, livery.colour2, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index f4988a03bd..f677a78fdf 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -329,6 +329,7 @@ enum SaveLoadVersion : uint16 { SLV_VEH_MOTION_COUNTER, ///< 288 PR#8591 Desync safe motion counter SLV_INDUSTRY_TEXT, ///< 289 PR#8576 v1.11.0-RC1 Additional GS text for industries. SLV_MAPGEN_SETTINGS_REVAMP, ///< 290 PR#8891 v1.11 Revamp of some mapgen settings (snow coverage, desert coverage, heightmap height, custom terrain type). + SLV_GROUP_REPLACE_WAGON_REMOVAL, ///< 291 PR#7441 Per-group wagon removal flag. SL_MAX_VERSION, ///< Highest possible saveload version diff --git a/src/script/api/script_group.cpp b/src/script/api/script_group.cpp index 7f80815864..9cf6c7c957 100644 --- a/src/script/api/script_group.cpp +++ b/src/script/api/script_group.cpp @@ -89,14 +89,14 @@ { EnforcePrecondition(false, IsValidGroup(group_id)); - return ScriptObject::DoCommand(0, group_id, enable ? 1 : 0, CMD_SET_GROUP_REPLACE_PROTECTION); + return ScriptObject::DoCommand(0, group_id | GroupFlags::GF_REPLACE_PROTECTION, enable ? 1 : 0, CMD_SET_GROUP_FLAG); } /* static */ bool ScriptGroup::GetAutoReplaceProtection(GroupID group_id) { if (!IsValidGroup(group_id)) return false; - return ::Group::Get(group_id)->replace_protection; + return HasBit(::Group::Get(group_id)->flags, GroupFlags::GF_REPLACE_PROTECTION); } /* static */ int32 ScriptGroup::GetNumEngines(GroupID group_id, EngineID engine_id) diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 8a1c0b894a..59ccadf43a 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -36,6 +36,7 @@ #include "querystring_gui.h" #include "fontcache.h" #include "zoom_func.h" +#include "rev.h" #include "video/video_driver.hpp" #include "music/music_driver.hpp" @@ -220,7 +221,10 @@ struct GameOptionsWindow : Window { case WID_GO_LANG_DROPDOWN: { // Setup interface language dropdown for (uint i = 0; i < _languages.size(); i++) { - auto item = new DropDownListParamStringItem(STR_JUST_RAW_STRING, i, false); + bool hide_language = IsReleasedVersion() && !_languages[i].IsReasonablyFinished(); + if (hide_language) continue; + bool hide_percentage = IsReleasedVersion() || _languages[i].missing < _settings_client.gui.missing_strings_threshold; + auto item = new DropDownListParamStringItem(hide_percentage ? STR_JUST_RAW_STRING : STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE, i, false); if (&_languages[i] == _current_language) { *selected_index = i; item->SetParamStr(0, _languages[i].own_name); @@ -232,6 +236,7 @@ struct GameOptionsWindow : Window { * entries in the dropdown list. */ item->SetParamStr(0, _languages[i].name); } + item->SetParam(1, (LANGUAGE_TOTAL_STRINGS - _languages[i].missing) * 100 / LANGUAGE_TOTAL_STRINGS); list.emplace_back(item); } std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc); diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 4716091d3e..b8dc3fbb45 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -156,9 +156,6 @@ void CheckRedrawStationCoverage(Window *w) * @param type Cargo type * @param amount Cargo amount * @param rating ratings data for that particular cargo - * - * @note Each cargo-bar is 16 pixels wide and 6 pixels high - * @note Each rating 14 pixels wide and 1 pixel high and is 1 pixel below the cargo-bar */ static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating) { @@ -168,32 +165,33 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoID typ const CargoSpec *cs = CargoSpec::Get(type); if (!cs->IsValid()) return; + int padding = ScaleFontTrad(1); + int width = right - left; int colour = cs->rating_colour; TextColour tc = GetContrastColour(colour); - uint w = (std::min(amount, units_full) + 5) / 36; - - int height = GetCharacterHeight(FS_SMALL); + uint w = std::min(amount + 5, units_full) * width / units_full; - /* Draw total cargo (limited) on station (fits into 16 pixels) */ - if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour); + int height = GetCharacterHeight(FS_SMALL) + padding - 1; - /* Draw a one pixel-wide bar of additional cargo meter, useful - * for stations with only a small amount (<=30) */ - if (w == 0) { - uint rest = amount / 5; + if (amount > 30) { + /* Draw total cargo (limited) on station */ + GfxFillRect(left, y, left + w - 1, y + height, colour); + } else { + /* Draw a (scaled) one pixel-wide bar of additional cargo meter, useful + * for stations with only a small amount (<=30) */ + uint rest = ScaleFontTrad(amount) / 5; if (rest != 0) { - w += left; - GfxFillRect(w, y + height - rest, w, y + height, colour); + GfxFillRect(left, y + height - rest, left + padding - 1, y + height, colour); } } - DrawString(left + 1, right, y, cs->abbrev, tc); + DrawString(left + padding, right, y, cs->abbrev, tc); - /* Draw green/red ratings bar (fits into 14 pixels) */ - y += height + 2; - GfxFillRect(left + 1, y, left + 14, y, PC_RED); - rating = std::min(rating, rating_full) / 16; - if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN); + /* Draw green/red ratings bar (fits under the waiting bar) */ + y += height + padding + 1; + GfxFillRect(left + padding, y, right - padding - 1, y + padding - 1, PC_RED); + w = std::min(rating, rating_full) * (width - padding - padding) / rating_full; + if (w != 0) GfxFillRect(left + padding, y, left + w - 1, y + padding - 1, PC_GREEN); } typedef GUIList GUIStationList; @@ -217,6 +215,7 @@ protected: GUIStationList stations; Scrollbar *vscroll; + uint rating_width; /** * (Re)Build station list @@ -395,16 +394,17 @@ public: } case WID_STL_LIST: - resize->height = FONT_HEIGHT_NORMAL; + resize->height = std::max(FONT_HEIGHT_NORMAL, FONT_HEIGHT_SMALL + ScaleFontTrad(3)); size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM; - break; - case WID_STL_TRAIN: - case WID_STL_TRUCK: - case WID_STL_BUS: - case WID_STL_AIRPLANE: - case WID_STL_SHIP: - size->height = std::max(FONT_HEIGHT_SMALL, 10) + padding.height; + /* Determine appropriate width for mini station rating graph */ + this->rating_width = 0; + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + this->rating_width = std::max(this->rating_width, GetStringBoundingBox(cs->abbrev).width); + } + /* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */ + this->rating_width = this->rating_width * 16 / 10; break; case WID_STL_CARGOALL: @@ -448,6 +448,12 @@ public: bool rtl = _current_text_dir == TD_RTL; int max = std::min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.size()); int y = r.top + WD_FRAMERECT_TOP; + uint line_height = this->GetWidget(widget)->resize_y; + /* Spacing between station name and first rating graph. */ + int text_spacing = ScaleFontTrad(5); + /* Spacing between additional rating graphs. */ + int rating_spacing = ScaleFontTrad(4); + for (int i = this->vscroll->GetPosition(); i < max; ++i) { // do until max number of stations of owner const Station *st = this->stations[i]; assert(st->xy != INVALID_TILE); @@ -458,8 +464,8 @@ public: SetDParam(0, st->index); SetDParam(1, st->facilities); - int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION); - x += rtl ? -5 : 5; + int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y + (line_height - FONT_HEIGHT_NORMAL) / 2, STR_STATION_LIST_STATION); + x += rtl ? -text_spacing : text_spacing; /* show cargo waiting and station ratings */ for (uint j = 0; j < _sorted_standard_cargo_specs_size; j++) { @@ -470,17 +476,17 @@ public: * instead of drawing to the left and then incrementing * the space. */ if (rtl) { - x -= 20; + x -= rating_width + rating_spacing; if (x < r.left + WD_FRAMERECT_LEFT) break; } - StationsWndShowStationRating(x, x + 16, y, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating); + StationsWndShowStationRating(x, x + rating_width, y, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating); if (!rtl) { - x += 20; + x += rating_width + rating_spacing; if (x > r.right - WD_FRAMERECT_RIGHT) break; } } } - y += FONT_HEIGHT_NORMAL; + y += line_height; } if (this->vscroll->GetCount() == 0) { // company has no stations @@ -491,30 +497,30 @@ public: } case WID_STL_NOCARGOWAITING: { - int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER); + int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0; + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER); break; } case WID_STL_CARGOALL: { - int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); + int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0; + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); break; } case WID_STL_FACILALL: { - int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); + int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0; + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); break; } default: if (widget >= WID_STL_CARGOSTART) { const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART]; - int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1; - GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour); + int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 1 : 0; + GfxFillRect(r.left + cg_ofst + 1, r.top + cg_ofst + 1, r.right - 1 + cg_ofst, r.bottom - 1 + cg_ofst, cs->rating_colour); TextColour tc = GetContrastColour(cs->rating_colour); - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER); + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER); } break; } @@ -532,7 +538,7 @@ public: { switch (widget) { case WID_STL_LIST: { - uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST, 0, FONT_HEIGHT_NORMAL); + uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST); if (id_v >= this->stations.size()) return; // click out of list bound const Station *st = this->stations[id_v]; @@ -723,7 +729,8 @@ static NWidgetBase *CargoWidgets(int *biggest_index) for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) { NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i); - panel->SetMinimalSize(14, 11); + panel->SetMinimalSize(14, 0); + panel->SetMinimalTextLines(1, 0, FS_NORMAL); panel->SetResize(0, 0); panel->SetFill(0, 1); panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE); @@ -742,16 +749,16 @@ static const NWidgetPart _nested_company_stations_widgets[] = { NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetMinimalSize(14, 11), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetMinimalSize(14, 11), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetMinimalSize(14, 11), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetMinimalSize(14, 11), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 11), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1), - NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1), + NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 0), SetFill(0, 1), EndContainer(), NWidgetFunction(CargoWidgets), - NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(), - NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1), + NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(), + NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1), NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), @@ -1922,7 +1929,7 @@ struct StationViewWindow : public Window { { switch (widget) { case WID_SV_WAITING: - this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL) - this->vscroll->GetPosition()); + this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP) - this->vscroll->GetPosition()); break; case WID_SV_CATCHMENT: diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index 9377e63622..68002cc3f0 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -273,13 +273,14 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { const char *real_filename; /** The previous string ID that was printed. */ int prev; + uint total_strings; /** * Open a file to write to. * @param filename The file to open. */ HeaderFileWriter(const char *filename) : FileWriter("tmp.xxx"), - real_filename(stredup(filename)), prev(0) + real_filename(stredup(filename)), prev(0), total_strings(0) { fprintf(this->fh, "/* This file is automatically generated. Do not modify */\n\n"); fprintf(this->fh, "#ifndef TABLE_STRINGS_H\n"); @@ -297,6 +298,7 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { if (prev + 1 != stringid) fprintf(this->fh, "\n"); fprintf(this->fh, "static const StringID %s = 0x%X;\n", name, stringid); prev = stringid; + total_strings++; } void Finalise(const StringData &data) @@ -311,8 +313,10 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { "\n" "static const uint LANGUAGE_PACK_VERSION = 0x%X;\n" "static const uint LANGUAGE_MAX_PLURAL = %u;\n" - "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n\n", - (uint)data.Version(), (uint)lengthof(_plural_forms), max_plural_forms + "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n" + "static const uint LANGUAGE_TOTAL_STRINGS = %u;\n" + "\n", + (uint)data.Version(), (uint)lengthof(_plural_forms), max_plural_forms, total_strings ); fprintf(this->fh, "#endif /* TABLE_STRINGS_H */\n"); diff --git a/src/string.cpp b/src/string.cpp index f5315f30d6..5bcafc9fd5 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -334,6 +334,66 @@ bool StrValid(const char *str, const char *last) return *str == '\0'; } +/** + * Trim the spaces from the begin of given string in place, i.e. the string buffer + * that is passed will be modified whenever spaces exist in the given string. + * When there are spaces at the begin, the whole string is moved forward. + * @param str The string to perform the in place left trimming on. + */ +static void StrLeftTrimInPlace(char *str) +{ + if (StrEmpty(str)) return; + + char *first_non_space = str; + while (*first_non_space == ' ') first_non_space++; + + if (first_non_space == str) return; + + /* The source will reach '\0' first, but set the '\0' on the destination afterwards. */ + char *dst = str; + for (char *src = first_non_space; *src != '\0'; dst++, src++) *dst = *src; + *dst = '\0'; +} + +/** + * Trim the spaces from the end of given string in place, i.e. the string buffer + * that is passed will be modified whenever spaces exist in the given string. + * When there are spaces at the end, the '\0' will be moved forward. + * @param str The string to perform the in place left trimming on. + */ +static void StrRightTrimInPlace(char *str) +{ + if (StrEmpty(str)) return; + + char *end = str; + while (*end != '\0') end++; + + char *last_non_space = end - 1; + while (last_non_space >= str && *last_non_space == ' ') last_non_space--; + + /* The last non space points to the last character of the string that is not + * a space. For a string with only spaces or an empty string this would be + * the position before the begin of the string. The previous search ensures + * that this location before the string is not read. + * In any case, the character after the last non space character will be + * either a space or the existing termination, so it can be set to '\0'. + */ + last_non_space[1] = '\0'; +} + +/** + * Trim the spaces from given string in place, i.e. the string buffer that + * is passed will be modified whenever spaces exist in the given string. + * When there are spaces at the begin, the whole string is moved forward + * and when there are spaces at the back the '\0' termination is moved. + * @param str The string to perform the in place trimming on. + */ +void StrTrimInPlace(char *str) +{ + StrLeftTrimInPlace(str); + StrRightTrimInPlace(str); +} + /** Scans the string for colour codes and strips them */ void str_strip_colours(char *str) { diff --git a/src/string_func.h b/src/string_func.h index e45e7c5d65..0184deea18 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -54,6 +54,7 @@ bool strtolower(char *str); bool strtolower(std::string &str, std::string::size_type offs = 0); bool StrValid(const char *str, const char *last); +void StrTrimInPlace(char *str); /** * Check if a string buffer is empty. diff --git a/src/strings.cpp b/src/strings.cpp index 267405a6a0..44d2e0ae97 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -2058,6 +2058,15 @@ bool LanguagePackHeader::IsValid() const StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator)); } +/** + * Check whether a translation is sufficiently finished to offer it to the public. + */ +bool LanguagePackHeader::IsReasonablyFinished() const +{ + /* "Less than 25% missing" is "sufficiently finished". */ + return 4 * this->missing < LANGUAGE_TOTAL_STRINGS; +} + /** * Read a particular language. * @param lang The metadata about the language. @@ -2312,6 +2321,10 @@ void InitializeLanguagePacks() } if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng; + + /* Only auto-pick finished translations */ + if (!lng.IsReasonablyFinished()) continue; + if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng; if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng; } diff --git a/src/subsidy.cpp b/src/subsidy.cpp index ff420455d8..2668a62bd7 100644 --- a/src/subsidy.cpp +++ b/src/subsidy.cpp @@ -50,14 +50,14 @@ void Subsidy::AwardTo(CompanyID company) char *cn = stredup(company_name); /* Add a news item */ - Pair reftype = SetupSubsidyDecodeParam(this, false); + std::pair reftype = SetupSubsidyDecodeParam(this, false); InjectDParam(1); SetDParamStr(0, cn); AddNewsItem( STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, NT_SUBSIDIES, NF_NORMAL, - (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst, + reftype.first, this->src, reftype.second, this->dst, cn ); AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index)); @@ -72,7 +72,7 @@ void Subsidy::AwardTo(CompanyID company) * @param mode Unit of cargo used, \c true means general name, \c false means singular form. * @return Reference of the subsidy in the news system. */ -Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) +std::pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) { NewsReferenceType reftype1 = NR_NONE; NewsReferenceType reftype2 = NR_NONE; @@ -107,10 +107,7 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) } SetDParam(5, s->dst); - Pair p; - p.a = reftype1; - p.b = reftype2; - return p; + return std::pair(reftype1, reftype2); } /** @@ -219,8 +216,8 @@ void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType ds s->remaining = SUBSIDY_OFFER_MONTHS; s->awarded = INVALID_COMPANY; - Pair reftype = SetupSubsidyDecodeParam(s, false); - AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); + std::pair reftype = SetupSubsidyDecodeParam(s, false); + AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC); SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST); AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index)); @@ -494,14 +491,14 @@ void SubsidyMonthlyLoop() for (Subsidy *s : Subsidy::Iterate()) { if (--s->remaining == 0) { if (!s->IsAwarded()) { - Pair reftype = SetupSubsidyDecodeParam(s, true); - AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); + std::pair reftype = SetupSubsidyDecodeParam(s, true); + AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index)); } else { if (s->awarded == _local_company) { - Pair reftype = SetupSubsidyDecodeParam(s, true); - AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); + std::pair reftype = SetupSubsidyDecodeParam(s, true); + AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); } AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyExpired(s->index)); diff --git a/src/subsidy_func.h b/src/subsidy_func.h index 4889ead249..cc63577d33 100644 --- a/src/subsidy_func.h +++ b/src/subsidy_func.h @@ -14,8 +14,9 @@ #include "station_type.h" #include "company_type.h" #include "cargo_type.h" +#include "news_type.h" -Pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode); +std::pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode); void DeleteSubsidyWith(SourceType type, SourceID index); bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st); void RebuildSubsidisedSourceAndDestinationCache(); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 40a3ff39ae..ac60b87b8c 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -3458,7 +3458,7 @@ public: break; case WID_TRSL_LIST_SLOTS: { // Matrix Slot - uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height); + uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0); if (id_s >= this->slots.size()) return; this->slot_sel = this->vli.index = this->slots[id_s]->index; @@ -3526,7 +3526,7 @@ public: this->slot_over = INVALID_GROUP; this->SetDirty(); - uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height); + uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0); if (id_s >= this->slots.size()) return; // click out of list bound if (_ctrl_pressed) { @@ -3627,7 +3627,7 @@ public: break; case WID_TRSL_LIST_SLOTS: { // ... the list of slots. - uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height); + uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0); if (id_s < this->slots.size()) new_slot_over = this->slots[id_s]->index; break; } @@ -3959,7 +3959,7 @@ public: { switch (widget) { case WID_TRCL_LIST_COUNTERS: { // Matrix - uint id_s = this->sb->GetScrolledRowFromWidget(pt.y, this, WID_TRCL_LIST_COUNTERS, 0, this->tiny_step_height); + uint id_s = this->sb->GetScrolledRowFromWidget(pt.y, this, WID_TRCL_LIST_COUNTERS, 0); if (id_s >= this->ctrs.size()) return; this->selected = this->ctrs[id_s]->index; diff --git a/src/video/cocoa/cocoa_ogl.mm b/src/video/cocoa/cocoa_ogl.mm index f8c2e97e0d..96c3ea1bfe 100644 --- a/src/video/cocoa/cocoa_ogl.mm +++ b/src/video/cocoa/cocoa_ogl.mm @@ -254,7 +254,7 @@ const char *VideoDriver_CocoaOpenGL::AllocateContext(bool allow_software) CGLSetCurrentContext(this->gl_context); - return OpenGLBackend::Create(&GetOGLProcAddressCallback); + return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize()); } NSView *VideoDriver_CocoaOpenGL::AllocateDrawView() diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index bb509bcd74..1eab9b10e3 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -464,16 +464,17 @@ void SetupDebugOutput() /** * Create and initialize the singleton back-end class. * @param get_proc Callback to get an OpenGL function from the OS driver. + * @param screen_res Current display resolution. * @return nullptr on success, error message otherwise. */ -/* static */ const char *OpenGLBackend::Create(GetOGLProcAddressProc get_proc) +/* static */ const char *OpenGLBackend::Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res) { if (OpenGLBackend::instance != nullptr) OpenGLBackend::Destroy(); GetOGLProcAddress = get_proc; OpenGLBackend::instance = new OpenGLBackend(); - return OpenGLBackend::instance->Init(); + return OpenGLBackend::instance->Init(screen_res); } /** @@ -521,9 +522,10 @@ OpenGLBackend::~OpenGLBackend() /** * Check for the needed OpenGL functionality and allocate all resources. + * @param screen_res Current display resolution. * @return Error string or nullptr if successful. */ -const char *OpenGLBackend::Init() +const char *OpenGLBackend::Init(const Dimension &screen_res) { if (!BindBasicInfoProcs()) return "OpenGL not supported"; @@ -546,6 +548,12 @@ const char *OpenGLBackend::Init() _gl_major_ver = atoi(ver); _gl_minor_ver = minor != nullptr ? atoi(minor + 1) : 0; +#ifdef _WIN32 + /* Old drivers on Windows (especially if made by Intel) seem to be + * unstable, so cull the oldest stuff here. */ + if (!IsOpenGLVersionAtLeast(3, 2)) return "Need at least OpenGL version 3.2 on Windows"; +#endif + if (!BindBasicOpenGLProcs()) return "Failed to bind basic OpenGL functions."; SetupDebugOutput(); @@ -581,6 +589,11 @@ const char *OpenGLBackend::Init() } if (this->persistent_mapping_supported) DEBUG(driver, 3, "OpenGL: Using persistent buffer mapping"); + /* Check maximum texture size against screen resolution. */ + GLint max_tex_size = 0; + _glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); + if (std::max(screen_res.width, screen_res.height) > (uint)max_tex_size) return "Max supported texture size is too small"; + /* Check available texture units. */ GLint max_tex_units = 0; _glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_tex_units); diff --git a/src/video/opengl.h b/src/video/opengl.h index b0318f9882..e5cd749f2e 100644 --- a/src/video/opengl.h +++ b/src/video/opengl.h @@ -74,7 +74,7 @@ private: OpenGLBackend(); ~OpenGLBackend(); - const char *Init(); + const char *Init(const Dimension &screen_res); bool InitShaders(); void InternalClearCursorCache(); @@ -87,7 +87,7 @@ public: { return OpenGLBackend::instance; } - static const char *Create(GetOGLProcAddressProc get_proc); + static const char *Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res); static void Destroy(); void PrepareContext(); diff --git a/src/video/sdl2_opengl_v.cpp b/src/video/sdl2_opengl_v.cpp index 1e86abad64..7dcecb5ce3 100644 --- a/src/video/sdl2_opengl_v.cpp +++ b/src/video/sdl2_opengl_v.cpp @@ -121,7 +121,7 @@ const char *VideoDriver_SDL_OpenGL::AllocateContext() ToggleVsync(_video_vsync); - return OpenGLBackend::Create(&GetOGLProcAddressCallback); + return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize()); } void VideoDriver_SDL_OpenGL::PopulateSystemSprites() diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 5711d9d6b9..69e7ba887f 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1368,14 +1368,22 @@ const char *VideoDriver_Win32OpenGL::AllocateContext() /* Create OpenGL device context. Try to get an 3.2+ context if possible. */ if (_wglCreateContextAttribsARB != nullptr) { + /* Try for OpenGL 4.5 first. */ int attribs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, 3, - WGL_CONTEXT_MINOR_VERSION_ARB, 2, + WGL_CONTEXT_MAJOR_VERSION_ARB, 4, + WGL_CONTEXT_MINOR_VERSION_ARB, 5, WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0, _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, // Terminate list if WGL_ARB_create_context_profile isn't supported. 0 }; rc = _wglCreateContextAttribsARB(this->dc, nullptr, attribs); + + if (rc == nullptr) { + /* Try again for a 3.2 context. */ + attribs[1] = 3; + attribs[3] = 2; + rc = _wglCreateContextAttribsARB(this->dc, nullptr, attribs); + } } if (rc == nullptr) { @@ -1388,7 +1396,7 @@ const char *VideoDriver_Win32OpenGL::AllocateContext() this->ToggleVsync(_video_vsync); this->gl_rc = rc; - return OpenGLBackend::Create(&GetOGLProcAddressCallback); + return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize()); } bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen) diff --git a/src/widget.cpp b/src/widget.cpp index 849e542846..d3f3227e61 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -24,6 +24,33 @@ #include "safeguards.h" +/** + * Calculate x and y coordinates for an aligned object within a window. + * @param r Rectangle of the widget to be drawn in. + * @param d Dimension of the object to be drawn. + * @param align Alignment of the object. + * @return A point containing the position at which to draw. + */ +static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align) +{ + Point p; + /* In case we have a RTL language we swap the alignment. */ + if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT; + switch (align & SA_HOR_MASK) { + case SA_LEFT: p.x = r.left; break; + case SA_HOR_CENTER: p.x = CenterBounds(r.left, r.right, d.width); break; + case SA_RIGHT: p.x = r.right - d.width; break; + default: NOT_REACHED(); + } + switch (align & SA_VERT_MASK) { + case SA_TOP: p.y = r.top; break; + case SA_VERT_CENTER: p.y = CenterBounds(r.top, r.bottom, d.height); break; + case SA_BOTTOM: p.y = r.bottom - d.height; break; + default: NOT_REACHED(); + } + return p; +} + /** * Compute the vertical position of the draggable part of scrollbar * @param sb Scrollbar list data @@ -212,15 +239,17 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra * @param colour Colour of the button. * @param clicked Button is lowered. * @param img Sprite to draw. + * @param align Alignment of the sprite. */ -static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img) +static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align) { assert(img != 0); DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE); if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2. Dimension d = GetSpriteSize(img); - DrawSprite(img, PAL_NONE, CenterBounds(r.left, r.right, d.width) + clicked, CenterBounds(r.top, r.bottom, d.height) + clicked); + Point p = GetAlignedPosition(r, d, align); + DrawSprite(img, PAL_NONE, p.x + clicked, p.y + clicked); } /** @@ -228,15 +257,17 @@ static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colo * @param r Rectangle of the label background. * @param type Widget type (#WWT_TEXTBTN, #WWT_TEXTBTN_2, or #WWT_LABEL). * @param clicked Label is rendered lowered. + * @param colour Colour of the text. * @param str Text to draw. + * @param align Alignment of the text. */ -static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, StringID str) +static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, TextColour colour, StringID str, StringAlignment align) { if (str == STR_NULL) return; if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++; Dimension d = GetStringBoundingBox(str); - int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered - DrawString(r.left + clicked, r.right + clicked, r.top + offset + clicked, str, TC_FROMSTRING, SA_HOR_CENTER); + Point p = GetAlignedPosition(r, d, align); + DrawString(r.left + clicked, r.right + clicked, p.y + clicked, str, colour, align); } /** @@ -244,24 +275,27 @@ static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, Strin * @param r Rectangle of the background. * @param colour Colour of the text. * @param str Text to draw. + * @param align Alignment of the text. */ -static inline void DrawText(const Rect &r, TextColour colour, StringID str) +static inline void DrawText(const Rect &r, TextColour colour, StringID str, StringAlignment align) { Dimension d = GetStringBoundingBox(str); - int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered - if (str != STR_NULL) DrawString(r.left, r.right, r.top + offset, str, colour); + Point p = GetAlignedPosition(r, d, align); + if (str != STR_NULL) DrawString(r.left, r.right, p.y, str, colour, align); } /** * Draw an inset widget. - * @param r Rectangle of the background. - * @param colour Colour of the inset. - * @param str Text to draw. + * @param r Rectangle of the background. + * @param colour Colour of the inset. + * @param text_colour Colour of the text. + * @param str Text to draw. + * @param align Alignment of the text. */ -static inline void DrawInset(const Rect &r, Colours colour, StringID str) +static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align) { DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED); - if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str); + if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str, text_colour, align); } /** @@ -402,15 +436,17 @@ static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool l /** * Draw a frame widget. - * @param r Rectangle of the frame. - * @param colour Colour of the frame. - * @param str Text of the frame. + * @param r Rectangle of the frame. + * @param colour Colour of the frame. + * @param text_colour Colour of the text. + * @param str Text of the frame. + * @param align Alignment of the text in the frame. */ -static inline void DrawFrame(const Rect &r, Colours colour, StringID str) +static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align) { int x2 = r.left; // by default the left side is the left side of the widget - if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str); + if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str, text_colour, align); int c1 = _colour_gradient[colour][3]; int c2 = _colour_gradient[colour][7]; @@ -458,7 +494,7 @@ static inline void DrawFrame(const Rect &r, Colours colour, StringID str) */ static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE); + DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER); } /** @@ -469,7 +505,7 @@ static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked) */ static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN); + DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER); } /** @@ -480,7 +516,7 @@ static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked) */ static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE); + DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER); } /** @@ -491,7 +527,7 @@ static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked) */ static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG); + DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER); } /** @@ -530,12 +566,14 @@ static inline void DrawCloseBox(const Rect &r, Colours colour) /** * Draw a caption bar. - * @param r Rectangle of the bar. - * @param colour Colour of the window. - * @param owner 'Owner' of the window. - * @param str Text to draw in the bar. + * @param r Rectangle of the bar. + * @param colour Colour of the window. + * @param owner 'Owner' of the window. + * @param text_colour Colour of the text. + * @param str Text to draw in the bar. + * @param align Alignment of the text. */ -void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str) +void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align) { bool company_owned = owner < MAX_COMPANIES; @@ -548,8 +586,8 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str) if (str != STR_NULL) { Dimension d = GetStringBoundingBox(str); - int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered - DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, r.top + offset, str, TC_FROMSTRING, SA_HOR_CENTER); + Point p = GetAlignedPosition(r, d, align); + DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, p.y, str, text_colour, align); } } @@ -560,10 +598,11 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str) * @param clicked_button The button-part is lowered. * @param clicked_dropdown The drop-down part is lowered. * @param str Text of the button. + * @param align Alignment of the text within the dropdown. * * @note Magic constants are also used in #NWidgetLeaf::ButtonHit. */ -static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str) +static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align) { int text_offset = std::max(0, ((int)(r.bottom - r.top + 1) - FONT_HEIGHT_NORMAL) / 2); // Offset for rendering the text vertically centered @@ -575,12 +614,12 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE); DrawFrameRect(r.right + 1 - dd_width, r.top, r.right, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE); DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.right - (dd_width - 2) + clicked_dropdown, r.top + image_offset + clicked_dropdown); - if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - dd_width - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK); + if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - dd_width - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK, align); } else { DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE); DrawFrameRect(r.left, r.top, r.left + dd_width - 1, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE); DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.left + 1 + clicked_dropdown, r.top + image_offset + clicked_dropdown); - if (str != STR_NULL) DrawString(r.left + dd_width + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK); + if (str != STR_NULL) DrawString(r.left + dd_width + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK, align); } } @@ -590,10 +629,11 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke * @param colour Background colour of the widget. * @param clicked The widget is lowered. * @param str Text of the button. + * @param align Alignment of the text. */ -static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str) +static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str, StringAlignment align) { - DrawButtonDropdown(r, colour, false, clicked, str); + DrawButtonDropdown(r, colour, false, clicked, str, align); } /** @@ -871,6 +911,8 @@ NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, uint fill_x, uint fill_y this->widget_data = widget_data; this->tool_tip = tool_tip; this->scrollbar_index = -1; + this->text_colour = TC_FROMSTRING; + this->align = SA_CENTER; } /** @@ -894,6 +936,15 @@ void NWidgetCore::SetDataTip(uint32 widget_data, StringID tool_tip) this->tool_tip = tool_tip; } +/** + * Set the text colour of the nested widget. + * @param colour TextColour to use. + */ +void NWidgetCore::SetTextColour(TextColour colour) +{ + this->text_colour = colour; +} + /** * Set the tool tip of the nested widget. * @param tool_tip Tool tip string to use. @@ -903,6 +954,15 @@ void NWidgetCore::SetToolTip(StringID tool_tip) this->tool_tip = tool_tip; } +/** + * Set the text/image alignment of the nested widget. + * @param align Alignment to use. + */ +void NWidgetCore::SetAlignment(StringAlignment align) +{ + this->align = align; +} + void NWidgetCore::FillNestedArray(NWidgetBase **array, uint length) { if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this; @@ -1774,6 +1834,7 @@ NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, int index, N assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME); if (index >= 0) this->SetIndex(index); this->child = child; + this->SetAlignment(SA_TOP | SA_LEFT); } NWidgetBackground::~NWidgetBackground() @@ -1910,12 +1971,12 @@ void NWidgetBackground::Draw(const Window *w) case WWT_FRAME: if (this->index >= 0) w->SetStringParameters(this->index); - DrawFrame(r, this->colour, this->widget_data); + DrawFrame(r, this->colour, this->text_colour, this->widget_data, this->align); break; case WWT_INSET: if (this->index >= 0) w->SetStringParameters(this->index); - DrawInset(r, this->colour, this->widget_data); + DrawInset(r, this->colour, this->text_colour, this->widget_data, this->align); break; default: @@ -2030,16 +2091,75 @@ void NWidgetViewport::UpdateViewportCoordinates(Window *w) * @param w The window the click was in. * @param widget Widget number of the widget clicked in. * @param padding Amount of empty space between the widget edge and the top of the first row. Default value is \c 0. - * @param line_height Height of a single row. A negative value means using the vertical resize step of the widget. * @return Row number clicked at. If clicked at a wrong position, #INT_MAX is returned. */ -int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding, int line_height) const +int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding) const { - uint pos = w->GetRowFromWidget(clickpos, widget, padding, line_height); + uint pos = w->GetRowFromWidget(clickpos, widget, padding, -1); if (pos != INT_MAX) pos += this->GetPosition(); return (pos >= this->GetCount()) ? INT_MAX : pos; } +/** + * Update the given list position as if it were on this scroll bar when the given keycode was pressed. + * This does not update the actual position of this scroll bar, that is left to the caller. It does, + * however use the capacity and count of the scroll bar for the bounds and amount to scroll. + * + * When the count is 0 or the return is ES_NOT_HANDLED, then the position is not updated. + * With WKC_UP and WKC_DOWN the position goes one up or down respectively. + * With WKC_PAGEUP and WKC_PAGEDOWN the position goes one capacity up or down respectively. + * With WKC_HOME the first position is selected and with WKC_END the last position is selected. + * This function ensures that pos is in the range [0..count). + * @param list_position The current position in the list. + * @param key_code The pressed key code. + * @return ES_NOT_HANDLED when another key than the 6 specific keys was pressed, otherwise ES_HANDLED. + */ +EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16 keycode) const +{ + int new_pos = list_position; + switch (keycode) { + case WKC_UP: + /* scroll up by one */ + new_pos--; + break; + + case WKC_DOWN: + /* scroll down by one */ + new_pos++; + break; + + case WKC_PAGEUP: + /* scroll up a page */ + new_pos -= this->GetCapacity(); + break; + + case WKC_PAGEDOWN: + /* scroll down a page */ + new_pos += this->GetCapacity(); + break; + + case WKC_HOME: + /* jump to beginning */ + new_pos = 0; + break; + + case WKC_END: + /* jump to end */ + new_pos = this->GetCount() - 1; + break; + + default: + return ES_NOT_HANDLED; + } + + /* If there are no elements, there is nothing to scroll/update. */ + if (this->GetCount() != 0) { + list_position = Clamp(new_pos, 0, this->GetCount() - 1); + } + return ES_HANDLED; +} + + /** * Set capacity of visible elements from the size and resize properties of a widget. * @param w Window. @@ -2202,6 +2322,11 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_EMPTY: break; + case WWT_TEXT: + this->SetFill(0, 0); + this->SetAlignment(SA_LEFT | SA_VERT_CENTER); + break; + case WWT_PUSHBTN: case WWT_IMGBTN: case WWT_PUSHIMGBTN: @@ -2210,7 +2335,6 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_PUSHTXTBTN: case WWT_TEXTBTN_2: case WWT_LABEL: - case WWT_TEXT: case WWT_MATRIX: case NWID_BUTTON_DROPDOWN: case NWID_PUSHBUTTON_DROPDOWN: @@ -2269,6 +2393,7 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_DROPDOWN: this->SetFill(0, 0); this->min_y = WD_DROPDOWN_HEIGHT; + this->SetAlignment(SA_TOP | SA_LEFT); break; default: @@ -2506,7 +2631,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_IMGBTN: case WWT_PUSHIMGBTN: case WWT_IMGBTN_2: - DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data); + DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data, this->align); break; case WWT_TEXTBTN: @@ -2514,7 +2639,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_TEXTBTN_2: if (this->index >= 0) w->SetStringParameters(this->index); DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE); - DrawLabel(r, this->type, clicked, this->widget_data); + DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align); break; case WWT_ARROWBTN: @@ -2527,18 +2652,18 @@ void NWidgetLeaf::Draw(const Window *w) case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break; default: NOT_REACHED(); } - DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite); + DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align); break; } case WWT_LABEL: if (this->index >= 0) w->SetStringParameters(this->index); - DrawLabel(r, this->type, clicked, this->widget_data); + DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align); break; case WWT_TEXT: if (this->index >= 0) w->SetStringParameters(this->index); - DrawText(r, (TextColour)this->colour, this->widget_data); + DrawText(r, this->text_colour, this->widget_data, this->align); break; case WWT_MATRIX: @@ -2553,7 +2678,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_CAPTION: if (this->index >= 0) w->SetStringParameters(this->index); - DrawCaption(r, this->colour, w->owner, this->widget_data); + DrawCaption(r, this->colour, w->owner, this->text_colour, this->widget_data, this->align); break; case WWT_SHADEBOX: @@ -2586,13 +2711,13 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_DROPDOWN: if (this->index >= 0) w->SetStringParameters(this->index); - DrawDropdown(r, this->colour, clicked, this->widget_data); + DrawDropdown(r, this->colour, clicked, this->widget_data, this->align); break; case NWID_BUTTON_DROPDOWN: case NWID_PUSHBUTTON_DROPDOWN: if (this->index >= 0) w->SetStringParameters(this->index); - DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data); + DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data, this->align); break; default: @@ -2731,6 +2856,22 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, break; } + case WPT_TEXTCOLOUR: { + NWidgetCore *nwc = dynamic_cast(*dest); + if (nwc != nullptr) { + nwc->SetTextColour(parts->u.colour.colour); + } + break; + } + + case WPT_ALIGNMENT: { + NWidgetCore *nwc = dynamic_cast(*dest); + if (nwc != nullptr) { + nwc->SetAlignment(parts->u.align.align); + } + break; + } + case WPT_FILL: { NWidgetResizeBase *nwrb = dynamic_cast(*dest); if (nwrb != nullptr) nwrb->SetFill(parts->u.xy.x, parts->u.xy.y); diff --git a/src/widget_type.h b/src/widget_type.h index 89dd9f93a0..1144800383 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -92,6 +92,8 @@ enum WidgetType : uint8 { WPT_DATATIP, ///< Widget part for specifying data and tooltip. WPT_PADDING, ///< Widget part for specifying a padding. WPT_PIPSPACE, ///< Widget part for specifying pre/inter/post space for containers. + WPT_TEXTCOLOUR, ///< Widget part for specifying text colour. + WPT_ALIGNMENT, ///< Widget part for specifying text/image alignment. WPT_ENDCONTAINER, ///< Widget part to denote end of a container. WPT_FUNCTION, ///< Widget part for calling a user function. WPT_SCROLLBAR, ///< Widget part for attaching a scrollbar. @@ -316,6 +318,8 @@ public: void SetIndex(int index); void SetDataTip(uint32 widget_data, StringID tool_tip); void SetToolTip(StringID tool_tip); + void SetTextColour(TextColour colour); + void SetAlignment(StringAlignment align); inline void SetLowered(bool lowered); inline bool IsLowered() const; @@ -336,6 +340,8 @@ public: StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips int scrollbar_index; ///< Index of an attached scrollbar. TextColour highlight_colour; ///< Colour of highlight. + TextColour text_colour; ///< Colour of text within widget. + StringAlignment align; ///< Alignment of text/image within widget. }; /** @@ -774,7 +780,8 @@ public: } } - int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0, int line_height = -1) const; + int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0) const; + EventState UpdateListPositionOnKeyPress(int &list_position, uint16 keycode) const; }; /** @@ -930,6 +937,22 @@ struct NWidgetPartTextLines { FontSize size; ///< Font size of text lines. }; +/** + * Widget part for storing text colour. + * @ingroup NestedWidgetParts + */ +struct NWidgetPartTextColour { + TextColour colour; ///< TextColour for DrawString. +}; + +/** + * Widget part for setting text/image alignment within a widget. + * @ingroup NestedWidgetParts + */ +struct NWidgetPartAlignment { + StringAlignment align; ///< Alignment of text/image. +}; + /** * Pointer to function returning a nested widget. * @param biggest_index Pointer to storage for collecting the biggest index used in the nested widget. @@ -951,6 +974,8 @@ struct NWidgetPart { NWidgetPartPaddings padding; ///< Part with paddings. NWidgetPartPIP pip; ///< Part with pre/inter/post spaces. NWidgetPartTextLines text_lines; ///< Part with text line data. + NWidgetPartTextColour colour; ///< Part with text colour data. + NWidgetPartAlignment align; ///< Part with internal alignment. NWidgetFunctionType *func_ptr; ///< Part with a function call. NWidContainerFlags cont_flags; ///< Part with container flags. } u; @@ -1009,6 +1034,36 @@ static inline NWidgetPart SetMinimalTextLines(uint8 lines, uint8 spacing, FontSi return part; } +/** + * Widget part function for setting the text colour. + * @param colour Colour to draw string within widget. + * @ingroup NestedWidgetParts + */ +static inline NWidgetPart SetTextColour(TextColour colour) +{ + NWidgetPart part; + + part.type = WPT_TEXTCOLOUR; + part.u.colour.colour = colour; + + return part; +} + +/** + * Widget part function for setting the alignment of text/images. + * @param align Alignment of text/image within widget. + * @ingroup NestedWidgetParts + */ +static inline NWidgetPart SetAlignment(StringAlignment align) +{ + NWidgetPart part; + + part.type = WPT_ALIGNMENT; + part.u.align.align = align; + + return part; +} + /** * Widget part function for setting filling. * @param fill_x Horizontal filling step from minimal size. diff --git a/src/window_gui.h b/src/window_gui.h index bcdd9221bb..c54a0f4c3b 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -140,7 +140,7 @@ enum WidgetDrawDistances { /* widget.cpp */ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags); -void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str); +void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align); /* window.cpp */ extern WindowBase *_z_front_window;