diff --git a/bin/ai/compat_1.10.nut b/bin/ai/compat_1.10.nut index 3081fb58e8..2baaddb836 100644 --- a/bin/ai/compat_1.10.nut +++ b/bin/ai/compat_1.10.nut @@ -4,3 +4,5 @@ * 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 . */ + +AILog.Info("1.10 API compatibility in effect."); diff --git a/bin/ai/compat_1.11.nut b/bin/ai/compat_1.11.nut new file mode 100644 index 0000000000..3081fb58e8 --- /dev/null +++ b/bin/ai/compat_1.11.nut @@ -0,0 +1,6 @@ +/* + * 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 . + */ diff --git a/bin/ai/regression/regression_info.nut b/bin/ai/regression/regression_info.nut index 020b186faf..758754cfe7 100644 --- a/bin/ai/regression/regression_info.nut +++ b/bin/ai/regression/regression_info.nut @@ -4,7 +4,7 @@ class Regression extends AIInfo { function GetShortName() { return "REGR"; } function GetDescription() { return "This runs regression-tests on some commands. On the same map the result should always be the same."; } function GetVersion() { return 1; } - function GetAPIVersion() { return "1.10"; } + function GetAPIVersion() { return "1.11"; } function GetDate() { return "2007-03-18"; } function CreateInstance() { return "Regression"; } } diff --git a/bin/game/compat_1.10.nut b/bin/game/compat_1.10.nut index 3081fb58e8..1c85766c0f 100644 --- a/bin/game/compat_1.10.nut +++ b/bin/game/compat_1.10.nut @@ -4,3 +4,5 @@ * 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 . */ + +GSLog.Info("1.10 API compatibility in effect."); diff --git a/bin/game/compat_1.11.nut b/bin/game/compat_1.11.nut new file mode 100644 index 0000000000..3081fb58e8 --- /dev/null +++ b/bin/game/compat_1.11.nut @@ -0,0 +1,6 @@ +/* + * 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 . + */ diff --git a/os/os2/installer/make_installer.cmd b/os/os2/installer/make_installer.cmd index 3addeca1ae..885b2dc623 100644 --- a/os/os2/installer/make_installer.cmd +++ b/os/os2/installer/make_installer.cmd @@ -1,6 +1,6 @@ @echo off -set OPENTTD_VERSION=1.10.0 +set OPENTTD_VERSION=1.11.0 set OPENSFX_VERSION=0.8.0 set NOSOUND_VERSION=0.8.0 set OPENGFX_VERSION=1.2.0 diff --git a/os/rpm/openttd.spec b/os/rpm/openttd.spec index 340a9886b9..134caad38e 100644 --- a/os/rpm/openttd.spec +++ b/os/rpm/openttd.spec @@ -17,9 +17,9 @@ # Name: openttd -Version: 1.10.beta2 +Version: 1.11.beta1 Release: 0 -%define srcver 1.10.0-beta2 +%define srcver 1.11.0-beta1 Summary: An open source reimplementation of Chris Sawyer's Transport Tycoon Deluxe License: GPL-2.0 Group: Amusements/Games/Strategy/Other diff --git a/os/windows/installer/install.nsi b/os/windows/installer/install.nsi index 2561b76d55..b6837c559e 100644 --- a/os/windows/installer/install.nsi +++ b/os/windows/installer/install.nsi @@ -1,9 +1,9 @@ # Version numbers to update !define APPV_MAJOR 1 -!define APPV_MINOR 10 +!define APPV_MINOR 11 !define APPV_MAINT 0 -!define APPV_BUILD 1 -!define APPV_EXTRA "-beta2" +!define APPV_BUILD 0 +!define APPV_EXTRA "-beta1" !define APPNAME "OpenTTD" ; Define application name !define APPVERSION "${APPV_MAJOR}.${APPV_MINOR}.${APPV_MAINT}${APPV_EXTRA}" ; Define application version diff --git a/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp b/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp index 540975fb5c..a4f4e21c33 100644 --- a/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp +++ b/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp @@ -38,7 +38,7 @@ void sqstd_printcallstack(HSQUIRRELVM v) src = si.source; } } - pf(v,"*FUNCTION [%s()] %s line [%d]\n",fn,src,si.line); + pf(v,"*FUNCTION [%s()] %s line [" OTTD_PRINTF64 "]\n",fn,src,si.line); level++; } level=0; @@ -56,7 +56,7 @@ void sqstd_printcallstack(HSQUIRRELVM v) break; case OT_INTEGER: sq_getinteger(v,-1,&i); - pf(v,"[%s] %d\n",name,i); + pf(v,"[%s] " OTTD_PRINTF64 "\n",name,i); break; case OT_FLOAT: sq_getfloat(v,-1,&f); @@ -134,7 +134,7 @@ void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSourc { SQPRINTFUNCTION pf = sq_getprintfunc(v); if(pf) { - pf(v,"%s line = (%d) column = (%d) : error %s\n",sSource,line,column,sErr); + pf(v,"%s line = (" OTTD_PRINTF64 ") column = (" OTTD_PRINTF64 ") : error %s\n",sSource,line,column,sErr); } } diff --git a/src/3rdparty/squirrel/squirrel/sqbaselib.cpp b/src/3rdparty/squirrel/squirrel/sqbaselib.cpp index 86a1b11858..e5de01a635 100644 --- a/src/3rdparty/squirrel/squirrel/sqbaselib.cpp +++ b/src/3rdparty/squirrel/squirrel/sqbaselib.cpp @@ -219,7 +219,7 @@ static SQInteger base_array(HSQUIRRELVM v) SQInteger nInitialSize = tointeger(stack_get(v,2)); SQInteger ret = 1; if (nInitialSize < 0) { - v->Raise_Error("can't create/resize array with/to size %d", nInitialSize); + v->Raise_Error("can't create/resize array with/to size " OTTD_PRINTF64, nInitialSize); nInitialSize = 0; ret = -1; } diff --git a/src/3rdparty/squirrel/squirrel/sqdebug.cpp b/src/3rdparty/squirrel/squirrel/sqdebug.cpp index 2f24e83b66..44f767c297 100644 --- a/src/3rdparty/squirrel/squirrel/sqdebug.cpp +++ b/src/3rdparty/squirrel/squirrel/sqdebug.cpp @@ -122,5 +122,5 @@ void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger ty StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes); } } - Raise_Error("parameter %d has an invalid type '%s' ; expected: '%s'", nparam, IdType2Name((SQObjectType)type), _stringval(exptypes)); + Raise_Error("parameter " OTTD_PRINTF64 " has an invalid type '%s' ; expected: '%s'", nparam, IdType2Name((SQObjectType)type), _stringval(exptypes)); } diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index a684e4a350..a0552628cd 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -179,6 +179,7 @@ struct AIListWindow : public Window { InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_AI); InvalidateWindowClassesData(WC_AI_SETTINGS); DeleteWindowByClass(WC_QUERY_STRING); + InvalidateWindowClassesData(WC_TEXTFILE); } void OnClick(Point pt, int widget, int click_count) override @@ -640,15 +641,24 @@ struct ScriptTextfileWindow : public TextfileWindow { ScriptTextfileWindow(TextfileType file_type, CompanyID slot) : TextfileWindow(file_type), slot(slot) { - const char *textfile = GetConfig(slot)->GetTextfile(file_type, slot); - this->LoadTextfile(textfile, (slot == OWNER_DEITY) ? GAME_DIR : AI_DIR); + this->OnInvalidateData(); } void SetStringParameters(int widget) const override { if (widget == WID_TF_CAPTION) { SetDParam(0, (slot == OWNER_DEITY) ? STR_CONTENT_TYPE_GAME_SCRIPT : STR_CONTENT_TYPE_AI); - SetDParamStr(1, GetConfig(slot)->GetName()); + SetDParamStr(1, GetConfig(slot)->GetInfo()->GetName()); + } + } + + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + const char *textfile = GetConfig(slot)->GetTextfile(file_type, slot); + if (textfile == nullptr) { + delete this; + } else { + this->LoadTextfile(textfile, (slot == OWNER_DEITY) ? GAME_DIR : AI_DIR); } } }; diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp index 2d80003f6b..6e1ef6cecd 100644 --- a/src/ai/ai_info.cpp +++ b/src/ai/ai_info.cpp @@ -27,7 +27,8 @@ static bool CheckAPIVersion(const char *api_version) return strcmp(api_version, "0.7") == 0 || strcmp(api_version, "1.0") == 0 || strcmp(api_version, "1.1") == 0 || strcmp(api_version, "1.2") == 0 || strcmp(api_version, "1.3") == 0 || strcmp(api_version, "1.4") == 0 || strcmp(api_version, "1.5") == 0 || strcmp(api_version, "1.6") == 0 || strcmp(api_version, "1.7") == 0 || - strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0; + strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0 || + strcmp(api_version, "1.11") == 0; } #if defined(_WIN32) diff --git a/src/console.cpp b/src/console.cpp index 3155b08c63..8e1dce0135 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -21,6 +21,7 @@ #include "safeguards.h" static const uint ICON_TOKEN_COUNT = 20; ///< Maximum number of tokens in one command +static const uint ICON_MAX_RECURSE = 10; ///< Maximum number of recursion /* console parser */ IConsoleCmd *_iconsole_cmds; ///< list of registered commands @@ -317,13 +318,18 @@ IConsoleAlias *IConsoleAliasGet(const char *name) * @param tokencount the number of parameters passed * @param *tokens are the parameters given to the original command (0 is the first param) */ -static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT]) +static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT], const uint recurse_count) { char alias_buffer[ICON_MAX_STREAMSIZE] = { '\0' }; char *alias_stream = alias_buffer; DEBUG(console, 6, "Requested command is an alias; parsing..."); + if (recurse_count > ICON_MAX_RECURSE) { + IConsoleError("Too many alias expansions, recursion limit reached. Aborting"); + return; + } + for (const char *cmdptr = alias->cmdline; *cmdptr != '\0'; cmdptr++) { switch (*cmdptr) { case '\'': // ' will double for "" @@ -331,7 +337,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char break; case ';': // Cmd separator; execute previous and start new command - IConsoleCmdExec(alias_buffer); + IConsoleCmdExec(alias_buffer, recurse_count); alias_stream = alias_buffer; *alias_stream = '\0'; // Make sure the new command is terminated. @@ -391,7 +397,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char } } - IConsoleCmdExec(alias_buffer); + IConsoleCmdExec(alias_buffer, recurse_count); } /** @@ -399,7 +405,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char * individual tokens (separated by spaces), then execute it if possible * @param cmdstr string to be parsed and executed */ -void IConsoleCmdExec(const char *cmdstr) +void IConsoleCmdExec(const char *cmdstr, const uint recurse_count) { const char *cmdptr; char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE]; @@ -505,7 +511,7 @@ void IConsoleCmdExec(const char *cmdstr) t_index--; IConsoleAlias *alias = IConsoleAliasGet(tokens[0]); if (alias != nullptr) { - IConsoleAliasExec(alias, t_index, &tokens[1]); + IConsoleAliasExec(alias, t_index, &tokens[1], recurse_count + 1); return; } diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 3096322802..9475e1929b 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -526,7 +526,7 @@ DEF_CONSOLE_CMD(ConClearBuffer) * Network Core Console Commands **********************************/ -static bool ConKickOrBan(const char *argv, bool ban) +static bool ConKickOrBan(const char *argv, bool ban, const char *reason) { uint n; @@ -550,14 +550,14 @@ static bool ConKickOrBan(const char *argv, bool ban) if (!ban) { /* Kick only this client, not all clients with that IP */ - NetworkServerKickClient(client_id); + NetworkServerKickClient(client_id, reason); return true; } /* When banning, kick+ban all clients with that IP */ - n = NetworkServerKickOrBanIP(client_id, ban); + n = NetworkServerKickOrBanIP(client_id, ban, reason); } else { - n = NetworkServerKickOrBanIP(argv, ban); + n = NetworkServerKickOrBanIP(argv, ban, reason); } if (n == 0) { @@ -572,28 +572,48 @@ static bool ConKickOrBan(const char *argv, bool ban) DEF_CONSOLE_CMD(ConKick) { if (argc == 0) { - IConsoleHelp("Kick a client from a network game. Usage: 'kick '"); + IConsoleHelp("Kick a client from a network game. Usage: 'kick []'"); IConsoleHelp("For client-id's, see the command 'clients'"); return true; } - if (argc != 2) return false; + if (argc != 2 && argc != 3) return false; - return ConKickOrBan(argv[1], false); + /* No reason supplied for kicking */ + if (argc == 2) return ConKickOrBan(argv[1], false, nullptr); + + /* Reason for kicking supplied */ + size_t kick_message_length = strlen(argv[2]); + if (kick_message_length >= 255) { + IConsolePrintF(CC_ERROR, "ERROR: Maximum kick message length is 254 characters. You entered %d characters.", kick_message_length); + return false; + } else { + return ConKickOrBan(argv[1], false, argv[2]); + } } DEF_CONSOLE_CMD(ConBan) { if (argc == 0) { - IConsoleHelp("Ban a client from a network game. Usage: 'ban '"); + IConsoleHelp("Ban a client from a network game. Usage: 'ban []'"); IConsoleHelp("For client-id's, see the command 'clients'"); IConsoleHelp("If the client is no longer online, you can still ban his/her IP"); return true; } - if (argc != 2) return false; + if (argc != 2 && argc != 3) return false; - return ConKickOrBan(argv[1], true); + /* No reason supplied for kicking */ + if (argc == 2) return ConKickOrBan(argv[1], true, nullptr); + + /* Reason for kicking supplied */ + size_t kick_message_length = strlen(argv[2]); + if (kick_message_length >= 255) { + IConsolePrintF(CC_ERROR, "ERROR: Maximum kick message length is 254 characters. You entered %d characters.", kick_message_length); + return false; + } else { + return ConKickOrBan(argv[1], true, argv[2]); + } } DEF_CONSOLE_CMD(ConUnBan) diff --git a/src/console_func.h b/src/console_func.h index 1dad477b8a..6d634a4553 100644 --- a/src/console_func.h +++ b/src/console_func.h @@ -28,7 +28,7 @@ void IConsoleWarning(const char *string); void IConsoleError(const char *string); /* Parser */ -void IConsoleCmdExec(const char *cmdstr); +void IConsoleCmdExec(const char *cmdstr, const uint recurse_count = 0); bool IsValidConsoleColour(TextColour c); diff --git a/src/depot.cpp b/src/depot.cpp index 5819c3b0fd..35dcadbdb3 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -28,6 +28,8 @@ INSTANTIATE_POOL_METHODS(Depot) */ Depot::~Depot() { + free(this->name); + if (CleaningPool()) return; if (!IsDepotTile(this->xy) || GetDepotIndex(this->xy) != this->index) { diff --git a/src/game/game_info.cpp b/src/game/game_info.cpp index 91a463bed5..a039401b32 100644 --- a/src/game/game_info.cpp +++ b/src/game/game_info.cpp @@ -24,7 +24,8 @@ static bool CheckAPIVersion(const char *api_version) { return strcmp(api_version, "1.2") == 0 || strcmp(api_version, "1.3") == 0 || strcmp(api_version, "1.4") == 0 || strcmp(api_version, "1.5") == 0 || strcmp(api_version, "1.6") == 0 || strcmp(api_version, "1.7") == 0 || - strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0; + strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0 || + strcmp(api_version, "1.11") == 0; } #if defined(_WIN32) diff --git a/src/group_gui.cpp b/src/group_gui.cpp index c86c11e2cb..ee0113db93 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -1102,6 +1102,7 @@ public: } this->groups.ForceRebuild(); this->BuildGroupList(this->owner); + this->group_sb->SetCount((uint)this->groups.size()); id_g = find_index(this->groups, g); } this->group_sb->ScrollTowards(id_g); diff --git a/src/lang/czech.txt b/src/lang/czech.txt index 964375e8ed..0861cc3f45 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -1395,6 +1395,8 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL :Zobrazovat popu STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Zobrazovat městskou populaci u názvu města na mapě STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Tloušťky čar v grafech: {STRING} STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Tloušťka čáry v grafech. Tenká čára se čte přesněji, silnější je lépe viditelná a barva je snadněji rozpoznatelná. +STR_CONFIG_SETTING_SHOW_NEWGRF_NAME :Zobrazovat název NewGRF v okně nákupu vozidel: {STRING} +STR_CONFIG_SETTING_SHOW_NEWGRF_NAME_HELPTEXT :Přidá řádek do okna nákupu vozidel informující, ze které NewGRF vybrané vozidlo pochází. STR_CONFIG_SETTING_LANDSCAPE :Klima: {STRING} STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Klima určuje základy herního scénáře s rozdílnými druhy nákladu a požadavky na růst měst. Nové GRaFiky a Herní Skripty umožní ještě jemnější kontrolu @@ -1632,6 +1634,10 @@ STR_CONFIG_SETTING_NEWS_MESSAGES_FULL :Plná STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :Barevné noviny se objeví v roce: {STRING} STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :Rok od kterého budou novinová oznámeni zobrazena v barvách. Před tímto rokem jsou černobílá. STR_CONFIG_SETTING_STARTING_YEAR :Počáteční datum: {STRING} +STR_CONFIG_SETTING_ENDING_YEAR :Rok vyhodnocení: {STRING} +STR_CONFIG_SETTING_ENDING_YEAR_HELPTEXT :Rok, kdy je ve hře uzavřeno hodnocení společností. Na konci tohoto roku je zaznamenáno skóre společností a je zobrazena tabulka nejlepších společností, ale ve hře je možné pokračovat i dál.{}Pokud je nastaven rok před rokem počátku hry, tabulka s hodnocením nebude zobrazena nikdy. +STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} +STR_CONFIG_SETTING_ENDING_YEAR_ZERO :Nikdy STR_CONFIG_SETTING_SMOOTH_ECONOMY :Plynulé změny ekonomiky (více menších změn): {STRING} STR_CONFIG_SETTING_SMOOTH_ECONOMY_HELPTEXT :Pokud je zapnuto, produkce průmyslu se mění častěji ale změny jsou menší. Toto nastavení většinou nemá vliv na průmysl, který je přidaný novou grafikou STR_CONFIG_SETTING_ALLOW_SHARES :Povolit kupování podílu z ostatních společností: {STRING} @@ -3477,8 +3483,17 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL :{WHITE}{CURRENC # Industry directory STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Průmysl STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- Nic - +STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% přepraveno){BLACK} STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} +STR_INDUSTRY_DIRECTORY_ITEM_PROD1 :{ORANGE}{INDUSTRY} {STRING} +STR_INDUSTRY_DIRECTORY_ITEM_PROD2 :{ORANGE}{INDUSTRY} {STRING}, {STRING} +STR_INDUSTRY_DIRECTORY_ITEM_PROD3 :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} +STR_INDUSTRY_DIRECTORY_ITEM_PRODMORE :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} a {NUM} další... STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}Název průmyslu - pohled na něj zaměříš kliknutím na jeho jméno. Při stisknutém Ctrl otevřeš nový pohled +STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}Přijímá náklad: {SILVER}{STRING} +STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER :{BLACK}Produkuje náklad: {SILVER}{STRING} +STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES :Všechny druhy nákladu +STR_INDUSTRY_DIRECTORY_FILTER_NONE :Žádný # Industry view STR_INDUSTRY_VIEW_CAPTION :{WHITE}{INDUSTRY} @@ -4256,6 +4271,13 @@ STR_AI_LIST_ACCEPT_TOOLTIP :{BLACK}Vybrat o STR_AI_LIST_CANCEL :{BLACK}Zrušit STR_AI_LIST_CANCEL_TOOLTIP :{BLACK}Neměňte skript +STR_SCREENSHOT_CAPTION :{WHITE}Pořídit snímek obrazovky +STR_SCREENSHOT_SCREENSHOT :{BLACK}Běžný snímek obrazovky +STR_SCREENSHOT_ZOOMIN_SCREENSHOT :{BLACK}Snímek obrazovky v úplném přiblížení +STR_SCREENSHOT_DEFAULTZOOM_SCREENSHOT :{BLACK}Snímek obrazovky ve výchozím přiblížení +STR_SCREENSHOT_WORLD_SCREENSHOT :{BLACK}Snímek celé mapy +STR_SCREENSHOT_HEIGHTMAP_SCREENSHOT :{BLACK}Snímek výškové mapy +STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}Snímek (náhledové) mapy světa # AI Parameters STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} Parametry diff --git a/src/lang/english.txt b/src/lang/english.txt index 5063e30999..c52fae2259 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2402,6 +2402,7 @@ STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong pa STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}You are banned from this server STR_NETWORK_ERROR_KICKED :{WHITE}You were kicked out of the game +STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Reason: {RAW_STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}Cheating is not allowed on this server STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}You were sending too many commands to the server STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}You took too long to enter the password @@ -2465,6 +2466,7 @@ STR_NETWORK_MESSAGE_MONEY_GIVEN :*** {RAW_STRING STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION :{RAW_STRING} ({COMPANY}) STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The server closed the session STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait... +STR_NETWORK_MESSAGE_KICKED :*** {RAW_STRING} was kicked. Reason: ({RAW_STRING}) # Content downloading window STR_CONTENT_TITLE :{WHITE}Content downloading diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 7640d42b15..cc1509da7d 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -799,8 +799,8 @@ STR_NEWS_FIRST_SHIP_ARRIVAL :{BIG_FONT}{BLAC STR_NEWS_FIRST_AIRCRAFT_ARRIVAL :{BIG_FONT}{BLACK}시민들이 축하하고 있습니다 . . .{}{STATION}에 처음으로 항공기가 도착했습니다! STR_NEWS_TRAIN_CRASH :{BIG_FONT}{BLACK}열차 충돌 사고!{}충돌로 인한 폭발로 {COMMA}명의 사망자가 발생하였습니다! -STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER :{BIG_FONT}{BLACK}차량 충돌!{}열차와의 충돌로 인해 운전자가 사망했습니다! -STR_NEWS_ROAD_VEHICLE_CRASH :{BIG_FONT}{BLACK}차량 충돌!{}열차와의 충돌로 {COMMA}명이 사망했습니다! +STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER :{BIG_FONT}{BLACK}차량 충돌!{}열차 충돌로 운전자가 사망했습니다! +STR_NEWS_ROAD_VEHICLE_CRASH :{BIG_FONT}{BLACK}차량 충돌!{}열차 충돌로 {COMMA}명이 사망했습니다! STR_NEWS_AIRCRAFT_CRASH :{BIG_FONT}{BLACK}항공기 충돌사고 발생!{}{COMMA}명이 {STATION}공항에서 사망하였습니다! STR_NEWS_PLANE_CRASH_OUT_OF_FUEL :{BIG_FONT}{BLACK}항공기 추락사고 발생!{}연료 부족으로 인하여 {COMMA}명이 사망하였습니다! @@ -1739,7 +1739,7 @@ STR_CONFIG_SETTING_TOWN_LAYOUT_BETTER_ROADS :개선된 도 STR_CONFIG_SETTING_TOWN_LAYOUT_2X2_GRID :2x2 칸 STR_CONFIG_SETTING_TOWN_LAYOUT_3X3_GRID :3x3 칸 STR_CONFIG_SETTING_TOWN_LAYOUT_RANDOM :무작위 -STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :도시 스스로의 도로 건설 허용: {STRING} +STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :도시 스스로 도로를 건설하는 것을 허용: {STRING} STR_CONFIG_SETTING_ALLOW_TOWN_ROADS_HELPTEXT :도시가 성장하기 위해 도로를 건설할 수 있도록 허용합니다. 도시 당국이 스스로 도로를 만들지 못하도록 하려면 이 설정을 끄십시오. STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :도시가 회사 소유의 선로에 건널목을 만드는 것을 허용: {STRING} STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT :도시가 회사 소유의 선로에 건널목을 건설할 수 있도록 허용합니다. @@ -2369,7 +2369,7 @@ STR_COMPANY_PASSWORD_MAKE_DEFAULT_TOOLTIP :{BLACK}이 회 STR_COMPANY_VIEW_JOIN :{BLACK}참여 STR_COMPANY_VIEW_JOIN_TOOLTIP :{BLACK}이 회사로 참가해서 플레이합니다 STR_COMPANY_VIEW_PASSWORD :{BLACK}암호 -STR_COMPANY_VIEW_PASSWORD_TOOLTIP :{BLACK}다른 참가자가 이 회사로의 플레이를 하지 못하도록 암호로 보호합니다 +STR_COMPANY_VIEW_PASSWORD_TOOLTIP :{BLACK}다른 참가자가 이 회사에 참여하여 플레이하지 못 하도록 암호로 보호합니다 STR_COMPANY_VIEW_SET_PASSWORD :{BLACK}회사 암호 설정 # Network chat @@ -2402,6 +2402,7 @@ STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}잘못 STR_NETWORK_ERROR_SERVER_FULL :{WHITE}서버에 인원이 가득 찼습니다 STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}서버 관리자에 의해 접속이 차단되었습니다 STR_NETWORK_ERROR_KICKED :{WHITE}서버에서 강제로 추방되었습니다 +STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}사유: {STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}이 서버에서 치트를 사용할 수 없습니다 STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}서버에 너무 많은 명령을 보냈습니다 STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}비밀번호 입력 시간을 초과하였습니다 @@ -2465,6 +2466,7 @@ STR_NETWORK_MESSAGE_MONEY_GIVEN :*** {STRING} STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION :{STRING} ({COMPANY}) STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}서버가 게임을 종료하였습니다 STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}서버가 재시작되고 있습니다...{}기다려주세요... +STR_NETWORK_MESSAGE_KICKED :*** {STRING} - 서버에서 강제로 추방되었습니다. 사유: ({STRING}) # Content downloading window STR_CONTENT_TITLE :{WHITE}콘텐츠 다운로드 diff --git a/src/lang/russian.txt b/src/lang/russian.txt index e0eb067b5e..4d9a2903fc 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2320,6 +2320,7 @@ STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Неве STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Сервер переполнен STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Вас забанили на этом сервере STR_NETWORK_ERROR_KICKED :{WHITE}Вас выкинули из игры +STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Причина: {STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}Чит-коды не разрешены на этом сервере STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}Вы посылали на сервер слишком много команд STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Вы не успели ввести пароль @@ -2379,6 +2380,7 @@ STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} п STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY :*** Вы передали {1:STRING} {2:CURRENCY_LONG} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Сервер закрыл сессию STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Сервер перезапускается...{}Пожалуйста, подождите... +STR_NETWORK_MESSAGE_KICKED :*** {STRING} был исключён из игры. Причина: ({STRING}) # Content downloading window STR_CONTENT_TITLE :{WHITE}Загрузка контента diff --git a/src/music/fluidsynth.cpp b/src/music/fluidsynth.cpp index 89cb7273f5..64abe0e538 100644 --- a/src/music/fluidsynth.cpp +++ b/src/music/fluidsynth.cpp @@ -15,11 +15,13 @@ #include "midifile.hpp" #include #include "../mixer.h" +#include static struct { fluid_settings_t* settings; ///< FluidSynth settings handle fluid_synth_t* synth; ///< FluidSynth synthesizer handle fluid_player_t* player; ///< FluidSynth MIDI player handle + std::mutex synth_mutex; ///< Guard mutex for synth access } _midi; ///< Metadata about the midi we're playing. /** Factory for the FluidSynth driver. */ @@ -42,12 +44,16 @@ static const char *default_sf[] = { static void RenderMusicStream(int16 *buffer, size_t samples) { - if (!_midi.synth || !_midi.player) return; + std::unique_lock lock{ _midi.synth_mutex, std::try_to_lock }; + + if (!lock.owns_lock() || !_midi.synth || !_midi.player) return; fluid_synth_write_s16(_midi.synth, samples, buffer, 0, 2, buffer, 1, 2); } const char *MusicDriver_FluidSynth::Start(const char * const *param) { + std::lock_guard lock{ _midi.synth_mutex }; + const char *sfont_name = GetDriverParam(param, "soundfont"); int sfont_id; @@ -59,6 +65,11 @@ const char *MusicDriver_FluidSynth::Start(const char * const *param) /* Don't try to lock sample data in memory, OTTD usually does not run with privileges allowing that */ fluid_settings_setint(_midi.settings, "synth.lock-memory", 0); + /* Install the music render routine and set up the samplerate */ + uint32 samplerate = MxSetMusicSource(RenderMusicStream); + fluid_settings_setnum(_midi.settings, "synth.sample-rate", samplerate); + DEBUG(driver, 1, "Fluidsynth: samplerate %.0f", (float)samplerate); + /* Create the synthesizer. */ _midi.synth = new_fluid_synth(_midi.settings); if (!_midi.synth) return "Could not open synth"; @@ -81,19 +92,23 @@ const char *MusicDriver_FluidSynth::Start(const char * const *param) _midi.player = nullptr; - uint32 samplerate = MxSetMusicSource(RenderMusicStream); - fluid_synth_set_sample_rate(_midi.synth, samplerate); - DEBUG(driver, 1, "Fluidsynth: samplerate %.0f", (float)samplerate); - return nullptr; } void MusicDriver_FluidSynth::Stop() { MxSetMusicSource(nullptr); - this->StopSong(); - delete_fluid_synth(_midi.synth); - delete_fluid_settings(_midi.settings); + + std::lock_guard lock{ _midi.synth_mutex }; + + if (_midi.player != nullptr) delete_fluid_player(_midi.player); + _midi.player = nullptr; + + if (_midi.synth != nullptr) delete_fluid_synth(_midi.synth); + _midi.synth = nullptr; + + if (_midi.settings != nullptr) delete_fluid_settings(_midi.settings); + _midi.settings = nullptr; } void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song) @@ -106,6 +121,8 @@ void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song) return; } + std::lock_guard lock{ _midi.synth_mutex }; + _midi.player = new_fluid_player(_midi.synth); if (!_midi.player) { DEBUG(driver, 0, "Could not create midi player"); @@ -128,6 +145,8 @@ void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song) void MusicDriver_FluidSynth::StopSong() { + std::lock_guard lock{ _midi.synth_mutex }; + if (!_midi.player) return; fluid_player_stop(_midi.player); @@ -142,6 +161,7 @@ void MusicDriver_FluidSynth::StopSong() bool MusicDriver_FluidSynth::IsSongPlaying() { + std::lock_guard lock{ _midi.synth_mutex }; if (!_midi.player) return false; return fluid_player_get_status(_midi.player) == FLUID_PLAYER_PLAYING; @@ -149,6 +169,9 @@ bool MusicDriver_FluidSynth::IsSongPlaying() void MusicDriver_FluidSynth::SetVolume(byte vol) { + std::lock_guard lock{ _midi.synth_mutex }; + if (_midi.settings == nullptr) return; + /* Allowed range of synth.gain is 0.0 to 10.0 */ /* fluidsynth's default gain is 0.2, so use this as "full * volume". Set gain using OpenTTD's volume, as a number between 0 diff --git a/src/network/network.cpp b/src/network/network.cpp index e28e38f48d..6e5f34add9 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -267,6 +267,7 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, case NETWORK_ACTION_CHAT_COMPANY: strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY; break; case NETWORK_ACTION_CHAT_CLIENT: strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT; break; + case NETWORK_ACTION_KICKED: strid = STR_NETWORK_MESSAGE_KICKED; break; default: strid = STR_NETWORK_CHAT_ALL; break; } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 26dbd347ce..1700954850 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -776,8 +776,15 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p StringID err = STR_NETWORK_ERROR_LOSTCONNECTION; if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error]; - - ShowErrorMessage(err, INVALID_STRING_ID, WL_CRITICAL); + /* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */ + if (error == NETWORK_ERROR_KICKED && p->CanReadFromPacket(1)) { + char kick_msg[255]; + p->Recv_string(kick_msg, sizeof(kick_msg)); + SetDParamStr(0, kick_msg); + ShowErrorMessage(err, STR_NETWORK_ERROR_KICK_MESSAGE, WL_CRITICAL); + } else { + ShowErrorMessage(err, INVALID_STRING_ID, WL_CRITICAL); + } /* Perform an emergency save if we had already entered the game */ if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); diff --git a/src/network/network_func.h b/src/network/network_func.h index 13ba6d4a95..a075a2c3e4 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -76,9 +76,9 @@ void NetworkServerDoMove(ClientID client_id, CompanyID company_id); void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const char *string); void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const char *msg, ClientID from_id, NetworkTextMessageData data = NetworkTextMessageData(), bool from_admin = false); -void NetworkServerKickClient(ClientID client_id); -uint NetworkServerKickOrBanIP(ClientID client_id, bool ban); -uint NetworkServerKickOrBanIP(const char *ip, bool ban); +void NetworkServerKickClient(ClientID client_id, const char *reason); +uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, const char *reason); +uint NetworkServerKickOrBanIP(const char *ip, bool ban, const char *reason); void NetworkInitChatMessage(); void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *message, ...) WARN_FORMAT(3, 4); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index ee29883572..0494261b25 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1688,12 +1688,12 @@ static WindowDesc _client_list_popup_desc( /* Here we start to define the options out of the menu */ static void ClientList_Kick(const NetworkClientInfo *ci) { - NetworkServerKickClient(ci->client_id); + NetworkServerKickClient(ci->client_id, nullptr); } static void ClientList_Ban(const NetworkClientInfo *ci) { - NetworkServerKickOrBanIP(ci->client_id, true); + NetworkServerKickOrBanIP(ci->client_id, true, nullptr); } static void ClientList_SpeakToClient(const NetworkClientInfo *ci) diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 147b922479..65a735d2c8 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -422,13 +422,15 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo() /** * Send an error to the client, and close its connection. * @param error The error to disconnect for. + * @param reason In case of kicking a client, specifies the reason for kicking the client. */ -NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode error) +NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode error, const char *reason) { char str[100]; Packet *p = new Packet(PACKET_SERVER_ERROR); p->Send_uint8(error); + if (reason != nullptr) p->Send_string(reason); this->SendPacket(p); StringID strid = GetNetworkErrorMsg(error); @@ -442,7 +444,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err DEBUG(net, 1, "'%s' made an error and has been disconnected. Reason: '%s'", client_name, str); - NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, nullptr, strid); + if (error == NETWORK_ERROR_KICKED && reason != nullptr) { + NetworkTextMessage(NETWORK_ACTION_KICKED, CC_DEFAULT, false, client_name, reason, strid); + } else { + NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, nullptr, strid); + } for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) { if (new_cs->status > STATUS_AUTHORIZED && new_cs != this) { @@ -2139,29 +2145,32 @@ void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const cha /** * Kick a single client. * @param client_id The client to kick. + * @param reason In case of kicking a client, specifies the reason for kicking the client. */ -void NetworkServerKickClient(ClientID client_id) +void NetworkServerKickClient(ClientID client_id, const char *reason) { if (client_id == CLIENT_ID_SERVER) return; - NetworkClientSocket::GetByClientID(client_id)->SendError(NETWORK_ERROR_KICKED); + NetworkClientSocket::GetByClientID(client_id)->SendError(NETWORK_ERROR_KICKED, reason); } /** * Ban, or kick, everyone joined from the given client's IP. * @param client_id The client to check for. * @param ban Whether to ban or kick. + * @param reason In case of kicking a client, specifies the reason for kicking the client. */ -uint NetworkServerKickOrBanIP(ClientID client_id, bool ban) +uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, const char *reason) { - return NetworkServerKickOrBanIP(NetworkClientSocket::GetByClientID(client_id)->GetClientIP(), ban); + return NetworkServerKickOrBanIP(NetworkClientSocket::GetByClientID(client_id)->GetClientIP(), ban, reason); } /** * Kick or ban someone based on an IP address. * @param ip The IP address/range to ban/kick. * @param ban Whether to ban or just kick. + * @param reason In case of kicking a client, specifies the reason for kicking the client. */ -uint NetworkServerKickOrBanIP(const char *ip, bool ban) +uint NetworkServerKickOrBanIP(const char *ip, bool ban, const char *reason) { /* Add address to ban-list */ if (ban) { @@ -2177,11 +2186,16 @@ uint NetworkServerKickOrBanIP(const char *ip, bool ban) uint n = 0; - /* There can be multiple clients with the same IP, kick them all */ + /* There can be multiple clients with the same IP, kick them all but don't kill the server, + * or the client doing the rcon. The latter can't be kicked because kicking frees closes + * and subsequently free the connection related instances, which we would be reading from + * and writing to after returning. So we would read or write data from freed memory up till + * the segfault triggers. */ for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) { if (cs->client_id == CLIENT_ID_SERVER) continue; + if (cs->client_id == _redirect_console_to_client) continue; if (cs->client_address.IsInNetmask(ip)) { - NetworkServerKickClient(cs->client_id); + NetworkServerKickClient(cs->client_id, reason); n++; } } diff --git a/src/network/network_server.h b/src/network/network_server.h index 2ade4bf231..209a4684a9 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -98,7 +98,7 @@ public: NetworkRecvStatus SendMove(ClientID client_id, CompanyID company_id); NetworkRecvStatus SendClientInfo(NetworkClientInfo *ci); - NetworkRecvStatus SendError(NetworkErrorCode error); + NetworkRecvStatus SendError(NetworkErrorCode error, const char *reason = nullptr); NetworkRecvStatus SendDesyncLog(const std::string &log); NetworkRecvStatus SendChat(NetworkAction action, ClientID client_id, bool self_send, const char *msg, NetworkTextMessageData data); NetworkRecvStatus SendJoin(ClientID client_id); diff --git a/src/network/network_type.h b/src/network/network_type.h index 3ea700e784..d176d4a486 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -86,6 +86,7 @@ enum DestType { enum NetworkAction { NETWORK_ACTION_JOIN, NETWORK_ACTION_LEAVE, + NETWORK_ACTION_KICKED, NETWORK_ACTION_SERVER_MESSAGE, NETWORK_ACTION_CHAT, NETWORK_ACTION_CHAT_COMPANY, diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index cfa4ce0018..6e17932ac8 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -368,6 +368,16 @@ TownScopeResolver *StationResolverObject::GetTown() return res; } + case 0x6A: { // GRFID of nearby station tiles + TileIndex nearby_tile = GetNearbyTile(parameter, this->tile); + + if (!HasStationTileRail(nearby_tile)) return 0xFFFFFFFF; + if (!IsCustomStationSpecIndex(nearby_tile)) return 0; + + const StationSpecList ssl = BaseStation::GetByTile(nearby_tile)->speclist[GetCustomStationSpecIndex(nearby_tile)]; + return ssl.grfid; + } + /* General station variables */ case 0x82: return 50; case 0x84: return this->st->string_id; diff --git a/src/os/windows/ottdres.rc.in b/src/os/windows/ottdres.rc.in index f51023843f..8cce72cea8 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,10,0,!!ISODATE!! - PRODUCTVERSION 1,10,0,!!ISODATE!! + FILEVERSION 1,11,0,!!ISODATE!! + PRODUCTVERSION 1,11,0,!!ISODATE!! FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L diff --git a/src/pathfinder/pathfinder_type.h b/src/pathfinder/pathfinder_type.h index 29b02cde75..79eeb8ec73 100644 --- a/src/pathfinder/pathfinder_type.h +++ b/src/pathfinder/pathfinder_type.h @@ -44,6 +44,9 @@ static const int YAPF_SHIP_PATH_CACHE_LENGTH = 32; /** Maximum segments of road vehicle path cache */ static const int YAPF_ROADVEH_PATH_CACHE_SEGMENTS = 16; +/** Distance from destination road stops to not cache any further */ +static const int YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT = 8; + /** * Helper container to find a depot */ diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 99ebc92235..349c75264a 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -429,9 +429,9 @@ public: const RoadStop *stop = st->GetPrimaryRoadStop(v); if (stop != nullptr && (IsDriveThroughStopTile(stop->xy) || stop->GetNextRoadStop(v) != nullptr)) { /* Destination station has at least 2 usable road stops, or first is a drive-through stop, - * trim end of path cache within 8 tiles of road stop tile area */ + * trim end of path cache within a number of tiles of road stop tile area */ TileArea non_cached_area = v->IsBus() ? st->bus_station : st->truck_station; - non_cached_area.Expand(8); + non_cached_area.Expand(YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT); while (!path_cache.empty() && non_cached_area.Contains(path_cache.tile.back())) { path_cache.td.pop_back(); path_cache.tile.pop_back(); diff --git a/src/rev.cpp.in b/src/rev.cpp.in index 836c32231d..50c7e06e12 100644 --- a/src/rev.cpp.in +++ b/src/rev.cpp.in @@ -91,4 +91,4 @@ const byte _openttd_revision_tagged = !!ISTAG!!; * final release will always have a lower version number than the released * version, thus making comparisons on specific revisions easy. */ -const uint32 _openttd_newgrf_version = 1 << 28 | 10 << 24 | 0 << 20 | !!ISSTABLETAG!! << 19 | 28004; +const uint32 _openttd_newgrf_version = 1 << 28 | 11 << 24 | 0 << 20 | !!ISSTABLETAG!! << 19 | 28004; diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index c57f403741..a97d489272 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -305,7 +305,7 @@ enum SaveLoadVersion : uint16 { SLV_SCRIPT_MEMLIMIT, ///< 215 PR#7516 Limit on AI/GS memory consumption. SLV_MULTITILE_DOCKS, ///< 216 PR#7380 Multiple docks per station. SLV_TRADING_AGE, ///< 217 PR#7780 Configurable company trading age. - SLV_ENDING_YEAR, ///< 218 PR#7747 Configurable ending year. + SLV_ENDING_YEAR, ///< 218 PR#7747 v1.10 Configurable ending year. SL_MAX_VERSION, ///< Highest possible saveload version @@ -506,7 +506,8 @@ enum VarTypes { SLF_NO_NETWORK_SYNC = 1 << 10, ///< do not synchronize over network (but it is saved if SLF_NOT_IN_SAVE is not set) SLF_ALLOW_CONTROL = 1 << 11, ///< allow control codes in the strings SLF_ALLOW_NEWLINE = 1 << 12, ///< allow new lines in the strings - /* 3 more possible flags */ + SLF_HEX = 1 << 13, ///< print numbers as hex in the config file (only useful for unsigned) + /* 2 more possible flags */ }; typedef uint32 VarType; diff --git a/src/script/api/ai_changelog.hpp b/src/script/api/ai_changelog.hpp index 9eea9ab152..bcb7df67f2 100644 --- a/src/script/api/ai_changelog.hpp +++ b/src/script/api/ai_changelog.hpp @@ -13,10 +13,12 @@ * functions may still be available if you return an older API version * in GetAPIVersion() in info.nut. * - * \b 1.10.0 + * \b 1.11.0 * * This version is not yet released. The following changes are not set in stone yet. * + * \b 1.10.0 + * * API additions: * \li AIGroup::SetPrimaryColour * \li AIGroup::SetSecondaryColour diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index 235dcee986..df8c663a03 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -13,10 +13,12 @@ * functions may still be available if you return an older API version * in GetAPIVersion() in info.nut. * - * \b 1.10.0 + * \b 1.11.0 * * This version is not yet released. The following changes are not set in stone yet. * + * \b 1.10.0 + * * API additions: * \li GSVehicle::BuildVehicleWithRefit * \li GSVehicle::GetBuildWithRefitCapacity diff --git a/src/settings.cpp b/src/settings.cpp index d587585d26..7c3a400103 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -22,6 +22,7 @@ */ #include "stdafx.h" +#include #include "currency.h" #include "screenshot.h" #include "network/network.h" @@ -176,7 +177,8 @@ static size_t LookupManyOfMany(const char *many, const char *str) * @param maxitems the maximum number of elements the integerlist-array has * @return returns the number of items found, or -1 on an error */ -static int ParseIntList(const char *p, int *items, int maxitems) +template +static int ParseIntList(const char *p, T *items, int maxitems) { int n = 0; // number of items read so far bool comma = false; // do we accept comma? @@ -196,9 +198,9 @@ static int ParseIntList(const char *p, int *items, int maxitems) default: { if (n == maxitems) return -1; // we don't accept that many numbers char *end; - long v = strtol(p, &end, 0); + unsigned long v = strtoul(p, &end, 0); if (p == end) return -1; // invalid character (not a number) - if (sizeof(int) < sizeof(long)) v = ClampToI32(v); + if (sizeof(T) < sizeof(v)) v = Clamp(v, std::numeric_limits::min(), std::numeric_limits::max()); items[n++] = v; p = end; // first non-number comma = true; // we accept comma now @@ -224,7 +226,7 @@ static int ParseIntList(const char *p, int *items, int maxitems) */ static bool LoadIntList(const char *str, void *array, int nelems, VarType type) { - int items[64]; + unsigned long items[64]; int i, nitems; if (str == nullptr) { @@ -273,7 +275,7 @@ static void MakeIntList(char *buf, const char *last, const void *array, int nele const byte *p = (const byte *)array; for (i = 0; i != nelems; i++) { - switch (type) { + switch (GetVarMemType(type)) { case SLE_VAR_BL: case SLE_VAR_I8: v = *(const int8 *)p; p += 1; break; case SLE_VAR_U8: v = *(const uint8 *)p; p += 1; break; @@ -283,7 +285,13 @@ static void MakeIntList(char *buf, const char *last, const void *array, int nele case SLE_VAR_U32: v = *(const uint32 *)p; p += 4; break; default: NOT_REACHED(); } - buf += seprintf(buf, last, (i == 0) ? "%d" : ",%d", v); + if (IsSignedVarMemType(type)) { + buf += seprintf(buf, last, (i == 0) ? "%d" : ",%d", v); + } else if (type & SLF_HEX) { + buf += seprintf(buf, last, (i == 0) ? "0x%X" : ",0x%X", v); + } else { + buf += seprintf(buf, last, (i == 0) ? "%u" : ",%u", v); + } } } @@ -696,7 +704,7 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp switch (sdb->cmd) { case SDT_BOOLX: strecpy(buf, (i != 0) ? "true" : "false", lastof(buf)); break; - case SDT_NUMX: seprintf(buf, lastof(buf), IsSignedVarMemType(sld->conv) ? "%d" : "%u", i); break; + case SDT_NUMX: seprintf(buf, lastof(buf), IsSignedVarMemType(sld->conv) ? "%d" : (sld->conv & SLF_HEX) ? "%X" : "%u", i); break; case SDT_ONEOFMANY: MakeOneOfMany(buf, lastof(buf), sdb->many, i); break; case SDT_MANYOFMANY: MakeManyOfMany(buf, lastof(buf), sdb->many, i); break; default: NOT_REACHED(); @@ -724,7 +732,7 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp break; case SDT_INTLIST: - MakeIntList(buf, lastof(buf), ptr, sld->length, GetVarMemType(sld->conv)); + MakeIntList(buf, lastof(buf), ptr, sld->length, sld->conv); break; default: NOT_REACHED(); @@ -1703,7 +1711,7 @@ static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_stati /* Parse parameters */ if (!StrEmpty(item->value)) { - int count = ParseIntList(item->value, (int*)c->param, lengthof(c->param)); + int count = ParseIntList(item->value, c->param, lengthof(c->param)); if (count < 0) { SetDParamStr(0, filename); ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 675d955a97..275c1958f0 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -4081,7 +4081,7 @@ void StationMonthlyLoop() void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius) { ForAllStationsRadius(tile, radius, [&](Station *st) { - if (st->owner == owner) { + if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) { for (CargoID i = 0; i < NUM_CARGO; i++) { GoodsEntry *ge = &st->goods[i]; diff --git a/src/table/company_settings.ini b/src/table/company_settings.ini index 29b5eef1eb..30ff023702 100644 --- a/src/table/company_settings.ini +++ b/src/table/company_settings.ini @@ -45,6 +45,7 @@ var = engine_renew def = false str = STR_CONFIG_SETTING_AUTORENEW_VEHICLE strhelp = STR_CONFIG_SETTING_AUTORENEW_VEHICLE_HELPTEXT +cat = SC_BASIC [SDT_VAR] base = CompanySettings diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 1362857b3a..c555e7e0d3 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -176,6 +176,7 @@ static const NIVariable _niv_stations[] = { NIV(0x67, "land info of nearby tiles"), NIV(0x68, "station info of nearby tiles"), NIV(0x69, "information about cargo accepted in the past"), + NIV(0x6A, "GRFID of nearby station tiles"), NIV_END() };