diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index 789f8f6a32..19c185894c 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -38,10 +38,14 @@ jobs: strategy: matrix: - commit-checker: {} - linux-amd64-clang-3.8: {} - linux-amd64-gcc-6: {} - linux-i386-gcc-6: {} + commit-checker: + Tag: 'commit-checker' + linux-amd64-clang-3.8: + Tag: 'linux-amd64-clang-3.8' + linux-amd64-gcc-6: + Tag: 'linux-amd64-gcc-6' + linux-i386-gcc-6: + Tag: 'linux-i386-gcc-6' steps: - template: azure-pipelines/templates/ci-git-rebase.yml @@ -50,7 +54,7 @@ jobs: - template: azure-pipelines/templates/linux-build.yml parameters: Image: compile-farm-ci - Tag: $(Agent.JobName) + Tag: $(Tag) - job: macos displayName: 'MacOS' diff --git a/azure-pipelines/templates/linux-build.yml b/azure-pipelines/templates/linux-build.yml index 7f442904c4..a173f3364d 100644 --- a/azure-pipelines/templates/linux-build.yml +++ b/azure-pipelines/templates/linux-build.yml @@ -24,7 +24,7 @@ steps: ${{ if eq(parameters.Image, 'compile-farm-ci') }}: displayName: 'Build and test' # Run the commit-checker only if it is a Pull Request - condition: and(succeeded(), or(ne(variables['Agent.JobName'], 'commit-checker'), eq(variables['Build.Reason'], 'PullRequest'))) + condition: and(succeeded(), or(not(contains(variables['Agent.JobName'], 'commit-checker')), eq(variables['Build.Reason'], 'PullRequest'))) inputs: command: 'Run an image' imageName: openttd/${{ parameters.Image }}:${{ parameters.Tag }} diff --git a/azure-pipelines/templates/release.yml b/azure-pipelines/templates/release.yml index 7628d3c91e..a1438c4a58 100644 --- a/azure-pipelines/templates/release.yml +++ b/azure-pipelines/templates/release.yml @@ -110,14 +110,22 @@ jobs: strategy: matrix: - linux-ubuntu-xenial-i386-gcc: {} - linux-ubuntu-xenial-amd64-gcc: {} - linux-ubuntu-bionic-i386-gcc: {} - linux-ubuntu-bionic-amd64-gcc: {} - linux-debian-jessie-i386-gcc: {} - linux-debian-jessie-amd64-gcc: {} - linux-debian-stretch-i386-gcc: {} - linux-debian-stretch-amd64-gcc: {} + linux-ubuntu-xenial-i386-gcc: + Tag: 'linux-ubuntu-xenial-i386-gcc' + linux-ubuntu-xenial-amd64-gcc: + Tag: 'linux-ubuntu-xenial-amd64-gcc' + linux-ubuntu-bionic-i386-gcc: + Tag: 'linux-ubuntu-bionic-i386-gcc' + linux-ubuntu-bionic-amd64-gcc: + Tag: 'linux-ubuntu-bionic-amd64-gcc' + linux-debian-jessie-i386-gcc: + Tag: 'linux-debian-jessie-i386-gcc' + linux-debian-jessie-amd64-gcc: + Tag: 'linux-debian-jessie-amd64-gcc' + linux-debian-stretch-i386-gcc: + Tag: 'linux-debian-stretch-i386-gcc' + linux-debian-stretch-amd64-gcc: + Tag: 'linux-debian-stretch-amd64-gcc' steps: - template: release-fetch-source.yml @@ -125,7 +133,7 @@ jobs: parameters: Image: compile-farm ContainerCommand: '$(Build.BuildNumber)' - Tag: $(Agent.JobName) + Tag: $(Tag) - template: linux-claim-bundles.yml - template: release-bundles.yml diff --git a/changelog.txt b/changelog.txt index eb5cd6aece..1beeeaecdb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,29 @@ +1.9.2 (2019-07-07) +------------------------------------------------------------------------ +- Change: Set default setting in server browser of "Advertised" to "Yes" (#7568) +- Change: Allow building road stops over self-owned one-way/blocked road (#7547) +- Fix #7463: Promote scroll mode setting to basic category (#7586) +- Fix: Inconsistent GUI scaling (#7539) +- Fix #7491: Send company update admin message when bankruptcy counter changes (#7492) +- Fix #7553: Check bounds when loading strings (#7554) +- Fix: Really increase the maximum number of GameScript texts to 64k (#7555) +- Fix: Crash when attempting to load old save game with GRFs set (#7546) +- Fix #6507: Don't try to load invalid depots from older savegames (#7546) +- Fix: Railtype bits were moved too late, leading to rails under bridges losing their type (#7546) +- Fix: Bounds check access to railtype_map (#7529) +- Fix: Spurious errors when using more than 32 railtypes (#7533) +- Fix #7633: Allow zero-cost track conversion to succeed (#7634) +- Fix #7577: Check if linkgraph station index is valid before dereferencing (#7583) +- Fix #7224: Drag and drop vehicle group creation does not work correctly (#7581) +- Fix #7570: Show Github URL in the crashlog window (#7571) +- Fix: Clicking on scrollbar 'thumb' moved position up instantly (#7549) +- Fix #7255: Prevent crashlog corruption by only printing the 32 most recent news messages (#7542) +- Fix #5685: Check for free wagons in ScriptVehicleList (#7617) +- Fix: Make GSGoal.QuestionClient work correctly at least for clients with ID < 2**16 (#7560) +- Fix #6666: Mismatched parentheses in RTL languages (#7480) +- Fix: [Windows] Various reliability and correctness improvements to MIDI on Windows (#7620) + + 1.9.1 (2019-04-08) ------------------------------------------------------------------------ - Fix #6564: Enforce types of arguments for station name strings (#7419) @@ -8,6 +34,7 @@ - Fix: [Windows] Incorrect error handling could lead to cascading error windows (#7482) - Fix #7478: Don't remove NewGRF objects on company take-over. (#7483) + 1.9.0 (2019-04-01) ------------------------------------------------------------------------ - Fix #7411: Use industry production callback (if used) on initial industry cargo generation (#7412) diff --git a/known-bugs.txt b/known-bugs.txt index 7df6283c50..2d96c7d440 100644 --- a/known-bugs.txt +++ b/known-bugs.txt @@ -1,6 +1,6 @@ OpenTTD's known bugs -Last updated: 2019-04-08 -Release version: 1.9.1 +Last updated: 2019-07-07 +Release version: 1.9.2 ------------------------------------------------------------------------ diff --git a/os/debian/changelog b/os/debian/changelog index 37b3cd7814..246d5ed29d 100644 --- a/os/debian/changelog +++ b/os/debian/changelog @@ -1,3 +1,9 @@ +openttd (1.9.2-0) unstable; urgency=low + + * New upstream release 1.9.2 + + -- OpenTTD Sun, 07 Jul 2019 23:00:00 +0200 + openttd (1.9.1-0) unstable; urgency=low * New upstream release 1.9.1 diff --git a/os/os2/installer/make_installer.cmd b/os/os2/installer/make_installer.cmd index f0a8401643..9c8cbd65ea 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.9.1 +set OPENTTD_VERSION=1.9.2 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 81f73543b9..1c0795c781 100644 --- a/os/rpm/openttd.spec +++ b/os/rpm/openttd.spec @@ -17,9 +17,9 @@ # Name: openttd -Version: 1.9.1 +Version: 1.9.2 Release: 0 -%define srcver 1.9.1 +%define srcver 1.9.2 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 efd3310c95..1b07bab442 100644 --- a/os/windows/installer/install.nsi +++ b/os/windows/installer/install.nsi @@ -1,7 +1,7 @@ # Version numbers to update !define APPV_MAJOR 1 !define APPV_MINOR 9 -!define APPV_MAINT 1 +!define APPV_MAINT 2 !define APPV_BUILD 0 !define APPV_EXTRA "" diff --git a/src/core/enum_type.hpp b/src/core/enum_type.hpp index 35a0cb2926..fc89b9489e 100644 --- a/src/core/enum_type.hpp +++ b/src/core/enum_type.hpp @@ -12,18 +12,20 @@ #ifndef ENUM_TYPE_HPP #define ENUM_TYPE_HPP +#include + /** Some enums need to have allowed incrementing (i.e. StationClassID) */ -#define DECLARE_POSTFIX_INCREMENT(type) \ - inline type operator ++(type& e, int) \ +#define DECLARE_POSTFIX_INCREMENT(enum_type) \ + inline enum_type operator ++(enum_type& e, int) \ { \ - type e_org = e; \ - e = (type)((int)e + 1); \ + enum_type e_org = e; \ + e = (enum_type)((std::underlying_type::type)e + 1); \ return e_org; \ } \ - inline type operator --(type& e, int) \ + inline enum_type operator --(enum_type& e, int) \ { \ - type e_org = e; \ - e = (type)((int)e - 1); \ + enum_type e_org = e; \ + e = (enum_type)((std::underlying_type::type)e - 1); \ return e_org; \ } @@ -31,13 +33,13 @@ /** Operators to allow to work with enum as with type safe bit set in C++ */ # define DECLARE_ENUM_AS_BIT_SET(mask_t) \ - inline mask_t operator | (mask_t m1, mask_t m2) {return (mask_t)((int)m1 | m2);} \ - inline mask_t operator & (mask_t m1, mask_t m2) {return (mask_t)((int)m1 & m2);} \ - inline mask_t operator ^ (mask_t m1, mask_t m2) {return (mask_t)((int)m1 ^ m2);} \ + inline mask_t operator | (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 | m2);} \ + inline mask_t operator & (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 & m2);} \ + inline mask_t operator ^ (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type::type)m1 ^ m2);} \ inline mask_t& operator |= (mask_t& m1, mask_t m2) {m1 = m1 | m2; return m1;} \ inline mask_t& operator &= (mask_t& m1, mask_t m2) {m1 = m1 & m2; return m1;} \ inline mask_t& operator ^= (mask_t& m1, mask_t m2) {m1 = m1 ^ m2; return m1;} \ - inline mask_t operator ~(mask_t m) {return (mask_t)(~(int)m);} + inline mask_t operator ~(mask_t m) {return (mask_t)(~(std::underlying_type::type)m);} /** diff --git a/src/crashlog.cpp b/src/crashlog.cpp index ab27837078..2bc891dcf5 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -303,7 +303,7 @@ char *CrashLog::LogGamelog(char *buffer, const char *last) const } /** - * Writes any recent news messages to the buffer. + * Writes up to 32 recent news messages to the buffer, with the most recent first. * @param buffer The begin where to write at. * @param last The last position in the buffer to write to. * @return the position of the \c '\0' character after the buffer. @@ -312,7 +312,8 @@ char *CrashLog::LogRecentNews(char *buffer, const char *last) const { buffer += seprintf(buffer, last, "Recent news messages:\n"); - for (NewsItem *news = _oldest_news; news != NULL; news = news->next) { + int i = 0; + for (NewsItem *news = _latest_news; i < 32 && news != NULL; news = news->prev, i++) { YearMonthDay ymd; ConvertDateToYMD(news->date, &ymd); buffer += seprintf(buffer, last, "(%i-%02i-%02i) StringID: %u, Type: %u, Ref1: %u, %u, Ref2: %u, %u\n", diff --git a/src/economy.cpp b/src/economy.cpp index 7f9298258a..bbb3100bd3 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -572,8 +572,10 @@ static void CompanyCheckBankrupt(Company *c) { /* If the company has money again, it does not go bankrupt */ if (c->money - c->current_loan >= -_economy.max_loan) { + int previous_months_of_bankruptcy = CeilDiv(c->months_of_bankruptcy, 3); c->months_of_bankruptcy = 0; c->bankrupt_asked = 0; + if (previous_months_of_bankruptcy != 0) CompanyAdminUpdate(c); return; } @@ -642,10 +644,15 @@ static void CompanyCheckBankrupt(Company *c) * that changing the current company is okay. In case of single * player we are sure (the above check) that we are not the local * company and thus we won't be moved. */ - if (!_networking || _network_server) DoCommandP(0, CCA_DELETE | (c->index << 16) | (CRR_BANKRUPT << 24), 0, CMD_COMPANY_CTRL); + if (!_networking || _network_server) { + DoCommandP(0, CCA_DELETE | (c->index << 16) | (CRR_BANKRUPT << 24), 0, CMD_COMPANY_CTRL); + return; + } break; } } + + if (CeilDiv(c->months_of_bankruptcy, 3) != CeilDiv(c->months_of_bankruptcy - 1, 3)) CompanyAdminUpdate(c); } /** diff --git a/src/game/game_text.cpp b/src/game/game_text.cpp index a32e5b41d7..c71483b391 100644 --- a/src/game/game_text.cpp +++ b/src/game/game_text.cpp @@ -308,7 +308,7 @@ GameStrings *LoadTranslations() /** Compile the language. */ void GameStrings::Compile() { - StringData data(1); + StringData data(32); StringListReader master_reader(data, this->raw_strings[0], true, false); master_reader.ParseFile(); if (_errors != 0) throw std::exception(); diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index c65ead90e1..62e4831091 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -208,7 +208,7 @@ public: LEErrorCode status = LE_NO_ERROR; /* ParagraphLayout does not copy "buff", so it must stay valid. * "runs" is copied according to the ICU source, but the documentation does not specify anything, so this might break somewhen. */ - icu::ParagraphLayout *p = new icu::ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status); + icu::ParagraphLayout *p = new icu::ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? 1 : 0, false, status); if (status != LE_NO_ERROR) { delete p; return NULL; diff --git a/src/goal.cpp b/src/goal.cpp index f7aae350e5..16d826222c 100644 --- a/src/goal.cpp +++ b/src/goal.cpp @@ -236,10 +236,11 @@ CommandCost CmdSetGoalCompleted(TileIndex tile, DoCommandFlag flags, uint32 p1, * @param flags type of operation * @param p1 various bitstuffed elements * - p1 = (bit 0 - 15) - Unique ID to use for this question. - * - p1 = (bit 16 - 23) - Company or client for which this question is. - * - p1 = (bit 24 - 25) - Question type. - * - p1 = (bit 31) - Question target: 0 - company, 1 - client. - * @param p2 Buttons of the question. + * - p1 = (bit 16 - 31) - Company or client for which this question is. + * @param p2 various bitstuffed elements + * - p2 = (bit 0 - 17) - Buttons of the question. + * - p2 = (bit 29 - 30) - Question type. + * - p2 = (bit 31) - Question target: 0 - company, 1 - client. * @param text Text of the question. * @return the cost of this operation or an error */ @@ -248,36 +249,38 @@ CommandCost CmdGoalQuestion(TileIndex tile, DoCommandFlag flags, uint32 p1, uint uint16 uniqueid = (GoalType)GB(p1, 0, 16); CompanyID company = (CompanyID)GB(p1, 16, 8); #ifdef ENABLE_NETWORK - ClientIndex client = (ClientIndex)GB(p1, 16, 8); + ClientID client = (ClientID)GB(p1, 16, 16); #endif - byte type = GB(p1, 24, 2); - bool is_client = HasBit(p1, 31); + + assert_compile(GOAL_QUESTION_BUTTON_COUNT < 29); + uint32 button_mask = GB(p2, 0, GOAL_QUESTION_BUTTON_COUNT); + byte type = GB(p2, 29, 2); + bool is_client = HasBit(p2, 31); if (_current_company != OWNER_DEITY) return CMD_ERROR; if (StrEmpty(text)) return CMD_ERROR; if (is_client) { #ifdef ENABLE_NETWORK - if (!NetworkClientInfo::IsValidID(client)) return CMD_ERROR; + if (NetworkClientInfo::GetByClientID(client) == nullptr) return CMD_ERROR; #else return CMD_ERROR; #endif } else { if (company != INVALID_COMPANY && !Company::IsValidID(company)) return CMD_ERROR; } - if (CountBits(p2) < 1 || CountBits(p2) > 3) return CMD_ERROR; - if (p2 >= (1 << GOAL_QUESTION_BUTTON_COUNT)) return CMD_ERROR; + if (CountBits(button_mask) < 1 || CountBits(button_mask) > 3) return CMD_ERROR; if (type >= GOAL_QUESTION_TYPE_COUNT) return CMD_ERROR; if (flags & DC_EXEC) { if (is_client) { #ifdef ENABLE_NETWORK - if (NetworkClientInfo::Get(client)->client_id != _network_own_client_id) return CommandCost(); + if (client != _network_own_client_id) return CommandCost(); #endif } else { if (company == INVALID_COMPANY && !Company::IsValidID(_local_company)) return CommandCost(); if (company != INVALID_COMPANY && company != _local_company) return CommandCost(); } - ShowGoalQuestion(uniqueid, type, p2, text); + ShowGoalQuestion(uniqueid, type, button_mask, text); } return CommandCost(); diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index bd99aa1272..39a945102c 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -536,7 +536,7 @@ CommandCost CmdAddVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (new_g == NEW_GROUP) { /* Create new group. */ - CommandCost ret = CmdCreateGroup(0, flags, v->type, 0, NULL); + CommandCost ret = CmdCreateGroup(0, flags, v->type, INVALID_GROUP, NULL); if (ret.Failed()) return ret; new_g = _new_group_id; diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index 2f658115c7..bedfd34f9d 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -282,7 +282,7 @@ STR_SORT_BY_TIMETABLE_DELAY :تاخير جد STR_SORT_BY_FACILITY :نوع المحطة STR_SORT_BY_RATING_MAX :اعلى نسبة شحن STR_SORT_BY_RATING_MIN :اقل نسبة شحن -STR_SORT_BY_ENGINE_ID :نوع المحرك (قياسي( +STR_SORT_BY_ENGINE_ID :نوع المحرك (قياسي) STR_SORT_BY_COST :التكلفة STR_SORT_BY_POWER :الطاقة STR_SORT_BY_TRACTIVE_EFFORT :قوة الجذب @@ -1147,7 +1147,7 @@ STR_CONFIG_SETTING_INDUSTRY_DENSITY :الكثافة STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :إختر مقدار الصناعات التي يجب تكوينها وعلى اي مستوى يجب ان تكون خلال اللعبة STR_CONFIG_SETTING_SNOWLINE_HEIGHT :ارتفاع خط الثلج: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :تحكم على اي ارتفاع يبدأ نزول الثلج في المناطق القطبية,تؤثر الثلوج على مستوى تطور القطاع الصناعي ونمو المدن -STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :خشونة التضاريس (صفر التكوين فقط ) :({STRING} +STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :خشونة التضاريس: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(TerraGenesis only)إختر تكرار الهضبات: الاراض المستويه تحتوي على البضع منها,هضبات موزعه عرضيا اكثر,الاراض الوعرة تحتوي الكثير من الهضاب,التي من الممكن ان تكون متكررة STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :ناعم جدا STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_SMOOTH :ناعم @@ -1946,7 +1946,7 @@ STR_RAIL_TOOLBAR_MAGLEV_CONSTRUCTION_CAPTION :بناء الس STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}بناء سكة حديد STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL :{BLACK}بناء سكة القطار باستخدام البناء التلقائي -STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}بناء ورشة قطارات (لصيانة و شراء القطارات). +STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}بناء ورشة قطارات (لصيانة و شراء القطارات) STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT :{BLACK}غير السكة الى نقطة عبور STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION :{BLACK}بناء محطة قطار. مفتاح كنترول يسمح بضم المحطات STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_SIGNALS :{BLACK}بناء إشارات السكك الحديدية. @@ -2028,7 +2028,7 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}بناء STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}بناء الطرق باستخدام النظام الآلي STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}بناء سكة القطار باستخدام النظام الآلي STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}بناء ورشة صيانة لعربات الطرق (لشراء و صيانة العربات). -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}بناء ورشة لصيانة عربات الترام (لشراء و صيانة عربات الترام). +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}بناء ورشة لصيانة عربات الترام (لشراء و صيانة عربات الترام) STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}بناء محطة باصات STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION :{BLACK}بناء محطة ركاب ترام. STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY :{BLACK}بناء محطة تحميل عربات. مفتاح كنترول يسمح بمجاورة المحطات. @@ -2590,7 +2590,8 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}ادخل # Town directory window STR_TOWN_DIRECTORY_CAPTION :{WHITE}مدن/ بلدات STR_TOWN_DIRECTORY_NONE :{ORANGE}-بدون- -STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ) {COMMA}) +STR_TOWN_DIRECTORY_TOWN :{ORANGE}{RLE}{TOWN}{BLACK} {RLM}({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{RLE}{TOWN}{YELLOW} (مدينة){BLACK} {RLM}({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}اسم المدينة - اضغط على الاسم لتوسيط الشاشة عليها. اضغط + كنترول لفتح شاشة عرض جديدة للضاحية. STR_TOWN_POPULATION :{BLACK}سكان العالم: {COMMA} @@ -2678,7 +2679,7 @@ STR_STATION_LIST_STATION :{YELLOW}{STATIO STR_STATION_LIST_WAYPOINT :{YELLOW}{WAYPOINT} STR_STATION_LIST_NONE :{YELLOW}- بدون - STR_STATION_LIST_SELECT_ALL_FACILITIES :{BLACK}اختر جميع المرافق -STR_STATION_LIST_SELECT_ALL_TYPES :{BLACK}اختر كل انواع الشحن (حتى غير المنتظرة( +STR_STATION_LIST_SELECT_ALL_TYPES :{BLACK}اختر كل انواع الشحن (حتى غير المنتظرة) STR_STATION_LIST_NO_WAITING_CARGO :{BLACK}لا يوجد اي شحنة منتظرة # Station view window @@ -2897,7 +2898,7 @@ STR_PURCHASE_INFO_SPEED_OCEAN :{BLACK}السر STR_PURCHASE_INFO_SPEED_CANAL :{BLACK}السرعة في القناة/النهر: {GOLD}{VELOCITY} STR_PURCHASE_INFO_RUNNINGCOST :{BLACK}تكلفة التشغيل: {GOLD}{CURRENCY_LONG}/ سنة STR_PURCHASE_INFO_CAPACITY :{BLACK}السعة: {GOLD}{CARGO_LONG} {STRING} -STR_PURCHASE_INFO_REFITTABLE :)قابل لتغيير( +STR_PURCHASE_INFO_REFITTABLE :(قابل لتغيي) STR_PURCHASE_INFO_DESIGNED_LIFE :{BLACK}تصميم: {GOLD}{NUM}{BLACK} العمر الافتراضي: {GOLD}{COMMA} سنة STR_PURCHASE_INFO_RELIABILITY :{BLACK}الاعتمادية القصوى: {GOLD}{COMMA}% STR_PURCHASE_INFO_COST :{BLACK}التكلفة: {GOLD}{CURRENCY_LONG} @@ -3193,7 +3194,7 @@ STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP :{BLACK} عرض STR_VEHICLE_DETAILS_TRAIN_ARTICULATED_RV_CAPACITY :{BLACK}السعة: {LTBLUE} # Vehicle refit -STR_REFIT_CAPTION :{WHITE}{VEHICLE} )تغيير( +STR_REFIT_CAPTION :{WHITE}{VEHICLE} (تغيير) STR_REFIT_TITLE :{GOLD}اختر نوع الحمولة ... STR_REFIT_NEW_CAPACITY_COST_OF_REFIT :{BLACK}السعة الجديدة: {GOLD}{CARGO_LONG}{}{BLACK}تكلفة التغيير: {RED}{CURRENCY_LONG} STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT :{BLACK}السعة الجديدة: {GOLD}{CARGO_LONG}, {GOLD}{CARGO_LONG}{}{BLACK}تكلفة اعادة التهيئة: {RED}{CURRENCY_LONG} @@ -3415,7 +3416,7 @@ STR_TIMETABLE_RESET_LATENESS :{BLACK}اعد STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}عدل وقت التاخير للعداد حتى تصل المركبة في الوقت المحدد STR_TIMETABLE_AUTOFILL :{BLACK}تهيئة تلقائية -STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}املأ الجدولة تلقائيا بقيم الرحلة التالية (مفتاح كنترول لمحاولة ابقائ وقت الانتظار). +STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}مفتاح كنترول لمحاولة ابقائ وقت الانتظار (املأ الجدولة تلقائيا بقيم الرحلة التالية) STR_TIMETABLE_EXPECTED :{BLACK}متوقع STR_TIMETABLE_SCHEDULED :{BLACK}مجدول diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index 5560ef785b..52215d57e7 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -4586,7 +4586,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... гэ STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... няправільны кірунак дарогі STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... на прахадных прыпынках нельга рабіць павароты STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... на прахадных прыпынках нельга рабіць скрыжаваньнi -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... дарога аднабаковая ці заблакавана # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Немагчыма выдаліць частку станцыi... diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 660e9caa7c..e6ac23a72a 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -4276,7 +4276,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... é u STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... rua na direção errada STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... paradas "drive-thru" não podem ter esquinas STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... paradas "drive-thru" não podem ter junções -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... rua é mão única ou está bloqueada # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Impossível remover parte da estação... diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index fa6b17868e..d262e1029f 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -4317,7 +4317,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... aque STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... sentit de la carretera en la direcció incorrecta STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... les estacions d'autobús de pas no poden tenir cantonades STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... les estacions d'autobús de pas no poden tenir interseccions -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la carretera és d'un sol sentit o està blocada. # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}No es pot treure part de la estació... diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 261b08cd16..e890c2fa26 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -4413,7 +4413,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ovo STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... cesta je orijentirana u krivom smjeru STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... prolazne postaje ne mogu imati zavoje STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... prolazne postaje ne mogu imati raskrižja -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... cesta je jednosmjerna ili je blokirana # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nije moguće ukloniti dio postaje... diff --git a/src/lang/czech.txt b/src/lang/czech.txt index 2e622b1422..77eb01f506 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -4339,7 +4339,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... tato STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... silnice je otočena jiným směrem STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... průjezdné zastávky nemohou být v zatáčce STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... průjezdné zastávky nemohou být na křižovatce -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... silnice je jednosměrná nebo uzavřená. # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nelze odstranit část stanice... diff --git a/src/lang/danish.txt b/src/lang/danish.txt index 28b0778a00..78ee9195b9 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -4317,7 +4317,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... denn STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... vejen peger i den forkerte retning STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... drive-through stops kan ikke have hjørner STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... drive-through stops kan ikke have kryds -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}vejen er ensrettet eller blokeret # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Kan ikke fjerne en del af en station... diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index ad2e043dd3..a36c69177d 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -4317,7 +4317,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... deze STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... weg is in de verkeerde richting STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... doorrij haltes kunnen geen bochten hebben STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... doorrij haltes kunnen geen kruisingen hebben -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... weg is eenrichtingsverkeer of geblokkeerd # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Kan deel van station niet verwijderen... diff --git a/src/lang/english.txt b/src/lang/english.txt index b4688d5e27..638fb2cdfa 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4317,7 +4317,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... this STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... road facing in the wrong direction STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... drive through stops can't have corners STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... drive through stops can't have junctions -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... road is one way or blocked # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Can't remove part of station... diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index f2c0c8d321..44a324578d 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -4317,7 +4317,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... this STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... road facing in the wrong direction STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... drive through stops can't have corners STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... drive through stops can't have junctions -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... road is one way or blocked # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Can't remove part of station... diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 458c9e9e15..a124d7d988 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -4284,7 +4284,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... see STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... tee on vales suunas STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... nurgad ei saa läbisõidupeatustes olla STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... ristmikud ei saa olla läbisõidupeatustes -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... tee on ühesuunaline või blokeeritud # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Siinset jaamablokki ei saa lammutada... diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index d8574e05e9..6e2cd8a19a 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -4317,7 +4317,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... kunt STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... tie on väärin päin STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... läpiajettavissa pysäkeissä ei voi olla mutkia STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... läpiajettavissa pysäkeissä ei voi olla risteyksiä -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... tie on yksisuuntainen tai suljettu # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Aseman osaa ei voi poistaa... diff --git a/src/lang/french.txt b/src/lang/french.txt index 18a6394b9f..b531327fca 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -4318,7 +4318,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... cett STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... mauvaise orientation de la route STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... les arrêts ne peuvent pas avoir de virages STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... les arrêts ne peuvent pas avoir de jonctions -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la route est à sens unique ou bloquée # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Impossible de supprimer une partie de la gare... diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index f2096687f1..58372f411a 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -4496,7 +4496,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... tha STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... tha comhair an rathaid cearr STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... chan urrainn dha dh’oiseanan a bhith aig stèiseanan draibhidh troimhe STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... chan urrainn dha ghoibhlean a bhith aig stèiseanan draibhidh troimhe -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... rathad aon-shligheach no bacte # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Cha ghabh pàirt dhen stèisean toirt air falbh... diff --git a/src/lang/german.txt b/src/lang/german.txt index 42490b2a39..7aee3c4960 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -4302,7 +4302,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... dies STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... Straße verläuft in die falsche Richtung STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... Bus- und Lkw-Haltestellen können nicht um die Kurve gehen STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... Bus- und Lkw-Haltestellen können keine Abzweigung haben -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}...Einbahnstraße oder blockierter Weg # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Dieser Teil der Station kann nicht entfernt werden... diff --git a/src/lang/greek.txt b/src/lang/greek.txt index 7ea02340e4..7e1f21ef57 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -4430,7 +4430,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... αυ STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... ο δρόμος βλέπει σε λάθος κατεύθυνση STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν στροφές STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν διασταυρώσεις -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... ο δρόμος είναι μονόδρομος η μπλοκαρισμένος # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Δεν μπορεί να αφαιρεθεί μέρος του σταθμού... diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index 05fb5fe1d7..9be20a1f09 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -4275,7 +4275,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... כב STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}הכביש פונה לכיוון לא נכון... STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... לתחנות "על הדרך" לא יכולות להיות פינות STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... לתחנות "על הדרך" לא יכולות להיות צמתים -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... הדרך חד כיוונית או חסומה # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}... לא ניתן לבטל חלק מהתחנה diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index c66e0fdf4f..e591a71288 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -4381,7 +4381,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ez e STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... az út a másik irányba vezet STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... áthaladó megállóhelyeken nem lehet kanyar STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... áthaladó megállóhelyeken nem lehet elágazás -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... az út egyirányú vagy blokkolt # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nem lehet eltávolítani az állomás részét... diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index fe1991d835..c8230703ce 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -4249,7 +4249,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... jala STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... menghadap pada arah yang salah STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... terminal lintas-lalu tak bisa memiliki sudut STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... terminal lintas-lalu tak bisa memiliki simpangan -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... jalannya satu arah atau terhalang # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Tidak dapat menghapus bagian dari stasiun... diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 3f1014cbf8..3afee136d7 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -4347,7 +4347,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ques STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... strada rivolta nella direzione sbagliata STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... le fermate passanti non possono trovarsi in curva STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... le fermate passanti non possono avere raccordi -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la strada è bloccata o a senso unico # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Impossibile rimuovere parte della stazione... diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 755e3a8500..c2250b31f9 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -4318,7 +4318,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... 여 STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... 도로의 방향과 일치하지 않습니다. STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... 도로 통과 정류장은 곡선도로에 건설할 수 없습니다. STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... 도로 통과 정류장은 교차로에 건설할 수 없습니다. -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... 도로가 일방통행이거나 막혔습니다 # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}역의 일부를 제거할 수 없습니다... diff --git a/src/lang/latin.txt b/src/lang/latin.txt index 77ff4614cc..a1ca538b6c 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -4479,7 +4479,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... via STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... viae directio non convenit STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... stationi perviae non licet esse curva STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... stationi perviae non licet compita habere -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... via est monodromus vel obstructa # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Non licet partem stationis removere... diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index b52b60dde5..929a4bccb2 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -4276,7 +4276,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... d'St STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... Strooss geet an dei falsch Richtung STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... Duerchfahrtstops kënnen keng Kéiren hunn STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... Duerchfahrtstops kënnen keng Kräizungen hunn -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... Einbahnstrooss oder blockéiert # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Kann den Deel vun der Gare net ofrappen... diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 1d63e6fb0e..325f7daad4 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -4322,7 +4322,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... denn STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... veien vender i feil retning STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... gjennomkjøringsstopper kan ikke ha hjørner STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... gjennomkjøringsstopper kan ikke ha kryss -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... veien er enveiskjørt eller blokkert # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Kan ikke fjerne del av stasjonen... diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 637b5ab6ea..76ee7225aa 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -4666,7 +4666,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ta d STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... droga jest zorientowana w złym kierunku STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... przystanki przelotowe nie mogą mieć zakrętów STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... przystanki przelotowe nie mogą mieć skrzyżowań -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... droga jest jednokierunkowa lub zablokowana # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nie można usunąć części stacji... diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 74b40844d9..95a21d3320 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -4318,7 +4318,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... esta STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... estrada orientada na direcção incorrecta STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... estações de passagem não podem ter curvas STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... estações de passagem não podem ter cruzamentos -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... estrada de sentido único ou bloqueada # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Não é possível remover parte da estação... diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 21542eb5f5..40cb1c794c 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -4507,7 +4507,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... эт STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... неверное направление дороги STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... на проходных остановках нельзя делать повороты STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... на проходных остановках нельзя делать перекрёстки -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... дорога односторонняя или заблокирована # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Невозможно удалить часть станции... diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index 20a813b38a..38e4f9f038 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -4443,7 +4443,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ova STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... ulica je okrenuta u drugom pravcu STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... stajalište ne može biti na krivini STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... stajalište ne može biti na raskrsnici -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... put je jednosmeran ili blokiran. # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Deo stanice se ne može ukloniti... diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 79a0dfac19..48a01a3fb6 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -4268,7 +4268,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... esta STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... carretera en la dirección incorrecta STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... las estaciones de autobús de paso no pueden tener esquinas STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... las estaciones de autobús de paso no pueden tener intersecciones -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... carretera de un solo sentido o bloqueada # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}No se puede retirar parte de la estación... diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 7ce9fb2d2f..a62689c98f 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -4318,7 +4318,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... esta STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... carretera en el sentido incorrecto STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... las estaciones y paradas intermedias no pueden ponerse sobre esquinas STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... las estaciones y paradas intermedias no pueden ponerse sobre intersecciones -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la carretera está bloqueada o es de un solo sentido # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}No se puede quitar parte de la estación... diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 03df018123..9f5ea5f98f 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -4301,7 +4301,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... dett STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... vägen pekar i fel riktning STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... genomfartshållplatser kan inte ha gatuhörn STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... genomfartshållplatser kan inte ha korsningar -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... vägen är enkelriktad eller blockerad # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Kan ej ta bort del av station... diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index c9e17a9cce..c53547ac44 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -4310,7 +4310,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... bu y STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... yol yanlış yönde STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... yol üstü duraklar köşe üzerine inşa edilemez STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... yol üstü duraklar kavşak üzerine inşa edilemez -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... yol tek yön ya da engellenmiş # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}İstasyonun parçasi kaldırılamaz... diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index ffd1d135c8..dd6a4a5809 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -4446,7 +4446,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ця STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... дорога не в тому напрямку STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... проїзні зупинки не можуть мати поворотів STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... проїзні зупинки не можуть мати перехресть -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... дорога одностороння або заблокована # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Не можна зруйнувати частину станції... diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index 6b3c288342..2eef87f4cd 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -4279,7 +4279,6 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... đâ STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... đường quay mặt sai hướng STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... đi qua điểm dừng không thể đi qua góc STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... đi qua điểm dừng không thể có ngã rẽ -STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... đường 1 chiều hoặc bị chặn # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Không thể xoá bỏ một phần của ga... diff --git a/src/main_gui.cpp b/src/main_gui.cpp index cdd3831576..42a2a7eff4 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -205,6 +205,16 @@ void ZoomInOrOutToCursorWindow(bool in, Window *w) } } +void FixTitleGameZoom() +{ + if (_game_mode != GM_MENU) return; + + ViewPort *vp = FindWindowByClass(WC_MAIN_WINDOW)->viewport; + vp->zoom = _gui_zoom; + vp->virtual_width = ScaleByZoom(vp->width, vp->zoom); + vp->virtual_height = ScaleByZoom(vp->height, vp->zoom); +} + static const struct NWidgetPart _nested_main_window_widgets[] = { NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_M_VIEWPORT), SetResize(1, 1), }; diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index fece709fda..47b3db19ae 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -538,19 +538,19 @@ static void TransmitChannelMsg(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, by } } -static void TransmitSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, byte *&msg_start, size_t &remaining) +static void TransmitSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, const byte *&msg_start, size_t &remaining) { /* Find end of message. */ - byte *msg_end = msg_start; + const byte *msg_end = msg_start; while (*msg_end != MIDIST_ENDSYSEX) msg_end++; msg_end++; // Also include SysEx end byte. - if (buffer->PackUnstructured(rt, 0, msg_end - msg_start, msg_start) == E_OUTOFMEMORY) { + if (buffer->PackUnstructured(rt, 0, msg_end - msg_start, const_cast(msg_start)) == E_OUTOFMEMORY) { /* Buffer is full, clear it and try again. */ _port->PlayBuffer(buffer); buffer->Flush(); - buffer->PackUnstructured(rt, 0, msg_end - msg_start, msg_start); + buffer->PackUnstructured(rt, 0, msg_end - msg_start, const_cast(msg_start)); } /* Update position in buffer. */ @@ -558,9 +558,11 @@ static void TransmitSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, byte *& msg_start = msg_end; } -static void TransmitSysexConst(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, byte *msg_start, size_t length) +static void TransmitStandardSysex(IDirectMusicBuffer *buffer, REFERENCE_TIME rt, MidiSysexMessage msg) { - TransmitSysex(buffer, rt, msg_start, length); + size_t length = 0; + const byte *data = MidiGetStandardSysexMessage(msg, length); + TransmitSysex(buffer, rt, data, length); } /** Transmit 'Note off' messages to all MIDI channels. */ @@ -571,27 +573,16 @@ static void TransmitNotesOff(IDirectMusicBuffer *buffer, REFERENCE_TIME block_ti TransmitChannelMsg(_buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_SUSTAINSW, 0); TransmitChannelMsg(_buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_MODE_RESETALLCTRL, 0); } - /* Explicitly flush buffer to make sure the note off messages are processed - * before we send any additional control messages. */ + + /* Performing a GM reset stops all sound and resets all parameters. */ + TransmitStandardSysex(_buffer, block_time + 20, MidiSysexMessage::ResetGM); + TransmitStandardSysex(_buffer, block_time + 30, MidiSysexMessage::RolandSetReverb); + + /* Explicitly flush buffer to make sure the messages are processed, + * as we want sound to stop immediately. */ _port->PlayBuffer(_buffer); _buffer->Flush(); - /* Some songs change the "Pitch bend range" registered parameter. If - * this doesn't get reset, everything else will start sounding wrong. */ - for (int ch = 0; ch < 16; ch++) { - /* Running status, only need status for first message - * Select RPN 00.00, set value to 02.00, and de-select again */ - TransmitChannelMsg(_buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_RPN_SELECT_LO, 0x00); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_RPN_SELECT_HI, 0x00); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_DATAENTRY, 0x02); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_DATAENTRY_LO, 0x00); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_RPN_SELECT_LO, 0x7F); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_RPN_SELECT_HI, 0x7F); - - _port->PlayBuffer(_buffer); - _buffer->Flush(); - } - /* Wait until message time has passed. */ Sleep(Clamp((block_time - cur_time) / MS_TO_REFTIME, 5, 1000)); } @@ -616,13 +607,6 @@ static void MidiThreadProc(void *) REFERENCE_TIME cur_time; clock->GetTime(&cur_time); - /* Standard "Enable General MIDI" message */ - static byte gm_enable_sysex[] = { 0xF0, 0x7E, 0x00, 0x09, 0x01, 0xF7 }; - TransmitSysexConst(_buffer, cur_time, &gm_enable_sysex[0], sizeof(gm_enable_sysex)); - /* Roland-specific reverb room control, used by the original game */ - static byte roland_reverb_sysex[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x30, 0x02, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x09, 0xF7 }; - TransmitSysexConst(_buffer, cur_time, &roland_reverb_sysex[0], sizeof(roland_reverb_sysex)); - _port->PlayBuffer(_buffer); _buffer->Flush(); @@ -665,7 +649,7 @@ static void MidiThreadProc(void *) _playback.do_start = false; } - /* Turn all notes off in case we are seeking between music titles. */ + /* Reset playback device between songs. */ clock->GetTime(&cur_time); TransmitNotesOff(_buffer, block_time, cur_time); @@ -751,7 +735,7 @@ static void MidiThreadProc(void *) block_time = playback_start_time + block.realtime * MIDITIME_TO_REFTIME; DEBUG(driver, 9, "DMusic thread: Streaming block " PRINTF_SIZE " (cur=" OTTD_PRINTF64 ", block=" OTTD_PRINTF64 ")", current_block, (long long)(current_time / MS_TO_REFTIME), (long long)(block_time / MS_TO_REFTIME)); - byte *data = block.data.Begin(); + const byte *data = block.data.Begin(); size_t remaining = block.data.Length(); byte last_status = 0; while (remaining > 0) { diff --git a/src/music/midi.h b/src/music/midi.h index 473f7f18bb..90f04435e6 100644 --- a/src/music/midi.h +++ b/src/music/midi.h @@ -141,4 +141,19 @@ enum MidiController { MIDICT_MODE_POLY = 127, }; + +/** Well-known MIDI system exclusive message values for use with the MidiGetStandardSysexMessage function. */ +enum class MidiSysexMessage { + /** Reset device to General MIDI defaults */ + ResetGM, + /** Reset device to (Roland) General Standard defaults */ + ResetGS, + /** Reset device to (Yamaha) XG defaults */ + ResetXG, + /** Set up Roland SoundCanvas reverb room as TTD does */ + RolandSetReverb, +}; + +const byte *MidiGetStandardSysexMessage(MidiSysexMessage msg, size_t &length); + #endif /* MUSIC_MIDI_H */ diff --git a/src/music/midifile.cpp b/src/music/midifile.cpp index 91f83c529d..eb5499426d 100644 --- a/src/music/midifile.cpp +++ b/src/music/midifile.cpp @@ -27,6 +27,37 @@ static MidiFile *_midifile_instance = NULL; +/** + * Retrieve a well-known MIDI system exclusive message. + * @param msg Which sysex message to retrieve + * @param[out] length Receives the length of the returned buffer + * @return Pointer to byte buffer with sysex message + */ +const byte *MidiGetStandardSysexMessage(MidiSysexMessage msg, size_t &length) +{ + static byte reset_gm_sysex[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 }; + static byte reset_gs_sysex[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 }; + static byte reset_xg_sysex[] = { 0xF0, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 }; + static byte roland_reverb_sysex[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x30, 0x02, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x09, 0xF7 }; + + switch (msg) { + case MidiSysexMessage::ResetGM: + length = lengthof(reset_gm_sysex); + return reset_gm_sysex; + case MidiSysexMessage::ResetGS: + length = lengthof(reset_gs_sysex); + return reset_gs_sysex; + case MidiSysexMessage::ResetXG: + length = lengthof(reset_xg_sysex); + return reset_xg_sysex; + case MidiSysexMessage::RolandSetReverb: + length = lengthof(roland_reverb_sysex); + return roland_reverb_sysex; + default: + NOT_REACHED(); + } +} + /** * Owning byte buffer readable as a stream. * RAII-compliant to make teardown in error situations easier. diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index a32318db12..abe2aa31b4 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -35,7 +35,7 @@ static struct { CRITICAL_SECTION lock; ///< synchronization for playback status fields bool playing; ///< flag indicating that playback is active - bool do_start; ///< flag for starting playback of next_file at next opportunity + int do_start; ///< flag for starting playback of next_file at next opportunity bool do_stop; ///< flag for stopping playback at next opportunity byte current_volume; ///< current effective volume setting byte new_volume; ///< volume setting to change to @@ -73,10 +73,10 @@ static void TransmitChannelMsg(byte status, byte p1, byte p2 = 0) midiOutShortMsg(_midi.midi_out, status | (p1 << 8) | (p2 << 16)); } -static void TransmitSysex(byte *&msg_start, size_t &remaining) +static void TransmitSysex(const byte *&msg_start, size_t &remaining) { /* find end of message */ - byte *msg_end = msg_start; + const byte *msg_end = msg_start; while (*msg_end != MIDIST_ENDSYSEX) msg_end++; msg_end++; /* also include sysex end byte */ @@ -97,9 +97,11 @@ static void TransmitSysex(byte *&msg_start, size_t &remaining) msg_start = msg_end; } -static void TransmitSysexConst(byte *msg_start, size_t length) +static void TransmitStandardSysex(MidiSysexMessage msg) { - TransmitSysex(msg_start, length); + size_t length = 0; + const byte *data = MidiGetStandardSysexMessage(msg, length); + TransmitSysex(data, length); } /** @@ -108,82 +110,94 @@ static void TransmitSysexConst(byte *msg_start, size_t length) */ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DWORD_PTR) { - /* Try to check playback status changes. - * If _midi is already locked, skip checking for this cycle and try again - * next cycle, instead of waiting for locks in the realtime callback. */ - if (TryEnterCriticalSection(&_midi.lock)) { - /* check for stop */ - if (_midi.do_stop) { - DEBUG(driver, 2, "Win32-MIDI: timer: do_stop is set"); - midiOutReset(_midi.midi_out); - _midi.playing = false; - _midi.do_stop = false; + /* Ensure only one timer callback is running at once, and prevent races on status flags */ + if (!TryEnterCriticalSection(&_midi.lock)) return; + + /* check for stop */ + if (_midi.do_stop) { + DEBUG(driver, 2, "Win32-MIDI: timer: do_stop is set"); + midiOutReset(_midi.midi_out); + _midi.playing = false; + _midi.do_stop = false; + LeaveCriticalSection(&_midi.lock); + return; + } + + /* check for start/restart/change song */ + if (_midi.do_start != 0) { + /* Have a delay between playback start steps, prevents jumbled-together notes at the start of song */ + if (timeGetTime() - _midi.playback_start_time < 50) { LeaveCriticalSection(&_midi.lock); return; } + DEBUG(driver, 2, "Win32-MIDI: timer: do_start step %d", _midi.do_start); - /* check for start/restart/change song */ - if (_midi.do_start) { - DEBUG(driver, 2, "Win32-MIDI: timer: do_start is set"); - if (_midi.playing) { - midiOutReset(_midi.midi_out); - /* Some songs change the "Pitch bend range" registered - * parameter. If this doesn't get reset, everything else - * will start sounding wrong. */ - for (int ch = 0; ch < 16; ch++) { - /* Running status, only need status for first message */ - /* Select RPN 00.00, set value to 02.00, and unselect again */ - TransmitChannelMsg(MIDIST_CONTROLLER | ch, MIDICT_RPN_SELECT_LO, 0x00); - TransmitChannelMsg(MIDICT_RPN_SELECT_HI, 0x00); - TransmitChannelMsg(MIDICT_DATAENTRY, 0x02); - TransmitChannelMsg(MIDICT_DATAENTRY_LO, 0x00); - TransmitChannelMsg(MIDICT_RPN_SELECT_LO, 0x7F); - TransmitChannelMsg(MIDICT_RPN_SELECT_HI, 0x7F); - } - } + if (_midi.do_start == 1) { + /* Send "all notes off" */ + midiOutReset(_midi.midi_out); + _midi.playback_start_time = timeGetTime(); + _midi.do_start = 2; + + LeaveCriticalSection(&_midi.lock); + return; + } else if (_midi.do_start == 2) { + /* Reset the device to General MIDI defaults */ + TransmitStandardSysex(MidiSysexMessage::ResetGM); + _midi.playback_start_time = timeGetTime(); + _midi.do_start = 3; + + LeaveCriticalSection(&_midi.lock); + return; + } else if (_midi.do_start == 3) { + /* Set up device-specific effects */ + TransmitStandardSysex(MidiSysexMessage::RolandSetReverb); + _midi.playback_start_time = timeGetTime(); + _midi.do_start = 4; + + LeaveCriticalSection(&_midi.lock); + return; + } else if (_midi.do_start == 4) { + /* Load the new file */ _midi.current_file.MoveFrom(_midi.next_file); std::swap(_midi.next_segment, _midi.current_segment); _midi.current_segment.start_block = 0; _midi.playback_start_time = timeGetTime(); _midi.playing = true; - _midi.do_start = false; + _midi.do_start = 0; _midi.current_block = 0; MemSetT(_midi.channel_volumes, 127, lengthof(_midi.channel_volumes)); - } else if (!_midi.playing) { - /* not playing, stop the timer */ - DEBUG(driver, 2, "Win32-MIDI: timer: not playing, stopping timer"); - timeKillEvent(uTimerID); - _midi.timer_id = 0; - LeaveCriticalSection(&_midi.lock); - return; } - - /* check for volume change */ - static int volume_throttle = 0; - if (_midi.current_volume != _midi.new_volume) { - if (volume_throttle == 0) { - DEBUG(driver, 2, "Win32-MIDI: timer: volume change"); - _midi.current_volume = _midi.new_volume; - volume_throttle = 20 / _midi.time_period; - for (int ch = 0; ch < 16; ch++) { - int vol = ScaleVolume(_midi.channel_volumes[ch], _midi.current_volume); - TransmitChannelMsg(MIDIST_CONTROLLER | ch, MIDICT_CHANVOLUME, vol); - } - } - else { - volume_throttle--; - } - } - + } else if (!_midi.playing) { + /* not playing, stop the timer */ + DEBUG(driver, 2, "Win32-MIDI: timer: not playing, stopping timer"); + timeKillEvent(uTimerID); + _midi.timer_id = 0; LeaveCriticalSection(&_midi.lock); + return; + } + + /* check for volume change */ + static int volume_throttle = 0; + if (_midi.current_volume != _midi.new_volume) { + if (volume_throttle == 0) { + DEBUG(driver, 2, "Win32-MIDI: timer: volume change"); + _midi.current_volume = _midi.new_volume; + volume_throttle = 20 / _midi.time_period; + for (int ch = 0; ch < 16; ch++) { + byte vol = ScaleVolume(_midi.channel_volumes[ch], _midi.current_volume); + TransmitChannelMsg(MIDIST_CONTROLLER | ch, MIDICT_CHANVOLUME, vol); + } + } else { + volume_throttle--; + } } /* skip beginning of file? */ if (_midi.current_segment.start > 0 && _midi.current_block == 0 && _midi.current_segment.start_block == 0) { /* find first block after start time and pretend playback started earlier - * this is to allow all blocks prior to the actual start to still affect playback, - * as they may contain important controller and program changes */ + * this is to allow all blocks prior to the actual start to still affect playback, + * as they may contain important controller and program changes */ uint preload_bytes = 0; for (size_t bl = 0; bl < _midi.current_file.blocks.size(); bl++) { MidiFile::DataBlock &block = _midi.current_file.blocks[bl]; @@ -200,7 +214,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW * The delay compensation is needed to avoid time-compression of following messages. */ DEBUG(driver, 2, "Win32-MIDI: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes); - _midi.playback_start_time -= block.realtime / 1000 - preload_bytes * 1000 / 3125; + _midi.playback_start_time -= block.realtime / 1000 - (DWORD)(preload_bytes * 1000 / 3125); break; } } @@ -229,7 +243,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW break; } - byte *data = block.data.Begin(); + const byte *data = block.data.Begin(); size_t remaining = block.data.Length(); byte last_status = 0; while (remaining > 0) { @@ -308,25 +322,28 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW _midi.do_stop = true; } } + + LeaveCriticalSection(&_midi.lock); } void MusicDriver_Win32::PlaySong(const MusicSongInfo &song) { DEBUG(driver, 2, "Win32-MIDI: PlaySong: entry"); + + MidiFile new_song; + if (!new_song.LoadSong(song)) return; + DEBUG(driver, 2, "Win32-MIDI: PlaySong: Loaded song"); + EnterCriticalSection(&_midi.lock); - if (!_midi.next_file.LoadSong(song)) { - LeaveCriticalSection(&_midi.lock); - return; - } - + _midi.next_file.MoveFrom(new_song); _midi.next_segment.start = song.override_start; _midi.next_segment.end = song.override_end; _midi.next_segment.loop = song.loop; DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag"); _midi.do_stop = _midi.playing; - _midi.do_start = true; + _midi.do_start = 1; if (_midi.timer_id == 0) { DEBUG(driver, 2, "Win32-MIDI: PlaySong: starting timer"); @@ -347,7 +364,7 @@ void MusicDriver_Win32::StopSong() bool MusicDriver_Win32::IsSongPlaying() { - return _midi.playing || _midi.do_start; + return _midi.playing || (_midi.do_start != 0); } void MusicDriver_Win32::SetVolume(byte vol) @@ -381,14 +398,6 @@ const char *MusicDriver_Win32::Start(const char * const *parm) midiOutReset(_midi.midi_out); - /* Standard "Enable General MIDI" message */ - static byte gm_enable_sysex[] = { 0xF0, 0x7E, 0x00, 0x09, 0x01, 0xF7 }; - TransmitSysexConst(&gm_enable_sysex[0], sizeof(gm_enable_sysex)); - - /* Roland-specific reverb room control, used by the original game */ - static byte roland_reverb_sysex[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x30, 0x02, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x09, 0xF7 }; - TransmitSysexConst(&roland_reverb_sysex[0], sizeof(roland_reverb_sysex)); - /* prepare multimedia timer */ TIMECAPS timecaps; if (timeGetDevCaps(&timecaps, sizeof(timecaps)) == MMSYSERR_NOERROR) { diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 9ec93e7e11..4c110c6b4f 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5480,7 +5480,8 @@ static void RailTypeMapSpriteGroup(ByteReader *buf, uint8 idcount) { uint8 *railtypes = AllocaM(uint8, idcount); for (uint i = 0; i < idcount; i++) { - railtypes[i] = _cur.grffile->railtype_map[buf->ReadByte()]; + uint8 id = buf->ReadByte(); + railtypes[i] = id < RAILTYPE_END ? _cur.grffile->railtype_map[id] : INVALID_RAILTYPE; } uint8 cidcount = buf->ReadByte(); diff --git a/src/newgrf.h b/src/newgrf.h index 1ab55dd044..27970dece8 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -163,7 +163,7 @@ struct GRFLoadedFeatures { uint64 used_liveries; ///< Bitmask of #LiveryScheme used by the defined engines. bool has_newhouses; ///< Set if there are any newhouses loaded. bool has_newindustries; ///< Set if there are any newindustries loaded. - ShoreReplacement shore; ///< It which way shore sprites were replaced. + ShoreReplacement shore; ///< In which way shore sprites were replaced. }; /** diff --git a/src/news_gui.cpp b/src/news_gui.cpp index b79418c899..f466c96852 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -45,8 +45,8 @@ const NewsItem *_statusbar_news_item = NULL; static uint MIN_NEWS_AMOUNT = 30; ///< preferred minimum amount of news messages static uint _total_news = 0; ///< current number of news items -NewsItem *_oldest_news = NULL; ///< head of news items queue -static NewsItem *_latest_news = NULL; ///< tail of news items queue +static NewsItem *_oldest_news = NULL; ///< head of news items queue +NewsItem *_latest_news = NULL; ///< tail of news items queue /** * Forced news item. @@ -54,7 +54,7 @@ static NewsItem *_latest_news = NULL; ///< tail of news items queue * If the message being shown was forced by the user, a pointer is stored * in _forced_news. Otherwise, \a _forced_news variable is NULL. */ -static const NewsItem *_forced_news = NULL; ///< item the user has asked for +static const NewsItem *_forced_news = NULL; /** Current news item (last item shown regularly). */ static const NewsItem *_current_news = NULL; @@ -331,6 +331,11 @@ struct NewsWindow : Window { *size = maxdim(*size, GetSpriteSize(SPR_GRADIENT)); break; + case WID_N_MGR_NAME: + SetDParamStr(0, static_cast(this->ni->free_data)->president_name); + str = STR_JUST_RAW_STRING; + break; + case WID_N_MESSAGE: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); str = this->ni->string_id; diff --git a/src/news_gui.h b/src/news_gui.h index 0f42c68c6c..569a8b8a74 100644 --- a/src/news_gui.h +++ b/src/news_gui.h @@ -17,6 +17,6 @@ void ShowLastNewsMessage(); void ShowMessageHistory(); -extern NewsItem *_oldest_news; +extern NewsItem *_latest_news; #endif /* NEWS_GUI_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index 6265b96849..077838cafd 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -62,6 +62,7 @@ #include "town.h" #include "subsidy_func.h" #include "gfx_layout.h" +#include "viewport_func.h" #include "viewport_sprite_sorter.h" #include "framerate_type.h" @@ -339,6 +340,7 @@ static void LoadIntroGame(bool load_newgrfs = true) SetLocalCompany(COMPANY_FIRST); } + FixTitleGameZoom(); _pause_mode = PM_UNPAUSED; _cursor.fix_at = false; diff --git a/src/os/macosx/crashlog_osx.cpp b/src/os/macosx/crashlog_osx.cpp index ad258bbc91..35e47cb061 100644 --- a/src/os/macosx/crashlog_osx.cpp +++ b/src/os/macosx/crashlog_osx.cpp @@ -203,7 +203,7 @@ public: char message[1024]; seprintf(message, lastof(message), "Please send the generated crash information and the last (auto)save to the developers. " - "This will greatly help debugging. The correct place to do this is http://bugs.openttd.org.\n\n" + "This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues.\n\n" "Generated file(s):\n%s\n%s\n%s", this->filename_log, this->filename_save, this->filename_screenshot); diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index 1abb0e725c..ded4020d99 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -582,7 +582,7 @@ static bool _expanded; static const TCHAR _crash_desc[] = _T("A serious fault condition occurred in the game. The game will shut down.\n") _T("Please send the crash information and the crash.dmp file (if any) to the developers.\n") - _T("This will greatly help debugging. The correct place to do this is http://bugs.openttd.org. ") + _T("This will greatly help debugging. The correct place to do this is https://github.com/OpenTTD/OpenTTD/issues. ") _T("The information contained in the report is displayed below.\n") _T("Press \"Emergency save\" to attempt saving the game. Generated file(s):\n") _T("%s"); diff --git a/src/os/windows/ottdres.rc.in b/src/os/windows/ottdres.rc.in index 6aa7ce216c..25f0bfd45a 100644 --- a/src/os/windows/ottdres.rc.in +++ b/src/os/windows/ottdres.rc.in @@ -79,8 +79,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,9,1,!!ISODATE!! - PRODUCTVERSION 1,9,1,!!ISODATE!! + FILEVERSION 1,9,2,!!ISODATE!! + PRODUCTVERSION 1,9,2,!!ISODATE!! FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 3230d9bf22..ef1137e297 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1565,6 +1565,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert. + bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633) TileIterator *iter = diagonal ? (TileIterator *)new DiagonalTileIterator(area_start, area_end) : new OrthogonalTileIterator(area_start, area_end); for (; (tile = *iter) != INVALID_TILE; ++(*iter)) { @@ -1660,6 +1661,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 InvalidateWindowData(WC_VEHICLE_DEPOT, tile); InvalidateWindowData(WC_BUILD_VEHICLE, tile); } + found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype)); break; @@ -1671,6 +1673,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); } } + found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile))); break; } @@ -1733,6 +1736,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } } + found_convertible_track = true; cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype)); break; } @@ -1743,6 +1747,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 YapfNotifyTrackLayoutChange(tile, track); } + found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype)); break; } @@ -1760,7 +1765,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } delete iter; - return (cost.GetCost() == 0) ? error : cost; + return found_convertible_track ? cost : error; } static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) diff --git a/src/rev.cpp b/src/rev.cpp index cbc3013e88..6cf36b344c 100644 --- a/src/rev.cpp +++ b/src/rev.cpp @@ -37,7 +37,7 @@ bool IsReleasedVersion() * * shows a "M", if the binary is made from modified source code. */ -const char _openttd_revision[] = "1.9.1"; +const char _openttd_revision[] = "1.9.2"; /** * The text version of OpenTTD's build date. @@ -50,7 +50,7 @@ const char _openttd_build_date[] = __DATE__ " " __TIME__; /** * The git revision hash of this version. */ -const char _openttd_revision_hash[] = "b61ef7e0d2e11e6f8fa963c9459e918b24afc8b5"; +const char _openttd_revision_hash[] = "a82f21f24df9f42aea38af9fb48da553b30432f4"; /** * Let us know if current build was modified. This detection @@ -82,11 +82,11 @@ const byte _openttd_revision_tagged = 1; * 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 | 9 << 24 | 1 << 20 | 1 << 19 | 28004; +const uint32 _openttd_newgrf_version = 1 << 28 | 9 << 24 | 2 << 20 | 1 << 19 | 28004; #ifdef __MORPHOS__ /** * Variable used by MorphOS to show the version. */ -extern const char morphos_versions_tag[] = "$VER: OpenTTD 1.9.1 (08.04.19) OpenTTD Team [MorphOS, PowerPC]"; +extern const char morphos_versions_tag[] = "$VER: OpenTTD 1.9.2 (09.07.19) OpenTTD Team [MorphOS, PowerPC]"; #endif diff --git a/src/rev.cpp.in b/src/rev.cpp.in index e77fee48ac..0fb8db7aaf 100644 --- a/src/rev.cpp.in +++ b/src/rev.cpp.in @@ -82,7 +82,7 @@ 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 | 9 << 24 | 1 << 20 | !!ISSTABLETAG!! << 19 | 28004; +const uint32 _openttd_newgrf_version = 1 << 28 | 9 << 24 | 2 << 20 | !!ISSTABLETAG!! << 19 | 28004; #ifdef __MORPHOS__ /** diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 96def205c5..effe4df690 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -1132,6 +1132,38 @@ bool AfterLoadGame() } } + /* Railtype moved from m3 to m8 in version SLV_EXTEND_RAILTYPES. */ + if (IsSavegameVersionBefore(SLV_EXTEND_RAILTYPES)) { + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_RAILWAY: + SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + break; + + case MP_ROAD: + if (IsLevelCrossing(t)) { + SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + } + break; + + case MP_STATION: + if (HasStationRail(t)) { + SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + } + break; + + case MP_TUNNELBRIDGE: + if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { + SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + } + break; + + default: + break; + } + } + } + if (IsSavegameVersionBefore(SLV_42)) { Vehicle *v; @@ -1213,38 +1245,6 @@ bool AfterLoadGame() } } - /* Railtype moved from m3 to m8 in version SLV_EXTEND_RAILTYPES. */ - if (IsSavegameVersionBefore(SLV_EXTEND_RAILTYPES)) { - for (TileIndex t = 0; t < map_size; t++) { - switch (GetTileType(t)) { - case MP_RAILWAY: - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); - break; - - case MP_ROAD: - if (IsLevelCrossing(t)) { - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); - } - break; - - case MP_STATION: - if (HasStationRail(t)) { - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); - } - break; - - case MP_TUNNELBRIDGE: - if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); - } - break; - - default: - break; - } - } - } - /* Elrails got added in rev 24 */ if (IsSavegameVersionBefore(SLV_24)) { RailType min_rail = RAILTYPE_ELECTRIC; @@ -1893,6 +1893,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_62)) { + GroupStatistics::UpdateAfterLoad(); // Ensure statistics pool is initialised before trying to delete vehicles /* Remove all trams from savegames without tram support. * There would be trams without tram track under causing crashes sooner or later. */ RoadVehicle *v; @@ -2304,6 +2305,14 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_128)) { const Depot *d; FOR_ALL_DEPOTS(d) { + /* At some point, invalid depots were saved into the game (possibly those removed in the past?) + * Remove them here, so they don't cause issues further down the line */ + if (!IsDepotTile(d->xy)) { + DEBUG(sl, 0, "Removing invalid depot %d at %d, %d", d->index, TileX(d->xy), TileY(d->xy)); + delete d; + d = nullptr; + continue; + } _m[d->xy].m2 = d->index; if (IsTileType(d->xy, MP_WATER)) _m[GetOtherShipDepotTile(d->xy)].m2 = d->index; } diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp index 76390a0101..50a0b62e69 100644 --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -233,7 +233,8 @@ void AfterLoadLinkGraphs() LinkGraph *lg; FOR_ALL_LINK_GRAPHS(lg) { for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { - (*lg)[node_id].UpdateLocation(Station::Get((*lg)[node_id].Station())->xy); + const Station *st = Station::GetIfValid((*lg)[node_id].Station()); + if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); } } @@ -241,7 +242,8 @@ void AfterLoadLinkGraphs() FOR_ALL_LINK_GRAPH_JOBS(lgj) { lg = &(const_cast(lgj->Graph())); for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { - (*lg)[node_id].UpdateLocation(Station::Get((*lg)[node_id].Station())->xy); + const Station *st = Station::GetIfValid((*lg)[node_id].Station()); + if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); } } } diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index bff85dfc61..ab4b1e2a1e 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -2889,36 +2889,3 @@ void FileToSaveLoad::SetTitle(const char *title) { strecpy(this->title, title, lastof(this->title)); } - -#if 0 -/** - * Function to get the type of the savegame by looking at the file header. - * NOTICE: Not used right now, but could be used if extensions of savegames are garbled - * @param file Savegame to be checked - * @return SL_OLD_LOAD or SL_LOAD of the file - */ -int GetSavegameType(char *file) -{ - const SaveLoadFormat *fmt; - uint32 hdr; - FILE *f; - int mode = SL_OLD_LOAD; - - f = fopen(file, "rb"); - if (fread(&hdr, sizeof(hdr), 1, f) != 1) { - DEBUG(sl, 0, "Savegame is obsolete or invalid format"); - mode = SL_LOAD; // don't try to get filename, just show name as it is written - } else { - /* see if we have any loader for this type. */ - for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) { - if (fmt->tag == hdr) { - mode = SL_LOAD; // new type of savegame - break; - } - } - } - - fclose(f); - return mode; -} -#endif diff --git a/src/script/api/script_goal.cpp b/src/script/api/script_goal.cpp index c183b75834..517bddfd8d 100644 --- a/src/script/api/script_goal.cpp +++ b/src/script/api/script_goal.cpp @@ -109,7 +109,7 @@ return g != NULL && g->completed; } -/* static */ bool ScriptGoal::DoQuestion(uint16 uniqueid, uint8 target, bool is_client, Text *question, QuestionType type, int buttons) +/* static */ bool ScriptGoal::DoQuestion(uint16 uniqueid, uint32 target, bool is_client, Text *question, QuestionType type, uint32 buttons) { CCountedPtr counter(question); @@ -121,7 +121,7 @@ EnforcePrecondition(false, buttons < (1 << ::GOAL_QUESTION_BUTTON_COUNT)); EnforcePrecondition(false, (int)type < ::GOAL_QUESTION_TYPE_COUNT); - return ScriptObject::DoCommand(0, uniqueid | (target << 16) | (type << 24) | (is_client ? (1 << 31) : 0), buttons, CMD_GOAL_QUESTION, text); + return ScriptObject::DoCommand(0, uniqueid | (target << 16), buttons | (type << 29) | (is_client ? (1 << 31) : 0), CMD_GOAL_QUESTION, text); } /* static */ bool ScriptGoal::Question(uint16 uniqueid, ScriptCompany::CompanyID company, Text *question, QuestionType type, int buttons) @@ -138,8 +138,9 @@ EnforcePrecondition(false, ScriptGame::IsMultiplayer()); EnforcePrecondition(false, ScriptClient::ResolveClientID(client) != ScriptClient::CLIENT_INVALID); #ifdef ENABLE_NETWORK - ClientIndex c = NetworkClientInfo::GetByClientID((::ClientID)client)->index; - return DoQuestion(uniqueid, c, true, question, type, buttons); + /* Can only send 16 bits of client_id before proper fix is implemented */ + EnforcePrecondition(false, client < (1 << 16)); + return DoQuestion(uniqueid, client, true, question, type, buttons); #else return false; #endif diff --git a/src/script/api/script_goal.hpp b/src/script/api/script_goal.hpp index f5dfba095f..9ad01c2b52 100644 --- a/src/script/api/script_goal.hpp +++ b/src/script/api/script_goal.hpp @@ -211,7 +211,7 @@ protected: /** * Does common checks and asks the question. */ - static bool DoQuestion(uint16 uniqueid, uint8 target, bool is_client, Text *question, QuestionType type, int buttons); + static bool DoQuestion(uint16 uniqueid, uint32 target, bool is_client, Text *question, QuestionType type, uint32 buttons); }; #endif /* SCRIPT_GOAL_HPP */ diff --git a/src/script/api/script_vehiclelist.cpp b/src/script/api/script_vehiclelist.cpp index 625f0f8c4f..c7040041f0 100644 --- a/src/script/api/script_vehiclelist.cpp +++ b/src/script/api/script_vehiclelist.cpp @@ -16,6 +16,7 @@ #include "script_station.hpp" #include "../../depot_map.h" #include "../../vehicle_base.h" +#include "../../train.h" #include "../../safeguards.h" @@ -23,7 +24,7 @@ ScriptVehicleList::ScriptVehicleList() { const Vehicle *v; FOR_ALL_VEHICLES(v) { - if ((v->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && v->IsPrimaryVehicle()) this->AddItem(v->index); + if ((v->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()))) this->AddItem(v->index); } } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index c3a38b4081..ffb32c2ee3 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -37,6 +37,7 @@ #include "stringfilter_type.h" #include "querystring_gui.h" #include "fontcache.h" +#include "zoom_func.h" #include @@ -557,6 +558,7 @@ struct GameOptionsWindow : Window { _gui_zoom = (ZoomLevel)(ZOOM_LVL_OUT_4X - index); UpdateCursorSize(); UpdateAllVirtCoords(); + FixTitleGameZoom(); ReInitAllWindows(); break; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 947630f0f7..2e0a8589f4 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -995,9 +995,12 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags if (ret.Failed()) return ret; } num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_ROAD)); - } - if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD); + if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE) { + CommandCost ret = CheckOwnership(road_owner); + if (ret.Failed()) return ret; + } + } /* There is a tram, check if we can build road+tram stop over it. */ if (HasBit(cur_rts, ROADTYPE_TRAM)) { diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp index 88ba495f34..557551f3f0 100644 --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -27,6 +27,7 @@ #include "toolbar_gui.h" #include "core/geometry_func.hpp" #include "guitimer_func.h" +#include "zoom_func.h" #include "widgets/statusbar_widget.h" @@ -166,7 +167,7 @@ struct StatusBarWindow : Window { DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, STR_STATUSBAR_PAUSED, TC_FROMSTRING, SA_HOR_CENTER); } else if (this->ticker_scroll < TICKER_STOP && FindWindowById(WC_NEWS_WINDOW, 0) == NULL && _statusbar_news_item != NULL && _statusbar_news_item->string_id != 0) { /* Draw the scrolling news text */ - if (!DrawScrollingStatusText(_statusbar_news_item, this->ticker_scroll, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom)) { + if (!DrawScrollingStatusText(_statusbar_news_item, ScaleGUITrad(this->ticker_scroll), r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom)) { InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); if (Company::IsValidID(_local_company)) { /* This is the default text */ diff --git a/src/stdafx.h b/src/stdafx.h index 6e96e98fde..cb92d97faa 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -257,6 +257,13 @@ # if !defined(FT_EXPORT) # define FT_EXPORT( x ) extern "C" x CDECL # endif +# endif + + /* liblzma from vcpkg (before 5.2.4-2) used to patch lzma.h to define LZMA_API_STATIC for static builds */ +# if defined(WITH_LZMA) +# if !defined(LZMA_API_STATIC) +# define LZMA_API_STATIC +# endif # endif #define strcasecmp stricmp @@ -319,13 +326,15 @@ /* MSVCRT of course has to have a different syntax for long long *sigh* */ #if defined(_MSC_VER) || defined(__MINGW32__) - #define OTTD_PRINTF64 "%I64d" - #define OTTD_PRINTFHEX64 "%I64x" - #define PRINTF_SIZE "%Iu" +# define OTTD_PRINTF64 "%I64d" +# define OTTD_PRINTFHEX64 "%I64x" +# define PRINTF_SIZE "%Iu" +# define PRINTF_SIZEX "%IX" #else - #define OTTD_PRINTF64 "%lld" - #define OTTD_PRINTFHEX64 "%llx" - #define PRINTF_SIZE "%zu" +# define OTTD_PRINTF64 "%lld" +# define OTTD_PRINTFHEX64 "%llx" +# define PRINTF_SIZE "%zu" +# define PRINTF_SIZEX "%zX" #endif typedef unsigned char byte; diff --git a/src/strgen/strgen.h b/src/strgen/strgen.h index ecae71a72a..a4a3aed709 100644 --- a/src/strgen/strgen.h +++ b/src/strgen/strgen.h @@ -29,12 +29,12 @@ struct LangString { char *name; ///< Name of the string. char *english; ///< English text. char *translated; ///< Translated text. - uint16 hash_next; ///< Next hash entry. - uint16 index; ///< The index in the language file. + size_t hash_next; ///< Next hash entry. + size_t index; ///< The index in the language file. int line; ///< Line of string in source-file. Case *translated_case; ///< Cases of the translation. - LangString(const char *name, const char *english, int index, int line); + LangString(const char *name, const char *english, size_t index, int line); ~LangString(); void FreeTranslation(); }; @@ -42,10 +42,10 @@ struct LangString { /** Information about the currently known strings. */ struct StringData { LangString **strings; ///< Array of all known strings. - uint16 *hash_heads; ///< Hash table for the strings. + size_t *hash_heads; ///< Hash table for the strings. size_t tabs; ///< The number of 'tabs' of strings. size_t max_strings; ///< The maximum number of strings. - int next_string_id; ///< The next string ID to allocate. + size_t next_string_id;///< The next string ID to allocate. StringData(size_t tabs); ~StringData(); diff --git a/src/strgen/strgen_base.cpp b/src/strgen/strgen_base.cpp index 8d46b1b271..8bfe442815 100644 --- a/src/strgen/strgen_base.cpp +++ b/src/strgen/strgen_base.cpp @@ -58,7 +58,7 @@ Case::~Case() * @param index The index in the string table. * @param line The line this string was found on. */ -LangString::LangString(const char *name, const char *english, int index, int line) : +LangString::LangString(const char *name, const char *english, size_t index, int line) : name(stredup(name)), english(stredup(english)), translated(NULL), hash_next(0), index(index), line(line), translated_case(NULL) { @@ -90,7 +90,7 @@ void LangString::FreeTranslation() StringData::StringData(size_t tabs) : tabs(tabs), max_strings(tabs * TAB_SIZE) { this->strings = CallocT(max_strings); - this->hash_heads = CallocT(max_strings); + this->hash_heads = CallocT(max_strings); this->next_string_id = 0; } @@ -144,9 +144,9 @@ void StringData::Add(const char *s, LangString *ls) */ LangString *StringData::Find(const char *s) { - int idx = this->hash_heads[this->HashStr(s)]; + size_t idx = this->hash_heads[this->HashStr(s)]; - while (--idx >= 0) { + while (idx-- > 0) { LangString *ls = this->strings[idx]; if (strcmp(ls->name, s) == 0) return ls; @@ -764,7 +764,7 @@ void StringReader::HandleString(char *str) } if (this->data.strings[this->data.next_string_id] != NULL) { - strgen_error("String ID 0x%X for '%s' already in use by '%s'", this->data.next_string_id, str, this->data.strings[this->data.next_string_id]->name); + strgen_error("String ID 0x" PRINTF_SIZEX " for '%s' already in use by '%s'", this->data.next_string_id, str, this->data.strings[this->data.next_string_id]->name); return; } @@ -830,11 +830,15 @@ void StringReader::ParseFile() strecpy(_lang.digit_decimal_separator, ".", lastof(_lang.digit_decimal_separator)); _cur_line = 1; - while (this->ReadLine(buf, lastof(buf)) != NULL) { + while (this->data.next_string_id < this->data.max_strings && this->ReadLine(buf, lastof(buf)) != NULL) { rstrip(buf); this->HandleString(buf); _cur_line++; } + + if (this->data.next_string_id == this->data.max_strings) { + strgen_error("Too many strings, maximum allowed is " PRINTF_SIZE, this->data.max_strings); + } } /** diff --git a/src/subsidy.cpp b/src/subsidy.cpp index d1fda0f0ac..4e819fb4a7 100644 --- a/src/subsidy.cpp +++ b/src/subsidy.cpp @@ -333,6 +333,7 @@ bool FindSubsidyTownCargoRoute() /* Select a random town. */ const Town *src_town = Town::GetRandom(); + if (src_town->cache.population < SUBSIDY_CARGO_MIN_POPULATION) return false; CargoTypes town_cargo_produced = src_town->cargo_produced; diff --git a/src/table/settings.ini b/src/table/settings.ini index 5bb2c73aad..0d508d56bb 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2574,6 +2574,7 @@ max = 3 str = STR_CONFIG_SETTING_SCROLLMODE strhelp = STR_CONFIG_SETTING_SCROLLMODE_HELPTEXT strval = STR_CONFIG_SETTING_SCROLLMODE_DEFAULT +cat = SC_BASIC [SDTC_BOOL] var = gui.smooth_scroll @@ -3730,7 +3731,7 @@ var = network.lan_internet type = SLE_UINT8 flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC guiflags = SGF_NETWORK_ONLY -def = 0 +def = 1 min = 0 max = 1 diff --git a/src/table/townname.h b/src/table/townname.h index 551a050697..b870aa04b7 100644 --- a/src/table/townname.h +++ b/src/table/townname.h @@ -1991,7 +1991,7 @@ static const char * const _name_romanian_real[] = { "Motru", "N\xC4\x83s\xC4\x83ud", "N\xC4\x83vodari", - "Odobe\xC8x99ti", + "Odobe\xC8\x99ti", "Olteni\xC8\x9B""a", "One\xC8\x99ti", "Oradea", diff --git a/src/viewport_func.h b/src/viewport_func.h index 319d4efd60..11fd2fed2c 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -34,6 +34,7 @@ void MarkAllViewportsDirty(int left, int top, int right, int bottom); bool DoZoomInOutWindow(ZoomStateChange how, Window *w); void ZoomInOrOutToCursorWindow(bool in, Window * w); Point GetTileZoomCenterWindow(bool in, Window * w); +void FixTitleGameZoom(); void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out); /** diff --git a/src/widget.cpp b/src/widget.cpp index 5ec539a496..11149d67ba 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2634,7 +2634,7 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, NWidgetResizeBase *nwrb = dynamic_cast(*dest); if (nwrb != NULL) { assert(parts->u.xy.x >= 0 && parts->u.xy.y >= 0); - nwrb->SetMinimalSize(parts->u.xy.x, parts->u.xy.y); + nwrb->SetMinimalSize(ScaleGUITrad(parts->u.xy.x), ScaleGUITrad(parts->u.xy.y)); } break; } @@ -2664,7 +2664,7 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, } case WPT_PADDING: - if (*dest != NULL) (*dest)->SetPadding(parts->u.padding.top, parts->u.padding.right, parts->u.padding.bottom, parts->u.padding.left); + if (*dest != NULL) (*dest)->SetPadding(ScaleGUITrad(parts->u.padding.top), ScaleGUITrad(parts->u.padding.right), ScaleGUITrad(parts->u.padding.bottom), ScaleGUITrad(parts->u.padding.left)); break; case WPT_PIPSPACE: { diff --git a/src/window.cpp b/src/window.cpp index faae96187c..52f6d4785c 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2414,7 +2414,7 @@ static void HandleScrollbarScrolling(Window *w) } /* Find the item we want to move to and make sure it's inside bounds. */ - int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity())); + int pos = min(RoundDivSU(max(0, i + _scrollbar_start_pos) * sb->GetCount(), _scrollbar_size), max(0, sb->GetCount() - sb->GetCapacity())); if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos); if (pos != sb->GetPosition()) { sb->SetPosition(pos);