diff --git a/docs/landscape.html b/docs/landscape.html
index 65fbee7db5..8e1306aedf 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -255,6 +255,7 @@
|
+ - m1 bit 7: Ship docking tile status (for half-tile with water)
- m1 bits 4..0: owner of the tile
- m2: see signals
- m3 bits 7..4: see signals
@@ -885,6 +886,7 @@
|
+ - m1 bit 7: Ship docking tile status (for buoys)
- m1 bits 6..5: water class for buoys, water part of docks and for airport tiles
- m1 bits 4..0: owner of the station
- m2: index into the array of stations
@@ -1022,6 +1024,7 @@
|
+ - m1 bit 7: Ship docking tile status
- m1 bits 6..5 : Water class (sea, canal or river)
- m1 bits 4..0: owner (for sea, rivers, and coasts normally 11)
- m2: Depot index (for depots only)
@@ -1473,6 +1476,7 @@
|
+ - m1 bit 7: Ship docking tile status (for aqueducts)
- m1 bits 4..0: owner
- m2 bits 7..0: custom road bridge heads (flat bridge heads only)
Road/tram bits below are XORed with the axial bridge direction road bits.
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index f13ff1c9c0..901c4fd3c1 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -102,7 +102,7 @@ the array so you can quickly see what is used and what is not.
rail |
XXXX XXXX |
XXXX XXXX |
- OOOX XXXX |
+ XOOX XXXX |
OOOO XXXX OOOO OOOO |
OOOO OOOO |
OOOO XXXX |
@@ -210,7 +210,7 @@ the array so you can quickly see what is used and what is not.
rail station |
XXXX XXXX |
XXXX XXXX |
- OXXX XXXX |
+ XXXX XXXX |
XXXX XXXX XXXX XXXX |
XXXX OOOO |
XXXX XXXX |
@@ -302,7 +302,7 @@ the array so you can quickly see what is used and what is not.
sea, shore |
XXXX XXXX |
XXXX XXXX |
- OXXX XXXX |
+ XXXX XXXX |
OOOO OOOO OOOO OOOO |
OOOO OOOO |
OOOO OOOO |
@@ -356,7 +356,7 @@ the array so you can quickly see what is used and what is not.
tunnel entrance |
XXXX XXXX |
XXXX XXXX |
- OOOX XXXX |
+ XOOX XXXX |
PPPP PPPP PPPP PPPP |
XXXX OOOO |
OOXX XXXX |
diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj
index c21d71d270..08f4471954 100644
--- a/projects/openttd_vs140.vcxproj
+++ b/projects/openttd_vs140.vcxproj
@@ -368,7 +368,6 @@
-
@@ -505,7 +504,6 @@
-
diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters
index d6a6a4aba1..b9a5835109 100644
--- a/projects/openttd_vs140.vcxproj.filters
+++ b/projects/openttd_vs140.vcxproj.filters
@@ -198,9 +198,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -609,9 +606,6 @@
Header Files
-
- Header Files
-
Header Files
diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj
index 61e911bb63..af77a7c6d9 100644
--- a/projects/openttd_vs141.vcxproj
+++ b/projects/openttd_vs141.vcxproj
@@ -368,7 +368,6 @@
-
@@ -505,7 +504,6 @@
-
diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters
index d6a6a4aba1..b9a5835109 100644
--- a/projects/openttd_vs141.vcxproj.filters
+++ b/projects/openttd_vs141.vcxproj.filters
@@ -198,9 +198,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -609,9 +606,6 @@
Header Files
-
- Header Files
-
Header Files
diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj
index 9f01053b01..46fc1f7bd7 100644
--- a/projects/openttd_vs142.vcxproj
+++ b/projects/openttd_vs142.vcxproj
@@ -78,16 +78,16 @@
$(SolutionDir)..\objs\$(Platform)\$(Configuration)\
$(SolutionDir)..\objs\$(Platform)\$(Configuration)\
- AllRules.ruleset
+ NativeMinimumRules.ruleset
- AllRules.ruleset
+ NativeMinimumRules.ruleset
- AllRules.ruleset
+ NativeMinimumRules.ruleset
- AllRules.ruleset
+ NativeMinimumRules.ruleset
$(SolutionDir)..\objs\$(Platform)\$(Configuration)\
@@ -368,7 +368,6 @@
-
@@ -505,7 +504,6 @@
-
diff --git a/projects/openttd_vs142.vcxproj.filters b/projects/openttd_vs142.vcxproj.filters
index d6a6a4aba1..b9a5835109 100644
--- a/projects/openttd_vs142.vcxproj.filters
+++ b/projects/openttd_vs142.vcxproj.filters
@@ -198,9 +198,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -609,9 +606,6 @@
Header Files
-
- Header Files
-
Header Files
diff --git a/projects/openttd_vs142.vcxproj.in b/projects/openttd_vs142.vcxproj.in
index 6b5c18d25c..83befcd8d0 100644
--- a/projects/openttd_vs142.vcxproj.in
+++ b/projects/openttd_vs142.vcxproj.in
@@ -78,16 +78,16 @@
$(SolutionDir)..\objs\$(Platform)\$(Configuration)\
$(SolutionDir)..\objs\$(Platform)\$(Configuration)\
- AllRules.ruleset
+ NativeMinimumRules.ruleset
- AllRules.ruleset
+ NativeMinimumRules.ruleset
- AllRules.ruleset
+ NativeMinimumRules.ruleset
- AllRules.ruleset
+ NativeMinimumRules.ruleset
$(SolutionDir)..\objs\$(Platform)\$(Configuration)\
diff --git a/projects/strgen_vs142.vcxproj b/projects/strgen_vs142.vcxproj
index eb0657a30b..554109b045 100644
--- a/projects/strgen_vs142.vcxproj
+++ b/projects/strgen_vs142.vcxproj
@@ -29,9 +29,9 @@
$(SolutionDir)..\objs\strgen\
$(SolutionDir)..\objs\strgen\
false
- AllRules.ruleset
-
-
+ NativeMinimumRules.ruleset
+
+
diff --git a/source.list b/source.list
index d38e722188..1784fa379a 100644
--- a/source.list
+++ b/source.list
@@ -30,7 +30,6 @@ dedicated.cpp
departures.cpp
depot.cpp
disaster_vehicle.cpp
-dock.cpp
driver.cpp
economy.cpp
effectvehicle.cpp
@@ -193,7 +192,6 @@ direction_func.h
direction_type.h
disaster_vehicle.h
music/dmusic.h
-dock_base.h
driver.h
economy_base.h
economy_func.h
diff --git a/src/bridge_map.h b/src/bridge_map.h
index cd672e8f0a..5dfa2662ab 100644
--- a/src/bridge_map.h
+++ b/src/bridge_map.h
@@ -14,6 +14,7 @@
#include "road_map.h"
#include "bridge.h"
+#include "water_map.h"
/**
* Checks if this is a bridge, instead of a tunnel
@@ -129,6 +130,7 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D
{
SetTileType(t, MP_TUNNELBRIDGE);
SetTileOwner(t, o);
+ SetDockingTile(t, false);
_m[t].m2 = 0;
_m[t].m3 = 0;
_m[t].m4 = INVALID_ROADTYPE;
diff --git a/src/dock.cpp b/src/dock.cpp
deleted file mode 100644
index c8e4466484..0000000000
--- a/src/dock.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/* $Id: dock.cpp $ */
-
-/*
-* 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 .
-*/
-
-/** @file dock.cpp Implementation of the dock base class. */
-
-#include "stdafx.h"
-#include "core/pool_func.hpp"
-#include "dock_base.h"
-#include "station_base.h"
-
-/** The pool of docks. */
-DockPool _dock_pool("Dock");
-INSTANTIATE_POOL_METHODS(Dock)
-
-/**
-* Find a dock at a given tile.
-* @param tile Tile with a dock.
-* @return The dock in the given tile.
-* @pre IsDockTile()
-*/
-/* static */ Dock *Dock::GetByTile(TileIndex tile)
-{
- const Station *st = Station::GetByTile(tile);
-
- for (Dock *d = st->GetPrimaryDock();; d = d->next) {
- if (d->sloped == tile || d->flat == tile) return d;
- assert(d->next != nullptr);
- }
-
- NOT_REACHED();
-}
diff --git a/src/dock_base.h b/src/dock_base.h
deleted file mode 100644
index 67f917fe26..0000000000
--- a/src/dock_base.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* $Id: dock_base.h $ */
-
-/*
-* 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 .
-*/
-
-/** @file dock_base.h Base class for docks. */
-
-#ifndef DOCK_BASE_H
-#define DOCK_BASE_H
-
-#include "station_type.h"
-#include "tile_type.h"
-#include "map_func.h"
-#include "core/pool_type.hpp"
-
-typedef Pool DockPool;
-extern DockPool _dock_pool;
-
-/** A Dock structure. */
-struct Dock : DockPool::PoolItem<&_dock_pool> {
- TileIndex sloped; ///< The sloped tile of the dock.
- TileIndex flat; ///< Position on the map of the flat tile.
- Dock *next; ///< Next dock of the given type at this station.
-
- Dock(TileIndex s = INVALID_TILE, TileIndex f = INVALID_TILE) : sloped(s), flat(f), next(nullptr) { }
-
- ~Dock() {}
-
- inline Dock *GetNextDock() const { return this->next; }
-
- inline TileIndex GetDockingTile() const
- {
- return this->flat + TileOffsByDiagDir(DiagdirBetweenTiles(this->sloped, this->flat));
- }
-
- static Dock *GetByTile(TileIndex tile);
-};
-
-#define FOR_ALL_DOCKS_FROM(var, start) FOR_ALL_ITEMS_FROM(Dock, dock_index, var, start)
-#define FOR_ALL_DOCKS(var) FOR_ALL_DOCKS_FROM(var, 0)
-
-#endif /* DOCK_BASE_H */
diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp
index 74a2a77595..4f8b3b621f 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, nullptr, nullptr, nullptr, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status);
+ icu::ParagraphLayout *p = new icu::ParagraphLayout(buff, length, &runs, nullptr, nullptr, nullptr, _current_text_dir == TD_RTL ? 1 : 0, false, status);
if (status != LE_NO_ERROR) {
delete p;
return nullptr;
diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp
index 5b9646170f..363c44634b 100644
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -156,6 +156,13 @@ Industry::~Industry()
}
}
+ if (this->neutral_station != nullptr) {
+ /* Remove possible docking tiles */
+ TILE_AREA_LOOP(tile_cur, this->location) {
+ ClearDockingTilesCheckingNeighbours(tile_cur);
+ }
+ }
+
if (GetIndustrySpec(this->type)->behaviour & INDUSTRYBEH_PLANT_FIELDS) {
TileArea ta = TileArea(this->location.tile, 0, 0).Expand(21);
diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt
index 4c76dd4f19..61c7a8666c 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 :ناعم
@@ -1939,7 +1939,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}بناء إشارات السكك الحديدية.
@@ -2021,7 +2021,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}بناء محطة تحميل عربات. مفتاح كنترول يسمح بمجاورة المحطات.
@@ -2584,7 +2584,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}
@@ -2672,7 +2673,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}
@@ -3199,7 +3200,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}
@@ -3421,7 +3422,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/korean.txt b/src/lang/korean.txt
index 5d88153ccf..768087867b 100644
--- a/src/lang/korean.txt
+++ b/src/lang/korean.txt
@@ -4479,7 +4479,7 @@ STR_VEHICLE_STATUS_STOPPED :{RED}정지함
STR_VEHICLE_STATUS_BROKEN_DOWN_VEL :{RED}고장 - {STRING}, {LTBLUE} {VELOCITY}
STR_VEHICLE_STATUS_BROKEN_DOWN_VEL_SHORT :{RED}고장 - {STRING}
STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL :{RED}멈추는 중, {VELOCITY}
-STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}기관차 없음
+STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}동력 없음
STR_VEHICLE_STATUS_TRAIN_STUCK :{ORANGE}빈 경로 탐색중
STR_VEHICLE_STATUS_TRAIN_STUCK_WAIT_RESTRICTION :{ORANGE}경로 제한 신호기에 의해 대기 중
STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR :{ORANGE}다음 목적지가 너무 멀리 있습니다
diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt
index 53e16db919..4ee3bfdbc2 100644
--- a/src/lang/welsh.txt
+++ b/src/lang/welsh.txt
@@ -1800,6 +1800,7 @@ STR_CHEAT_CHANGE_DATE_QUERY_CAPT :{WHITE}Newid y
STR_CHEAT_SETUP_PROD :{LTBLUE}Galluogi newid graddfeydd cynhyrchu: {ORANGE}{STRING}
# Livery window
+STR_LIVERY_CAPTION :{WHITE}{COMPANY} - Cynllun Lliw
STR_LIVERY_GENERAL_TOOLTIP :{BLACK}Dangos cynllun lliw cyffredinol
STR_LIVERY_TRAIN_TOOLTIP :{BLACK}Dangos cynllun lliw trenau
@@ -2681,6 +2682,7 @@ STR_ABOUT_VERSION :{BLACK}fersiwn
STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD {COPYRIGHT}2002-2019 Y tîm OpenTTD
# Framerate display window
+STR_FRAMERATE_RATE_BLITTER_TOOLTIP :{BLACK}Nifer o fframiau fideo a lunir bob eiliad.
############ Leave those lines in this order!!
############ End of leave-in-this-order
############ Leave those lines in this order!!
@@ -3791,6 +3793,7 @@ STR_ORDER_CONDITIONAL_AGE :Oed (blynyddoed
STR_ORDER_CONDITIONAL_REQUIRES_SERVICE :Angen gwasanaeth
STR_ORDER_CONDITIONAL_UNCONDITIONALLY :Bob tro
STR_ORDER_CONDITIONAL_REMAINING_LIFETIME :Oes yn weddill (blynyddoedd)
+STR_ORDER_CONDITIONAL_MAX_RELIABILITY :Dibynadwyedd uchafsymol
STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP :{BLACK}Sut i gymharu'r data cerbyd i'r gwerth a roddwyd
STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS :yn hafal i
@@ -4474,6 +4477,7 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Effeithiau sain
STR_BASESOUNDS_WIN_DESCRIPTION :Effeithiau sain gwreiddiol fersiwn Windows o Transport Tycoon Deluxe.
STR_BASESOUNDS_NONE_DESCRIPTION :Pecyn sain heb unrhyw effeithiau sain ynddo.
STR_BASEMUSIC_WIN_DESCRIPTION :Cerddoriaeth gwreiddiol fersion Windows o Transport Tycoon Deluxe.
+STR_BASEMUSIC_DOS_DESCRIPTION :Cerddoriaeth gwreiddiol Transport Tycoon Deluxe (fersiwn DOS).
STR_BASEMUSIC_NONE_DESCRIPTION :Pecyn cerddoriaeth heb unrhyw gerddoriaeth ynddo.
##id 0x2000
diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp
index 8b9e658121..d48b927592 100644
--- a/src/music/dmusic.cpp
+++ b/src/music/dmusic.cpp
@@ -542,19 +542,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. */
@@ -562,9 +562,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. */
@@ -575,27 +577,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));
}
@@ -620,13 +611,6 @@ static void MidiThreadProc()
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();
@@ -669,7 +653,7 @@ static void MidiThreadProc()
_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);
@@ -755,7 +739,7 @@ static void MidiThreadProc()
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.data();
+ const byte *data = block.data.data();
size_t remaining = block.data.size();
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 0259181b34..6ee1f0e60e 100644
--- a/src/music/midifile.cpp
+++ b/src/music/midifile.cpp
@@ -26,6 +26,37 @@
static MidiFile *_midifile_instance = nullptr;
+/**
+ * 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 de5facd48e..21ffe1ed3d 100644
--- a/src/music/win32_m.cpp
+++ b/src/music/win32_m.cpp
@@ -19,6 +19,7 @@
#include "midifile.hpp"
#include "midi.h"
#include "../base_media_base.h"
+#include
#include "../safeguards.h"
@@ -29,20 +30,20 @@ struct PlaybackSegment {
};
static struct {
- UINT time_period; ///< obtained timer precision value
- HMIDIOUT midi_out; ///< handle to open midiOut
- UINT timer_id; ///< ID of active multimedia timer
- CRITICAL_SECTION lock; ///< synchronization for playback status fields
+ UINT time_period; ///< obtained timer precision value
+ HMIDIOUT midi_out; ///< handle to open midiOut
+ UINT timer_id; ///< ID of active multimedia timer
+ std::mutex 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
MidiFile current_file; ///< file currently being played from
PlaybackSegment current_segment; ///< segment info for current playback
- size_t playback_start_time; ///< timestamp current file began playback
+ DWORD playback_start_time; ///< timestamp current file began playback
size_t current_block; ///< next block index to send
MidiFile next_file; ///< upcoming file to play
PlaybackSegment next_segment; ///< segment info for upcoming file
@@ -73,10 +74,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 +98,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 +111,89 @@ 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;
- LeaveCriticalSection(&_midi.lock);
+ /* Ensure only one timer callback is running at once, and prevent races on status flags */
+ std::unique_lock mutex_lock(_midi.lock, std::defer_lock);
+ if (!mutex_lock.try_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;
+ 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) {
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;
+
+ 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;
+
+ return;
+ } else if (_midi.do_start == 3) {
+ /* Set up device-specific effects */
+ TransmitStandardSysex(MidiSysexMessage::RolandSetReverb);
+ _midi.playback_start_time = timeGetTime();
+ _midi.do_start = 4;
+
+ 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;
}
+ } 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;
+ 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--;
+ /* 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--;
}
-
- LeaveCriticalSection(&_midi.lock);
}
/* 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 */
size_t 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 +210,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;
}
}
@@ -210,7 +220,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
/* play pending blocks */
DWORD current_time = timeGetTime();
- size_t playback_time = current_time - _midi.playback_start_time;
+ DWORD playback_time = current_time - _midi.playback_start_time;
while (_midi.current_block < _midi.current_file.blocks.size()) {
MidiFile::DataBlock &block = _midi.current_file.blocks[_midi.current_block];
@@ -229,7 +239,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
break;
}
- byte *data = block.data.data();
+ const byte *data = block.data.data();
size_t remaining = block.data.size();
byte last_status = 0;
while (remaining > 0) {
@@ -313,56 +323,51 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
void MusicDriver_Win32::PlaySong(const MusicSongInfo &song)
{
DEBUG(driver, 2, "Win32-MIDI: PlaySong: entry");
- EnterCriticalSection(&_midi.lock);
- if (!_midi.next_file.LoadSong(song)) {
- LeaveCriticalSection(&_midi.lock);
- return;
- }
+ MidiFile new_song;
+ if (!new_song.LoadSong(song)) return;
+ DEBUG(driver, 2, "Win32-MIDI: PlaySong: Loaded song");
+ std::lock_guard mutex_lock(_midi.lock);
+
+ _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");
_midi.timer_id = timeSetEvent(_midi.time_period, _midi.time_period, TimerCallback, (DWORD_PTR)this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
}
-
- LeaveCriticalSection(&_midi.lock);
}
void MusicDriver_Win32::StopSong()
{
DEBUG(driver, 2, "Win32-MIDI: StopSong: entry");
- EnterCriticalSection(&_midi.lock);
+ std::lock_guard mutex_lock(_midi.lock);
DEBUG(driver, 2, "Win32-MIDI: StopSong: setting flag");
_midi.do_stop = true;
- LeaveCriticalSection(&_midi.lock);
}
bool MusicDriver_Win32::IsSongPlaying()
{
- return _midi.playing || _midi.do_start;
+ return _midi.playing || (_midi.do_start != 0);
}
void MusicDriver_Win32::SetVolume(byte vol)
{
- EnterCriticalSection(&_midi.lock);
+ std::lock_guard mutex_lock(_midi.lock);
_midi.new_volume = vol;
- LeaveCriticalSection(&_midi.lock);
}
const char *MusicDriver_Win32::Start(const char * const *parm)
{
DEBUG(driver, 2, "Win32-MIDI: Start: initializing");
- InitializeCriticalSection(&_midi.lock);
-
int resolution = GetDriverParamInt(parm, "resolution", 5);
int port = GetDriverParamInt(parm, "port", -1);
@@ -381,14 +386,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) {
@@ -405,7 +402,7 @@ const char *MusicDriver_Win32::Start(const char * const *parm)
void MusicDriver_Win32::Stop()
{
- EnterCriticalSection(&_midi.lock);
+ std::lock_guard mutex_lock(_midi.lock);
if (_midi.timer_id) {
timeKillEvent(_midi.timer_id);
@@ -415,7 +412,4 @@ void MusicDriver_Win32::Stop()
timeEndPeriod(_midi.time_period);
midiOutReset(_midi.midi_out);
midiOutClose(_midi.midi_out);
-
- LeaveCriticalSection(&_midi.lock);
- DeleteCriticalSection(&_midi.lock);
}
diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp
index 13f9f94e19..32d8f60f12 100644
--- a/src/pathfinder/npf/npf.cpp
+++ b/src/pathfinder/npf/npf.cpp
@@ -15,6 +15,7 @@
#include "../../ship.h"
#include "../../roadstop_base.h"
#include "../../infrastructure_func.h"
+#include "../../vehicle_func.h"
#include "../pathfinder_func.h"
#include "../pathfinder_type.h"
#include "../follow_track.hpp"
@@ -166,7 +167,7 @@ static int32 NPFCalcStationOrTileHeuristic(AyStar *as, AyStarNode *current, Open
uint dist;
AyStarUserData *user = (AyStarUserData *)as->user_data;
- /* for stations, we are going to aim for the closest station tile */
+ /* aim for the closest station tile */
if (fstd->station_index != INVALID_STATION) {
to = CalcClosestStationTile(fstd->station_index, from, fstd->station_type);
}
@@ -304,6 +305,15 @@ static void NPFMarkTile(TileIndex tile)
}
}
+static Vehicle *CountShipProc(Vehicle *v, void *data)
+{
+ uint *count = (uint *)data;
+ /* Ignore other vehicles (aircraft) and ships inside depot. */
+ if (v->type == VEH_SHIP && (v->vehstatus & VS_HIDDEN) == 0) (*count)++;
+
+ return nullptr;
+}
+
static int32 NPFWaterPathCost(AyStar *as, AyStarNode *current, OpenListNode *parent)
{
/* TileIndex tile = current->tile; */
@@ -320,6 +330,13 @@ static int32 NPFWaterPathCost(AyStar *as, AyStarNode *current, OpenListNode *par
cost += _settings_game.pf.npf.npf_water_curve_penalty;
}
+ if (IsDockingTile(current->tile)) {
+ /* Check docking tile for occupancy */
+ uint count = 1;
+ HasVehicleOnPos(current->tile, &count, &CountShipProc);
+ cost += count * 3 * _trackdir_length[trackdir];
+ }
+
/* @todo More penalties? */
return cost;
@@ -562,16 +579,22 @@ static int32 NPFFindStationOrTile(const AyStar *as, const OpenListNode *current)
const AyStarNode *node = ¤t->path.node;
TileIndex tile = node->tile;
- if (fstd->station_index == INVALID_STATION) {
- return (tile == fstd->dest_coords) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
+ if (fstd->station_index == INVALID_STATION && tile == fstd->dest_coords) return AYSTAR_FOUND_END_NODE;
+
+ if (fstd->v->type == VEH_SHIP) {
+ /* Ships do not actually reach the destination station, so we check for a docking tile instead. */
+ if (IsDockingTile(tile) && IsShipDestinationTile(tile, fstd->station_index)) return AYSTAR_FOUND_END_NODE;
+ return AYSTAR_DONE;
}
- switch (fstd->v->type) {
- default: NOT_REACHED();
- case VEH_TRAIN: return (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
- case VEH_ROAD: return (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index && GetStationType(tile) == fstd->station_type && (fstd->not_articulated || IsDriveThroughStopTile(tile))) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
- case VEH_SHIP: return Station::Get(fstd->station_index)->IsDockingTile(tile) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
+ if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) {
+ if (fstd->v->type == VEH_TRAIN) return AYSTAR_FOUND_END_NODE;
+
+ assert(fstd->v->type == VEH_ROAD);
+ /* Only if it is a valid station *and* we can stop there */
+ if (GetStationType(tile) == fstd->station_type && (fstd->not_articulated || IsDriveThroughStopTile(tile))) return AYSTAR_FOUND_END_NODE;
}
+ return AYSTAR_DONE;
}
/**
@@ -1116,12 +1139,22 @@ void InitializeNPF()
static void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, const Vehicle *v, bool reserve_path = false)
{
- /* Fill station_index for station orders, else only dest_coords. */
- if (v->current_order.IsType(OT_GOTO_STATION) || (v->type != VEH_SHIP && v->current_order.IsType(OT_GOTO_WAYPOINT))) {
+ /* Ships don't really reach their stations, but the tile in front. So don't
+ * save the station id for ships. For roadvehs we don't store it either,
+ * because multistop depends on vehicles actually reaching the exact
+ * dest_tile, not just any stop of that station.
+ * So only for train orders to stations we fill fstd->station_index, for all
+ * others only dest_coords */
+ if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_WAYPOINT)) {
fstd->station_index = v->current_order.GetDestination();
- fstd->station_type = (v->type == VEH_SHIP) ? STATION_DOCK :
- (v->type == VEH_TRAIN) ? (v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT) :
- (RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK);
+ if (v->type == VEH_TRAIN) {
+ fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT;
+ } else if (v->type == VEH_ROAD) {
+ fstd->station_type = RoadVehicle::From(v)->IsBus() ? STATION_BUS : STATION_TRUCK;
+ } else if (v->type == VEH_SHIP) {
+ fstd->station_type = v->current_order.IsType(OT_GOTO_STATION) ? STATION_DOCK : STATION_BUOY;
+ }
+
fstd->not_articulated = v->type == VEH_ROAD && !RoadVehicle::From(v)->HasArticulatedPart();
/* Let's take the closest tile of the station as our target for vehicles */
fstd->dest_coords = CalcClosestStationTile(fstd->station_index, v->tile, fstd->station_type);
diff --git a/src/pathfinder/yapf/yapf_node_ship.hpp b/src/pathfinder/yapf/yapf_node_ship.hpp
index df4254fd98..a8f8270714 100644
--- a/src/pathfinder/yapf/yapf_node_ship.hpp
+++ b/src/pathfinder/yapf/yapf_node_ship.hpp
@@ -14,7 +14,19 @@
/** Yapf Node for ships */
template
-struct CYapfShipNodeT : CYapfNodeT > { };
+struct CYapfShipNodeT : CYapfNodeT > {
+ typedef CYapfNodeT > base;
+
+ TileIndex m_segment_last_tile;
+ Trackdir m_segment_last_td;
+
+ void Set(CYapfShipNodeT *parent, TileIndex tile, Trackdir td, bool is_choice)
+ {
+ base::Set(parent, tile, td, is_choice);
+ m_segment_last_tile = tile;
+ m_segment_last_td = td;
+ }
+};
/* now define two major node types (that differ by key type) */
typedef CYapfShipNodeT CYapfShipNodeExitDir;
diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp
index ddf2af90b4..731ae2f845 100644
--- a/src/pathfinder/yapf/yapf_ship.cpp
+++ b/src/pathfinder/yapf/yapf_ship.cpp
@@ -11,12 +11,96 @@
#include "../../stdafx.h"
#include "../../ship.h"
+#include "../../industry.h"
+#include "../../vehicle_func.h"
#include "yapf.hpp"
#include "yapf_node_ship.hpp"
#include "../../safeguards.h"
+template
+class CYapfDestinationTileWaterT
+{
+public:
+ typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class)
+ typedef typename Types::TrackFollower TrackFollower;
+ typedef typename Types::NodeList::Titem Node; ///< this will be our node type
+ typedef typename Node::Key Key; ///< key to hash tables
+
+protected:
+ TileIndex m_destTile;
+ TrackdirBits m_destTrackdirs;
+ StationID m_destStation;
+
+public:
+ void SetDestination(const Ship *v)
+ {
+ if (v->current_order.IsType(OT_GOTO_STATION)) {
+ m_destStation = v->current_order.GetDestination();
+ m_destTile = CalcClosestStationTile(m_destStation, v->tile, STATION_DOCK);
+ m_destTrackdirs = INVALID_TRACKDIR_BIT;
+ } else {
+ m_destStation = INVALID_STATION;
+ m_destTile = v->dest_tile;
+ m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
+ }
+ }
+
+protected:
+ /** to access inherited path finder */
+ inline Tpf& Yapf()
+ {
+ return *static_cast(this);
+ }
+
+public:
+ /** Called by YAPF to detect if node ends in the desired destination */
+ inline bool PfDetectDestination(Node& n)
+ {
+ return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td);
+ }
+
+ inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
+ {
+ if (m_destStation != INVALID_STATION) {
+ return IsDockingTile(tile) && IsShipDestinationTile(tile, m_destStation);
+ }
+
+ return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
+ }
+
+ /**
+ * Called by YAPF to calculate cost estimate. Calculates distance to the destination
+ * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
+ */
+ inline bool PfCalcEstimate(Node& n)
+ {
+ static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
+ static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
+ if (PfDetectDestination(n)) {
+ n.m_estimate = n.m_cost;
+ return true;
+ }
+
+ TileIndex tile = n.m_segment_last_tile;
+ DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
+ int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
+ int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
+ int x2 = 2 * TileX(m_destTile);
+ int y2 = 2 * TileY(m_destTile);
+ int dx = abs(x1 - x2);
+ int dy = abs(y1 - y2);
+ int dmin = min(dx, dy);
+ int dxy = abs(dx - dy);
+ int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
+ n.m_estimate = n.m_cost + d;
+ assert(n.m_estimate >= n.m_parent->m_estimate);
+ return true;
+ }
+};
+
+
/** Node Follower module of YAPF for ships */
template
class CYapfFollowShipT
@@ -90,12 +174,16 @@ public:
if (pNode != nullptr) {
uint steps = 0;
for (Node *n = pNode; n->m_parent != nullptr; n = n->m_parent) steps++;
+ uint skip = 0;
+ if (path_found) skip = YAPF_SHIP_PATH_CACHE_LENGTH / 2;
/* walk through the path back to the origin */
Node *pPrevNode = nullptr;
while (pNode->m_parent != nullptr) {
steps--;
- if (steps > 0 && steps < YAPF_SHIP_PATH_CACHE_LENGTH) {
+ /* Skip tiles at end of path near destination. */
+ if (skip > 0) skip--;
+ if (skip == 0 && steps > 0 && steps < YAPF_SHIP_PATH_CACHE_LENGTH) {
path_cache.push_front(pNode->GetTrackdir());
}
pPrevNode = pNode;
@@ -178,6 +266,15 @@ public:
return 0;
}
+ static Vehicle *CountShipProc(Vehicle *v, void *data)
+ {
+ uint *count = (uint *)data;
+ /* Ignore other vehicles (aircraft) and ships inside depot. */
+ if (v->type == VEH_SHIP && (v->vehstatus & VS_HIDDEN) == 0) (*count)++;
+
+ return nullptr;
+ }
+
/**
* Called by YAPF to calculate the cost from the origin to the given node.
* Calculates only the cost of given node, adds it to the parent node cost
@@ -190,6 +287,13 @@ public:
/* additional penalty for curves */
c += CurveCost(n.m_parent->GetTrackdir(), n.GetTrackdir());
+ if (IsDockingTile(n.GetTile())) {
+ /* Check docking tile for occupancy */
+ uint count = 1;
+ HasVehicleOnPos(n.GetTile(), &count, &CountShipProc);
+ c += count * 3 * YAPF_TILE_LENGTH;
+ }
+
/* Skipped tile cost for aqueducts. */
c += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
@@ -204,80 +308,6 @@ public:
}
};
-/** YAPF destination provider for ships */
-template
-class CYapfDestinationShipT
-{
-public:
- typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class)
- typedef typename Types::NodeList::Titem Node; ///< this will be our node type
- typedef typename Node::Key Key; ///< key to hash tables
-
-protected:
- StationID m_destStation; ///< destinatin station
- TileIndex m_destTile; ///< destination tile
-
-public:
- /** set the destination */
- void SetDestination(const Ship *v)
- {
- if (v->current_order.IsType(OT_GOTO_STATION)) {
- m_destStation = v->current_order.GetDestination();
- m_destTile = CalcClosestStationTile(m_destStation, v->tile, STATION_DOCK);
- } else {
- m_destStation = INVALID_STATION;
- m_destTile = v->dest_tile;
- }
- }
-
-protected:
- /** to access inherited path finder */
- Tpf& Yapf()
- {
- return *static_cast(this);
- }
-
-public:
- /** Called by YAPF to detect if node ends in the desired destination */
- inline bool PfDetectDestination(Node &n)
- {
- if (m_destStation == INVALID_STATION) {
- return n.m_key.m_tile == m_destTile;
- } else {
- return Station::Get(m_destStation)->IsDockingTile(n.m_key.m_tile);
- }
- }
-
- /**
- * Called by YAPF to calculate cost estimate. Calculates distance to the destination
- * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
- */
- inline bool PfCalcEstimate(Node &n)
- {
- static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
- static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
- if (PfDetectDestination(n)) {
- n.m_estimate = n.m_cost;
- return true;
- }
-
- TileIndex tile = n.GetTile();
- DiagDirection exitdir = TrackdirToExitdir(n.GetTrackdir());
- int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
- int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
- int x2 = 2 * TileX(m_destTile);
- int y2 = 2 * TileY(m_destTile);
- int dx = abs(x1 - x2);
- int dy = abs(y1 - y2);
- int dmin = min(dx, dy);
- int dxy = abs(dx - dy);
- int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
- n.m_estimate = n.m_cost + d;
- assert(n.m_estimate >= n.m_parent->m_estimate);
- return true;
- }
-};
-
/**
* Config struct of YAPF for ships.
* Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
@@ -299,7 +329,7 @@ struct CYapfShip_TypesT
typedef CYapfBaseT PfBase; // base pathfinder class
typedef CYapfFollowShipT PfFollow; // node follower
typedef CYapfOriginTileT PfOrigin; // origin provider
- typedef CYapfDestinationShipT PfDestination; // destination/distance provider
+ typedef CYapfDestinationTileWaterT PfDestination; // destination/distance provider
typedef CYapfSegmentCostCacheNoneT PfCache; // segment cost cache provider
typedef CYapfCostShipT PfCost; // cost provider
};
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 4d620644be..6d4a6ca8f7 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -734,6 +734,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
default: {
/* Will there be flat water on the lower halftile? */
bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh);
+ bool docking = IsPossibleDockingTile(tile) && IsDockingTile(tile);
CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile);
if (ret.Failed()) return ret;
@@ -750,7 +751,10 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (flags & DC_EXEC) {
MakeRailNormal(tile, _current_company, trackbit, railtype);
- if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER);
+ if (water_ground) {
+ SetRailGroundType(tile, RAIL_GROUND_WATER);
+ SetDockingTile(tile, docking);
+ }
Company::Get(_current_company)->infrastructure.rail[railtype]++;
DirtyCompanyInfrastructureWindows(_current_company);
}
@@ -882,7 +886,9 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
Slope tileh = GetTileSlope(tile);
/* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)) {
+ bool docking = IsDockingTile(tile);
MakeShore(tile);
+ SetDockingTile(tile, docking);
} else {
DoClearSquare(tile);
}
@@ -2041,6 +2047,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)) {
@@ -2155,6 +2162,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;
@@ -2163,6 +2171,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
/* notify YAPF about the track layout change */
yapf_notify_track_change(tile, GetTrackBits(tile));
}
+ found_convertible_track = true;
if (raw_secondary_type != INVALID_RAILTYPE) {
cost.AddCost(RailConvertCost(type, totype));
cost.AddCost(RailConvertCost(raw_secondary_type, totype));
@@ -2196,6 +2205,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
uint num_primary_pieces = GetTunnelBridgeLength(tile, endtile) + CountBits(GetPrimaryTunnelBridgeTrackBits(tile)) + CountBits(GetPrimaryTunnelBridgeTrackBits(endtile));
+ found_convertible_track = true;
cost.AddCost(num_primary_pieces * RailConvertCost(type, totype));
RailType end_secondary_type = GetTileSecondaryRailTypeIfValid(endtile);
if (raw_secondary_type != INVALID_RAILTYPE) cost.AddCost(RailConvertCost(raw_secondary_type, totype));
@@ -2238,6 +2248,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
YapfNotifyTrackLayoutChange(tile, track);
}
+ found_convertible_track = true;
cost.AddCost(RailConvertCost(type, totype));
break;
}
@@ -2255,7 +2266,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/rail_map.h b/src/rail_map.h
index 27e7e36a2a..470ef505fd 100644
--- a/src/rail_map.h
+++ b/src/rail_map.h
@@ -17,6 +17,7 @@
#include "signal_func.h"
#include "track_func.h"
#include "tile_map.h"
+#include "water_map.h"
#include "signal_type.h"
#include "tunnelbridge_map.h"
@@ -594,6 +595,7 @@ static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r)
{
SetTileType(t, MP_RAILWAY);
SetTileOwner(t, o);
+ SetDockingTile(t, false);
_m[t].m2 = 0;
_m[t].m3 = 0;
_m[t].m4 = 0;
@@ -608,6 +610,7 @@ static inline void MakeRailDepot(TileIndex t, Owner o, DepotID did, DiagDirectio
{
SetTileType(t, MP_RAILWAY);
SetTileOwner(t, o);
+ SetDockingTile(t, false);
_m[t].m2 = did;
_m[t].m3 = 0;
_m[t].m4 = 0;
diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp
index 32f772eb68..a7aff2e9d2 100644
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -2554,6 +2554,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost error = CommandCost((rtt == RTT_TRAM) ? STR_ERROR_NO_SUITABLE_TRAMWAY : STR_ERROR_NO_SUITABLE_ROAD); // by default, there is no road to convert.
+ bool found_convertible_road = false; // whether we actually did convert any road/tram (see bug #7633)
TileIterator *iter = new OrthogonalTileIterator(area_start, area_end);
for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
@@ -2609,6 +2610,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
uint num_pieces = CountBits(GetAnyRoadBits(tile, rtt));;
+ found_convertible_road = true;
cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type));
if (flags & DC_EXEC) { // we can safely convert, too
@@ -2660,6 +2662,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
/* There are 2 pieces on *every* tile of the bridge or tunnel */
uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * 2;
+ found_convertible_road = true;
cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type));
uint tunnel_length = GetTunnelBridgeLength(tile, endtile);
@@ -2718,7 +2721,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
}
delete iter;
- return (cost.GetCost() == 0) ? error : cost;
+ return found_convertible_road ? cost : error;
}
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index 653431acba..f81a52af2d 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -31,7 +31,6 @@
#include "../station_base.h"
#include "../waypoint_base.h"
#include "../roadstop_base.h"
-#include "../dock_base.h"
#include "../tunnelbridge_map.h"
#include "../pathfinder/yapf/yapf_cache.h"
#include "../elrail_func.h"
@@ -63,6 +62,7 @@
#include "../tracerestrict.h"
#include "../tunnel_map.h"
#include "../bridge_signal_map.h"
+#include "../water.h"
#include "saveload_internal.h"
@@ -761,7 +761,6 @@ bool AfterLoadGame()
Station *st;
FOR_ALL_STATIONS(st) {
if (st->airport.tile == 0) st->airport.tile = INVALID_TILE;
- if (st->dock_station.tile == 0) st->dock_station.tile = INVALID_TILE;
if (st->train_station.tile == 0) st->train_station.tile = INVALID_TILE;
}
@@ -890,7 +889,6 @@ bool AfterLoadGame()
}
}
-
if (SlXvIsFeaturePresent(XSLFI_SPRINGPP)) {
/*
* Reject huge airports
@@ -902,7 +900,7 @@ bool AfterLoadGame()
if (st->airport.tile == INVALID_TILE) continue;
StringID err = INVALID_STRING_ID;
if (st->airport.type == 9) {
- if (st->dock_station.tile != INVALID_TILE && IsOilRig(st->dock_station.tile)) {
+ if (st->ship_station.tile != INVALID_TILE && IsOilRig(st->ship_station.tile)) {
/* this airport is probably an oil rig, not a huge airport */
} else {
err = STR_GAME_SAVELOAD_ERROR_HUGE_AIRPORTS_PRESENT;
@@ -929,7 +927,7 @@ bool AfterLoadGame()
Aircraft *v;
FOR_ALL_AIRCRAFT(v) {
Station *st = GetTargetAirportIfValid(v);
- if (st != nullptr && ((st->dock_station.tile != INVALID_TILE && IsOilRig(st->dock_station.tile)) || st->airport.type == AT_OILRIG)) {
+ if (st != nullptr && ((st->ship_station.tile != INVALID_TILE && IsOilRig(st->ship_station.tile)) || st->airport.type == AT_OILRIG)) {
/* aircraft is on approach to an oil rig, bail out now */
SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_HELI_OILRIG_BUG);
/* Restore the signals */
@@ -939,6 +937,13 @@ bool AfterLoadGame()
}
}
+ if (IsSavegameVersionBefore(SLV_MULTITILE_DOCKS)) {
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ st->ship_station.tile = INVALID_TILE;
+ }
+ }
+
/* Update all vehicles */
AfterLoadVehicles(true);
@@ -1040,26 +1045,6 @@ bool AfterLoadGame()
}
}
- if (SlXvIsFeatureMissing(XSLFI_MULTIPLE_DOCKS)) {
- /* Dock type has changed. */
- Station *st;
- FOR_ALL_STATIONS(st) {
- if (st->dock_station.tile == INVALID_TILE) continue;
- assert(Dock::CanAllocateItem());
- if (IsOilRig(st->dock_station.tile)) {
- /* Set dock station tile to dest tile instead of station. */
- st->docks = new Dock(st->dock_station.tile, st->dock_station.tile + ToTileIndexDiff({ 1, 0 }));
- } else if (IsDock(st->dock_station.tile)) {
- /* A normal two-tiles dock. */
- st->docks = new Dock(st->dock_station.tile, TileAddByDiagDir(st->dock_station.tile, GetDockDirection(st->dock_station.tile)));
- } else if (IsBuoy(st->dock_station.tile)) {
- /* A buoy. */
- } else {
- NOT_REACHED();
- }
- }
- }
-
for (TileIndex t = 0; t < map_size; t++) {
switch (GetTileType(t)) {
case MP_STATION: {
@@ -3637,6 +3622,29 @@ bool AfterLoadGame()
}
}
+ /* Update structures for multitile docks */
+ if (IsSavegameVersionBefore(SLV_MULTITILE_DOCKS)) {
+ for (TileIndex t = 0; t < map_size; t++) {
+ /* Clear docking tile flag from relevant tiles as it
+ * was not previously cleared. */
+ if (IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)) {
+ SetDockingTile(t, false);
+ }
+ /* Add docks and oilrigs to Station::ship_station. */
+ if (IsTileType(t, MP_STATION)) {
+ if (IsDock(t) || IsOilRig(t)) Station::GetByTile(t)->ship_station.Add(t);
+ }
+ }
+ }
+
+ if (IsSavegameVersionBefore(SLV_MULTITILE_DOCKS) || !SlXvIsFeaturePresent(XSLFI_MULTIPLE_DOCKS, 2)) {
+ /* Scan for docking tiles */
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ if (st->ship_station.tile != INVALID_TILE) UpdateStationDockingTiles(st);
+ }
+ }
+
/* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */
Station::RecomputeCatchmentForAll();
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index afdab325fb..76531c04fb 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -91,7 +91,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_CHUNNEL, XSCF_NULL, 2, 2, "chunnel", nullptr, nullptr, "TUNN" },
{ XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 1, 1, "scheduled_dispatch", nullptr, nullptr, nullptr },
{ XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL, 1, 1, "more_town_growth_rates", nullptr, nullptr, nullptr },
- { XSLFI_MULTIPLE_DOCKS, XSCF_NULL, 1, 1, "multiple_docks", nullptr, nullptr, "DOCK" },
+ { XSLFI_MULTIPLE_DOCKS, XSCF_NULL, 2, 2, "multiple_docks", nullptr, nullptr, nullptr },
{ XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 6, 6, "timetable_extra", nullptr, nullptr, "ORDX" },
{ XSLFI_TRAIN_FLAGS_EXTRA, XSCF_NULL, 1, 1, "train_flags_extra", nullptr, nullptr, nullptr },
{ XSLFI_TRAIN_THROUGH_LOAD, XSCF_NULL, 2, 2, "train_through_load", nullptr, nullptr, nullptr },
diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp
index 37f723a06f..54694c8dbc 100644
--- a/src/saveload/oldloader_sl.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -725,7 +725,7 @@ static const OldChunks station_chunk[] = {
OCL_NULL( 4 ), ///< bus/lorry tile
OCL_SVAR( OC_TILE, Station, train_station.tile ),
OCL_SVAR( OC_TILE, Station, airport.tile ),
- OCL_SVAR( OC_TILE, Station, dock_station.tile),
+ OCL_NULL( 4 ), ///< dock tile
OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Station, train_station.w ),
OCL_NULL( 1 ), ///< sort-index, no longer in use
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index c76bfa67d8..2f18bada7a 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -25,7 +25,6 @@
#include "../stdafx.h"
#include "../debug.h"
#include "../station_base.h"
-#include "../dock_base.h"
#include "../thread.h"
#include "../town.h"
#include "../network/network.h"
@@ -1192,7 +1191,6 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt)
case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
- case REF_DOCKS: return ((const Dock*)obj)->index + 1;
default: NOT_REACHED();
}
}
@@ -1258,10 +1256,6 @@ static void *IntToReference(size_t index, SLRefType rt)
if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
SlErrorCorrupt("Referencing invalid RoadStop");
- case REF_DOCKS:
- if (Dock::IsValidID(index)) return Dock::Get(index);
- SlErrorCorrupt("Referencing invalid Dock");
-
case REF_ENGINE_RENEWS:
if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
SlErrorCorrupt("Referencing invalid EngineRenew");
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 9710235ad3..1dd052b3d9 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -304,6 +304,7 @@ enum SaveLoadVersion : uint16 {
SLV_ROAD_TYPES, ///< 214 PR#6811 NewGRF road types.
SLV_SCRIPT_MEMLIMIT, ///< 215 PR#7516 Limit on AI/GS memory consumption.
+ SLV_MULTITILE_DOCKS, ///< 216 PR#7380 Multiple docks per station.
SL_MAX_VERSION, ///< Highest possible saveload version
@@ -396,7 +397,6 @@ enum SLRefType {
REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph.
REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
REF_TEMPLATE_VEHICLE = 12, ///< Load/save a reference to a template vehicle
- REF_DOCKS = 13, ///< Load/save a reference to a dock.
};
/** Flags of a chunk. */
diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp
index 74acaf94ea..3a5d0ec122 100644
--- a/src/saveload/station_sl.cpp
+++ b/src/saveload/station_sl.cpp
@@ -13,7 +13,6 @@
#include "../station_base.h"
#include "../waypoint_base.h"
#include "../roadstop_base.h"
-#include "../dock_base.h"
#include "../vehicle_base.h"
#include "../newgrf_station.h"
@@ -127,11 +126,6 @@ void AfterLoadStations()
Station *sta = Station::From(st);
for (const RoadStop *rs = sta->bus_stops; rs != nullptr; rs = rs->next) sta->bus_station.Add(rs->xy);
for (const RoadStop *rs = sta->truck_stops; rs != nullptr; rs = rs->next) sta->truck_station.Add(rs->xy);
-
- for (const Dock *d = sta->docks; d != nullptr; d = d->next) {
- sta->dock_station.Add(d->sloped);
- sta->dock_station.Add(d->flat);
- }
}
StationUpdateCachedTriggers(st);
@@ -175,14 +169,6 @@ static const SaveLoad _roadstop_desc[] = {
SLE_END()
};
-static const SaveLoad _dock_desc[] = {
- SLE_VAR(Dock, sloped, SLE_UINT32),
- SLE_VAR(Dock, flat, SLE_UINT32),
- SLE_REF(Dock, next, REF_DOCKS),
-
- SLE_END()
-};
-
static const SaveLoad _old_station_desc[] = {
SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Station, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
@@ -191,8 +177,8 @@ static const SaveLoad _old_station_desc[] = {
SLE_CONDVAR(Station, train_station.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDVAR(Station, airport.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Station, airport.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
- SLE_CONDVAR(Station, dock_station.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
- SLE_CONDVAR(Station, dock_station.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
+ SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
+ SLE_CONDNULL(4, SLV_6, SLV_MULTITILE_DOCKS),
SLE_REF(Station, town, REF_TOWN),
SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16),
SLE_CONDVAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION),
@@ -447,8 +433,15 @@ static const SaveLoad _station_desc[] = {
SLE_REF(Station, bus_stops, REF_ROADSTOPS),
SLE_REF(Station, truck_stops, REF_ROADSTOPS),
- SLE_CONDVAR_X(Station, dock_station.tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 0, 0)),
- SLE_CONDREF_X(Station, docks, REF_DOCKS, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 1)),
+ SLE_CONDVAR_X(Station, ship_station.tile, SLE_UINT32, SL_MIN_VERSION, SLV_MULTITILE_DOCKS, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 0, 0)),
+ SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 1, 1)),
+ SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
+ SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
+ SLE_CONDVARVEC_X(Station, docking_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 2)),
SLE_VAR(Station, airport.tile, SLE_UINT32),
SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION),
SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION),
@@ -681,38 +674,15 @@ static void Ptrs_ROADSTOP()
}
}
-static void Save_DOCK()
-{
- Dock *d;
-
- FOR_ALL_DOCKS(d) {
- SlSetArrayIndex(d->index);
- SlObject(d, _dock_desc);
- }
-}
-
static void Load_DOCK()
{
- int index;
-
- while ((index = SlIterateArray()) != -1) {
- Dock *d = new (index) Dock();
-
- SlObject(d, _dock_desc);
- }
-}
-
-static void Ptrs_DOCK()
-{
- Dock *d;
- FOR_ALL_DOCKS(d) {
- SlObject(d, _dock_desc);
- }
+ extern void SlSkipArray();
+ SlSkipArray();
}
extern const ChunkHandler _station_chunk_handlers[] = {
{ 'STNS', nullptr, Load_STNS, Ptrs_STNS, nullptr, CH_ARRAY },
{ 'STNN', Save_STNN, Load_STNN, Ptrs_STNN, nullptr, CH_ARRAY },
{ 'ROAD', Save_ROADSTOP, Load_ROADSTOP, Ptrs_ROADSTOP, nullptr, CH_ARRAY},
- { 'DOCK', Save_DOCK, Load_DOCK, Ptrs_DOCK, nullptr, CH_ARRAY | CH_LAST},
+ { 'DOCK', nullptr, Load_DOCK, nullptr, nullptr, CH_ARRAY | CH_LAST},
};
diff --git a/src/script/api/script_order.cpp b/src/script/api/script_order.cpp
index 23f4f7937c..639b0643c2 100644
--- a/src/script/api/script_order.cpp
+++ b/src/script/api/script_order.cpp
@@ -17,7 +17,6 @@
#include "../../debug.h"
#include "../../vehicle_base.h"
#include "../../roadstop_base.h"
-#include "../../dock_base.h"
#include "../../depot_base.h"
#include "../../station_base.h"
#include "../../waypoint_base.h"
@@ -262,8 +261,10 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
TILE_AREA_LOOP(t, st->train_station) {
if (st->TileBelongsToRailStation(t)) return t;
}
- } else if (st->docks != nullptr) {
- return st->docks->flat;
+ } else if (st->ship_station.tile != INVALID_TILE) {
+ TILE_AREA_LOOP(t, st->ship_station) {
+ if (IsDockTile(t) && GetStationIndex(t) == st->index) return t;
+ }
} else if (st->bus_stops != nullptr) {
return st->bus_stops->xy;
} else if (st->truck_stops != nullptr) {
diff --git a/src/script/api/script_vehiclelist.cpp b/src/script/api/script_vehiclelist.cpp
index 48b290caeb..5534f6f8a5 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/ship.h b/src/ship.h
index e739097229..e15e654d70 100644
--- a/src/ship.h
+++ b/src/ship.h
@@ -60,6 +60,8 @@ struct Ship FINAL : public SpecializedVehicle {
void SetDestTile(TileIndex tile);
};
+bool IsShipDestinationTile(TileIndex tile, StationID station);
+
/**
* Iterate over all ships.
* @param var The variable used for iteration.
diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp
index 2ecde98843..c4e9fd010b 100644
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -11,7 +11,6 @@
#include "stdafx.h"
#include "ship.h"
-#include "dock_base.h"
#include "landscape.h"
#include "timetable.h"
#include "news_func.h"
@@ -36,6 +35,8 @@
#include "tunnelbridge_map.h"
#include "zoom_func.h"
#include "framerate_type.h"
+#include "industry.h"
+#include "industry_map.h"
#include "table/strings.h"
@@ -300,7 +301,7 @@ TileIndex Ship::GetOrderStationLocation(StationID station)
if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
const Station *st = Station::Get(station);
- if (st->HasFacilities(FACIL_DOCK)) {
+ if (CanVehicleUseStation(this, st)) {
return st->xy;
} else {
this->IncrementRealOrderIndex();
@@ -764,6 +765,28 @@ static bool ShipMoveUpDownOnLock(Ship *v)
return true;
}
+/**
+ * Test if a tile is a docking tile for the given station.
+ * @param tile Docking tile to test.
+ * @param station Destination station.
+ * @return true iff docking tile is next to station.
+ */
+bool IsShipDestinationTile(TileIndex tile, StationID station)
+{
+ assert(IsDockingTile(tile));
+ /* Check each tile adjacent to docking tile. */
+ for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
+ TileIndex t = tile + TileOffsByDiagDir(d);
+ if (!IsValidTile(t)) continue;
+ if (IsDockTile(t) && GetStationIndex(t) == station && IsValidDockingDirectionForDock(t, d)) return true;
+ if (IsTileType(t, MP_INDUSTRY)) {
+ const Industry *i = Industry::GetByTile(t);
+ if (i->neutral_station != nullptr && i->neutral_station->index == station) return true;
+ }
+ }
+ return false;
+}
+
static void ShipController(Ship *v)
{
uint32 r;
@@ -832,17 +855,18 @@ static void ShipController(Ship *v)
UpdateVehicleTimetable(v, true);
v->IncrementRealOrderIndex();
v->current_order.MakeDummy();
- } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
- if (v->dest_tile == gp.new_tile && (gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
+ } else if (v->current_order.IsType(OT_GOTO_DEPOT) &&
+ v->dest_tile == gp.new_tile) {
+ /* Depot orders really need to reach the tile */
+ if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
VehicleEnterDepot(v);
return;
}
- } else if (v->current_order.IsType(OT_GOTO_STATION)) {
+ } else if (v->current_order.IsType(OT_GOTO_STATION) && IsDockingTile(gp.new_tile)) {
+ /* Process station in the orderlist. */
Station *st = Station::Get(v->current_order.GetDestination());
- if (st->IsDockingTile(gp.new_tile)) {
- v->last_station_visited = v->current_order.GetDestination();
-
- /* Process station in the orderlist. */
+ if (st->docking_station.Contains(gp.new_tile) && IsShipDestinationTile(gp.new_tile, st->index)) {
+ v->last_station_visited = st->index;
if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
ShipArrivesAt(v, st);
v->BeginLoading();
diff --git a/src/station.cpp b/src/station.cpp
index da55903387..b416879499 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -24,7 +24,6 @@
#include "station_base.h"
#include "station_kdtree.h"
#include "roadstop_base.h"
-#include "dock_base.h"
#include "industry.h"
#include "town.h"
#include "core/random_func.hpp"
@@ -75,7 +74,7 @@ Station::Station(TileIndex tile) :
SpecializedStation(tile),
bus_station(INVALID_TILE, 0, 0),
truck_station(INVALID_TILE, 0, 0),
- dock_station(INVALID_TILE, 0, 0),
+ ship_station(INVALID_TILE, 0, 0),
indtype(IT_INVALID),
time_since_load(255),
time_since_unload(255)
@@ -337,10 +336,10 @@ uint Station::GetCatchmentRadius() const
if (this->bus_stops != nullptr) ret = max(ret, CA_BUS);
if (this->truck_stops != nullptr) ret = max(ret, CA_TRUCK);
if (this->train_station.tile != INVALID_TILE) ret = max(ret, CA_TRAIN);
- if (this->docks != nullptr) ret = max(ret, CA_DOCK);
+ if (this->ship_station.tile != INVALID_TILE) ret = max(ret, CA_DOCK);
if (this->airport.tile != INVALID_TILE) ret = max(ret, this->airport.GetSpec()->catchment);
} else {
- if (this->bus_stops != nullptr || this->truck_stops != nullptr || this->train_station.tile != INVALID_TILE || this->docks != nullptr || this->airport.tile != INVALID_TILE) {
+ if (this->bus_stops != nullptr || this->truck_stops != nullptr || this->train_station.tile != INVALID_TILE || this->ship_station.tile != INVALID_TILE || this->airport.tile != INVALID_TILE) {
ret = CA_UNMODIFIED;
}
}
@@ -369,19 +368,11 @@ Rect Station::GetCatchmentRectUsingRadius(uint catchment_radius) const
return ret;
}
-bool Station::IsDockingTile(TileIndex tile) const
-{
- for (const Dock *d = this->docks; d != nullptr; d = d->next) {
- if (tile == d->GetDockingTile()) return true;
- }
- return false;
-}
-
bool Station::IsWithinRangeOfDockingTile(TileIndex tile, uint max_distance) const
{
if (DistanceManhattan(this->xy, tile) > _settings_game.station.station_spread + max_distance) return false;
- for (const Dock *d = this->docks; d != nullptr; d = d->next) {
- if (DistanceManhattan(d->GetDockingTile(), tile) <= max_distance) return true;
+ for (TileIndex dock_tile : this->docking_tiles) {
+ if (DistanceManhattan(dock_tile, tile) <= max_distance) return true;
}
return false;
}
diff --git a/src/station_base.h b/src/station_base.h
index cbbb041957..e75b051cc0 100644
--- a/src/station_base.h
+++ b/src/station_base.h
@@ -467,10 +467,11 @@ public:
TileArea bus_station; ///< Tile area the bus 'station' part covers
RoadStop *truck_stops; ///< All the truck stops
TileArea truck_station; ///< Tile area the truck 'station' part covers
- Dock *docks; ///< All the docks
- TileArea dock_station; ///< Tile area dock 'station' part covers
- Airport airport; ///< Tile area the airport covers
+ Airport airport; ///< Tile area the airport covers
+ TileArea ship_station; ///< Tile area the ship 'station' part covers
+ TileArea docking_station; ///< Tile area the docking tiles cover
+ std::vector docking_tiles; ///< Tile vector the docking tiles cover
IndustryType indtype; ///< Industry type to get the name from
@@ -506,8 +507,6 @@ public:
void RecomputeCatchment();
static void RecomputeCatchmentForAll();
- Dock *GetPrimaryDock() const { return docks; }
-
uint GetCatchmentRadius() const;
Rect GetCatchmentRectUsingRadius(uint radius) const;
inline Rect GetCatchmentRect() const
@@ -533,7 +532,6 @@ public:
return IsAirportTile(tile) && GetStationIndex(tile) == this->index;
}
- bool IsDockingTile(TileIndex tile) const;
bool IsWithinRangeOfDockingTile(TileIndex tile, uint max_distance) const;
uint32 GetNewGRFVariable(const ResolverObject &object, byte variable, byte parameter, bool *available) const override;
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 861132bcbd..3081b24848 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -40,7 +40,6 @@
#include "station_base.h"
#include "station_kdtree.h"
#include "roadstop_base.h"
-#include "dock_base.h"
#include "newgrf_railtype.h"
#include "newgrf_roadtype.h"
#include "waypoint_base.h"
@@ -58,6 +57,7 @@
#include "linkgraph/refresh.h"
#include "widgets/station_widget.h"
#include "zoning.h"
+#include "tunnelbridge_map.h"
#include "table/strings.h"
@@ -403,7 +403,7 @@ void Station::GetTileArea(TileArea *ta, StationType type) const
case STATION_DOCK:
case STATION_OILRIG:
- *ta = this->dock_station;
+ *ta = this->docking_station;
break;
default: NOT_REACHED();
@@ -1578,16 +1578,14 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32
return cost;
}
-static void MakeRailStationAreaSmaller(BaseStation *st)
+static TileArea MakeStationAreaSmaller(BaseStation *st, TileArea ta, bool (*func)(BaseStation *, TileIndex))
{
- TileArea ta = st->train_station;
-
restart:
/* too small? */
if (ta.w != 0 && ta.h != 0) {
/* check the left side, x = constant, y changes */
- for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(0, i));) {
+ for (uint i = 0; !func(st, ta.tile + TileDiffXY(0, i));) {
/* the left side is unused? */
if (++i == ta.h) {
ta.tile += TileDiffXY(1, 0);
@@ -1597,7 +1595,7 @@ restart:
}
/* check the right side, x = constant, y changes */
- for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(ta.w - 1, i));) {
+ for (uint i = 0; !func(st, ta.tile + TileDiffXY(ta.w - 1, i));) {
/* the right side is unused? */
if (++i == ta.h) {
ta.w--;
@@ -1606,7 +1604,7 @@ restart:
}
/* check the upper side, y = constant, x changes */
- for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, 0));) {
+ for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, 0));) {
/* the left side is unused? */
if (++i == ta.w) {
ta.tile += TileDiffXY(0, 1);
@@ -1616,7 +1614,7 @@ restart:
}
/* check the lower side, y = constant, x changes */
- for (uint i = 0; !st->TileBelongsToRailStation(ta.tile + TileDiffXY(i, ta.h - 1));) {
+ for (uint i = 0; !func(st, ta.tile + TileDiffXY(i, ta.h - 1));) {
/* the left side is unused? */
if (++i == ta.w) {
ta.h--;
@@ -1627,7 +1625,28 @@ restart:
ta.Clear();
}
- st->train_station = ta;
+ return ta;
+}
+
+static bool TileBelongsToRailStation(BaseStation *st, TileIndex tile)
+{
+ return st->TileBelongsToRailStation(tile);
+}
+
+static void MakeRailStationAreaSmaller(BaseStation *st)
+{
+ st->train_station = MakeStationAreaSmaller(st, st->train_station, TileBelongsToRailStation);
+}
+
+static bool TileBelongsToShipStation(BaseStation *st, TileIndex tile)
+{
+ return IsDockTile(tile) && GetStationIndex(tile) == st->index;
+}
+
+static void MakeShipStationAreaSmaller(Station *st)
+{
+ st->ship_station = MakeStationAreaSmaller(st, st->ship_station, TileBelongsToShipStation);
+ UpdateStationDockingTiles(st);
}
/**
@@ -2756,20 +2775,13 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
/* Distant join */
if (st == nullptr && distant_join) st = Station::GetIfValid(station_to_join);
- if (!Dock::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_DOCKS);
-
ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK);
if (ret.Failed()) return ret;
if (flags & DC_EXEC) {
- /* Create the dock and insert it into the list of docks. */
- Dock *dock = new Dock(slope_tile, flat_tile);
- dock->next = st->docks;
- st->docks = dock;
-
- st->dock_station.Add(slope_tile);
- st->dock_station.Add(flat_tile);
- st->AddFacility(FACIL_DOCK, slope_tile);
+ st->ship_station.Add(tile);
+ st->ship_station.Add(tile + TileOffsByDiagDir(direction));
+ st->AddFacility(FACIL_DOCK, tile);
st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY);
@@ -2780,7 +2792,8 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
Company::Get(st->owner)->infrastructure.station += 2;
- MakeDock(slope_tile, st->owner, st->index, direction, wc);
+ MakeDock(tile, st->owner, st->index, direction, wc);
+ UpdateStationDockingTiles(st);
st->AfterStationTileSetChange(true, STATION_DOCK);
ZoningMarkDirtyStationCoverageArea(st);
@@ -2789,6 +2802,85 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
}
+void RemoveDockingTile(TileIndex t)
+{
+ for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
+ TileIndex tile = t + TileOffsByDiagDir(d);
+ if (!IsValidTile(tile)) continue;
+
+ if (IsTileType(tile, MP_STATION)) {
+ UpdateStationDockingTiles(Station::GetByTile(tile));
+ } else if (IsTileType(tile, MP_INDUSTRY)) {
+ UpdateStationDockingTiles(Industry::GetByTile(tile)->neutral_station);
+ }
+ }
+}
+
+/**
+ * Clear docking tile status from tiles around a removed dock, if the tile has
+ * no neighbours which would keep it as a docking tile.
+ * @param tile Ex-dock tile to check.
+ */
+void ClearDockingTilesCheckingNeighbours(TileIndex tile)
+{
+ assert(IsValidTile(tile));
+
+ /* Clear and maybe re-set docking tile */
+ for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
+ TileIndex docking_tile = tile + TileOffsByDiagDir(d);
+ if (!IsValidTile(docking_tile)) continue;
+
+ if (IsPossibleDockingTile(docking_tile)) {
+ SetDockingTile(docking_tile, false);
+ CheckForDockingTile(docking_tile);
+ }
+ }
+}
+
+/**
+ * Check if a dock tile can be docked from the given direction.
+ * @param t Tile index of dock.
+ * @param d DiagDirection adjacent to dock being tested.
+ * @return True iff the dock can be docked from the given direction.
+ */
+bool IsValidDockingDirectionForDock(TileIndex t, DiagDirection d)
+{
+ assert(IsDockTile(t));
+
+ /** Bitmap of valid directions for each dock tile part. */
+ static const uint8 _valid_docking_tile[] = {
+ 0, 0, 0, 0, // No docking against the slope part.
+ 1 << DIAGDIR_NE | 1 << DIAGDIR_SW, // Docking permitted at the end
+ 1 << DIAGDIR_NW | 1 << DIAGDIR_SE, // of the flat piers.
+ };
+
+ StationGfx gfx = GetStationGfx(t);
+ assert(gfx < lengthof(_valid_docking_tile));
+ return HasBit(_valid_docking_tile[gfx], d);
+}
+
+/**
+ * Find the part of a dock that is land-based
+ * @param t Dock tile to find land part of
+ * @return tile of land part of dock
+ */
+static TileIndex FindDockLandPart(TileIndex t)
+{
+ assert(IsDockTile(t));
+
+ StationGfx gfx = GetStationGfx(t);
+ if (gfx < GFX_DOCK_BASE_WATER_PART) return t;
+
+ for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
+ TileIndex tile = t + TileOffsByDiagDir(d);
+ if (!IsValidTile(tile)) continue;
+ if (!IsDockTile(tile)) continue;
+ if (GetStationGfx(tile) < GFX_DOCK_BASE_WATER_PART && tile + TileOffsByDiagDir(GetDockDirection(tile)) == t) return tile;
+ }
+
+ return INVALID_TILE;
+}
+
/**
* Remove a dock
* @param tile TileIndex been queried
@@ -2801,14 +2893,11 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
CommandCost ret = CheckOwnership(st->owner);
if (ret.Failed()) return ret;
- Dock *removing_dock = Dock::GetByTile(tile);
- assert(removing_dock != nullptr);
+ if (!IsDockTile(tile)) return CMD_ERROR;
- TileIndex tile1 = removing_dock->sloped;
- TileIndex tile2 = removing_dock->flat;
-
- DiagDirection direction = DiagdirBetweenTiles(removing_dock->sloped, removing_dock->flat);
- TileIndex docking_location = removing_dock->flat + TileOffsByDiagDir(direction);
+ TileIndex tile1 = FindDockLandPart(tile);
+ if (tile1 == INVALID_TILE) return CMD_ERROR;
+ TileIndex tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
ret = EnsureNoVehicleOnGround(tile1);
if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
@@ -2817,22 +2906,6 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
if (flags & DC_EXEC) {
ZoningMarkDirtyStationCoverageArea(st);
- if (st->docks == removing_dock) {
- /* The first dock in the list is removed. */
- st->docks = removing_dock->next;
- /* Last dock is removed. */
- if (st->docks == nullptr) {
- st->facilities &= ~FACIL_DOCK;
- }
- } else {
- /* Tell the predecessor in the list to skip this dock. */
- Dock *pred = st->docks;
- while (pred->next != removing_dock) pred = pred->next;
- pred->next = removing_dock->next;
- }
-
- delete removing_dock;
-
DoClearSquare(tile1);
MarkTileDirtyByTile(tile1);
MakeWaterKeepingClass(tile2, st->owner);
@@ -2840,29 +2913,35 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)
st->rect.AfterRemoveTile(st, tile1);
st->rect.AfterRemoveTile(st, tile2);
- st->dock_station.Clear();
- for (Dock *dock = st->docks; dock != nullptr; dock = dock->next) {
- st->dock_station.Add(dock->flat);
- st->dock_station.Add(dock->sloped);
+ MakeShipStationAreaSmaller(st);
+ if (st->ship_station.tile == INVALID_TILE) {
+ st->ship_station.Clear();
+ st->docking_station.Clear();
+ st->docking_tiles.clear();
+ st->facilities &= ~FACIL_DOCK;
}
Company::Get(st->owner)->infrastructure.station -= 2;
st->AfterStationTileSetChange(false, STATION_DOCK);
+ ClearDockingTilesCheckingNeighbours(tile1);
+ ClearDockingTilesCheckingNeighbours(tile2);
+
/* All ships that were going to our station, can't go to it anymore.
* Just clear the order, then automatically the next appropriate order
* will be selected and in case of no appropriate order it will just
* wander around the world. */
- Ship *s;
- FOR_ALL_SHIPS(s) {
- if (s->current_order.IsType(OT_LOADING) && s->tile == docking_location) {
- s->LeaveStation();
- }
+ if (!(st->facilities & FACIL_DOCK)) {
+ Ship *s;
+ FOR_ALL_SHIPS(s) {
+ if (s->current_order.IsType(OT_LOADING) && s->current_order.GetDestination() == st->index) {
+ s->LeaveStation();
+ }
- if (s->dest_tile == docking_location) {
- s->SetDestTile(0);
- s->current_order.Free();
+ if (s->current_order.IsType(OT_GOTO_STATION) && s->current_order.GetDestination() == st->index) {
+ s->SetDestTile(s->GetOrderStationLocation(st->index));
+ }
}
}
}
@@ -3111,7 +3190,7 @@ draw_default_foundation:
} else {
assert_tile(IsDock(ti->tile), ti->tile);
TileIndex water_tile = ti->tile + TileOffsByDiagDir(GetDockDirection(ti->tile));
- WaterClass wc = GetWaterClass(water_tile);
+ WaterClass wc = HasTileWaterClass(water_tile) ? GetWaterClass(water_tile) : WATER_CLASS_INVALID;
if (wc == WATER_CLASS_SEA) {
DrawShoreTile(ti->tileh);
} else {
@@ -4243,6 +4322,33 @@ uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, Sourc
return moved + UpdateStationWaiting(st2, type, worst_cargo, source_type, source_id);
}
+void UpdateStationDockingTiles(Station *st)
+{
+ st->docking_station.Clear();
+ st->docking_tiles.clear();
+
+ /* For neutral stations, start with the industry area instead of dock area */
+ const TileArea *area = st->industry != nullptr ? &st->industry->location : &st->ship_station;
+
+ if (area->tile == INVALID_TILE) return;
+
+ int x = TileX(area->tile);
+ int y = TileY(area->tile);
+
+ /* Expand the area by a tile on each side while
+ * making sure that we remain inside the map. */
+ int x2 = min(x + area->w + 1, MapSizeX());
+ int x1 = max(x - 1, 0);
+
+ int y2 = min(y + area->h + 1, MapSizeY());
+ int y1 = max(y - 1, 0);
+
+ TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1));
+ TILE_AREA_LOOP(tile, ta) {
+ if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
+ }
+}
+
void BuildOilRig(TileIndex tile)
{
if (!Station::CanAllocateItem()) {
@@ -4266,18 +4372,10 @@ void BuildOilRig(TileIndex tile)
st->owner = OWNER_NONE;
st->airport.type = AT_OILRIG;
st->airport.Add(tile);
- st->dock_station.tile = tile;
- st->facilities = FACIL_AIRPORT;
-
- if (!Dock::CanAllocateItem()) {
- DEBUG(misc, 0, "Can't allocate dock for oilrig at 0x%X, reverting to oilrig with airport only", tile);
- } else {
- st->docks = new Dock(tile, tile + ToTileIndexDiff({1, 0}));
- st->dock_station.tile = tile;
- st->facilities |= FACIL_DOCK;
- }
-
+ st->ship_station.Add(tile);
+ st->facilities = FACIL_AIRPORT | FACIL_DOCK;
st->build_date = _date;
+ UpdateStationDockingTiles(st);
st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
@@ -4295,20 +4393,46 @@ void DeleteOilRig(TileIndex tile)
MakeWaterKeepingClass(tile, OWNER_NONE);
- st->dock_station.tile = INVALID_TILE;
- if (st->docks != nullptr) {
- delete st->docks;
- st->docks = nullptr;
+ assert(st->facilities == (FACIL_AIRPORT | FACIL_DOCK) && st->airport.type == AT_OILRIG);
+ delete st;
+ return;
+
+ MakeShipStationAreaSmaller(st);
+ if (st->ship_station.tile == INVALID_TILE) {
+ st->ship_station.Clear();
+ st->docking_station.Clear();
+ st->docking_tiles.clear();
+ st->facilities &= ~FACIL_DOCK;
}
st->airport.Clear();
- st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
+ st->facilities &= ~FACIL_AIRPORT;
st->airport.flags = 0;
st->rect.AfterRemoveTile(st, tile);
st->UpdateVirtCoord();
st->RecomputeCatchment();
- if (!st->IsInUse()) delete st;
+ if (!st->IsInUse()) {
+ delete st;
+ } else {
+ st->industry = nullptr;
+ /* All ships that were going to our station, can't go to it anymore.
+ * Just clear the order, then automatically the next appropriate order
+ * will be selected and in case of no appropriate order it will just
+ * wander around the world. */
+ if (!(st->facilities & FACIL_DOCK)) {
+ Ship *s;
+ FOR_ALL_SHIPS(s) {
+ if (s->current_order.IsType(OT_LOADING) && s->current_order.GetDestination() == st->index) {
+ s->LeaveStation();
+ }
+
+ if (s->current_order.IsType(OT_GOTO_STATION) && s->current_order.GetDestination() == st->index) {
+ s->SetDestTile(s->GetOrderStationLocation(st->index));
+ }
+ }
+ }
+ }
}
static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
diff --git a/src/station_func.h b/src/station_func.h
index 6823bdad5b..1a890e1ebd 100644
--- a/src/station_func.h
+++ b/src/station_func.h
@@ -41,6 +41,10 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro
bool HasStationInUse(StationID station, bool include_company, CompanyID company);
void DeleteOilRig(TileIndex t);
+void UpdateStationDockingTiles(Station *st);
+void RemoveDockingTile(TileIndex t);
+void ClearDockingTilesCheckingNeighbours(TileIndex tile);
+bool IsValidDockingDirectionForDock(TileIndex t, DiagDirection d);
/* Check if a rail station tile is traversable. */
bool IsStationTileBlocked(TileIndex tile);
diff --git a/src/station_map.h b/src/station_map.h
index 7947d4228a..4a992bc821 100644
--- a/src/station_map.h
+++ b/src/station_map.h
@@ -536,6 +536,7 @@ static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType
SetTileType(t, MP_STATION);
SetTileOwner(t, o);
SetWaterClass(t, wc);
+ SetDockingTile(t, false);
_m[t].m2 = sid;
_m[t].m3 = 0;
_m[t].m4 = 0;
diff --git a/src/station_type.h b/src/station_type.h
index bb246e0c77..c33f9d46bb 100644
--- a/src/station_type.h
+++ b/src/station_type.h
@@ -23,7 +23,6 @@ typedef uint16 DockID;
struct BaseStation;
struct Station;
struct RoadStop;
-struct Dock;
struct StationSpec;
struct Waypoint;
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index 66390cc7ef..d241c92050 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -49,6 +49,7 @@
#include "industry_map.h"
#include "object_map.h"
#include "newgrf_station.h"
+#include "station_func.h"
#include "table/strings.h"
#include "table/bridge_land.h"
@@ -626,6 +627,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
if (is_new_owner && c != nullptr) c->infrastructure.water += (bridge_len + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
MakeAqueductBridgeRamp(tile_start, owner, dir);
MakeAqueductBridgeRamp(tile_end, owner, ReverseDiagDir(dir));
+ CheckForDockingTile(tile_start);
+ CheckForDockingTile(tile_end);
break;
default:
@@ -1200,6 +1203,9 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
find_train_reservations(endtile);
}
+ bool removetile = false;
+ bool removeendtile = false;
+
/* Update company infrastructure counts. */
if (rail) {
SubtractRailTunnelBridgeInfrastructure(tile, endtile);
@@ -1213,6 +1219,8 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
}
} else { // Aqueduct
if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ removetile = IsDockingTile(tile);
+ removeendtile = IsDockingTile(endtile);
}
DirtyAllCompanyInfrastructureWindows();
@@ -1221,6 +1229,9 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
DoClearSquare(tile);
DoClearSquare(endtile);
+
+ if (removetile) RemoveDockingTile(tile);
+ if (removeendtile) RemoveDockingTile(endtile);
for (TileIndex c = tile + delta; c != endtile; c += delta) {
/* do not let trees appear from 'nowhere' after removing bridge */
if (IsNormalRoadTile(c) && GetRoadside(c) == ROADSIDE_TREES) {
diff --git a/src/water.h b/src/water.h
index 1b804720ff..9e2c23418a 100644
--- a/src/water.h
+++ b/src/water.h
@@ -38,6 +38,7 @@ void DrawWaterClassGround(const struct TileInfo *ti);
void DrawShoreTile(Slope tileh);
void MakeWaterKeepingClass(TileIndex tile, Owner o);
+void CheckForDockingTile(TileIndex t);
bool RiverModifyDesertZone(TileIndex tile, void *data);
static const uint RIVER_OFFSET_DESERT_DISTANCE = 5; ///< Circular tile search radius to create non-desert around a river tile.
diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp
index 537b970f73..ca9c7318f6 100644
--- a/src/water_cmd.cpp
+++ b/src/water_cmd.cpp
@@ -39,6 +39,7 @@
#include "company_base.h"
#include "company_gui.h"
#include "newgrf_generic.h"
+#include "industry.h"
#include "table/strings.h"
@@ -148,6 +149,8 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
+ CheckForDockingTile(tile);
+ CheckForDockingTile(tile2);
MarkTileDirtyByTile(tile);
MarkTileDirtyByTile(tile2);
MakeDefaultName(depot);
@@ -156,6 +159,51 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
return cost;
}
+bool IsPossibleDockingTile(TileIndex t)
+{
+ assert(IsValidTile(t));
+ switch (GetTileType(t)) {
+ case MP_WATER:
+ if (IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) return false;
+ FALLTHROUGH;
+ case MP_RAILWAY:
+ case MP_STATION:
+ case MP_TUNNELBRIDGE:
+ return TrackStatusToTrackBits(GetTileTrackStatus(t, TRANSPORT_WATER, 0)) != TRACK_BIT_NONE;
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Mark the supplied tile as a docking tile if it is suitable for docking.
+ * Tiles surrounding the tile are tested to be docks with correct orientation.
+ * @param t Tile to test.
+ */
+void CheckForDockingTile(TileIndex t)
+{
+ for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
+ TileIndex tile = t + TileOffsByDiagDir(d);
+ if (!IsValidTile(tile)) continue;
+
+ if (IsDockTile(tile) && IsValidDockingDirectionForDock(tile, d)) {
+ Station *st = Station::GetByTile(tile);
+ st->docking_station.Add(t);
+ st->docking_tiles.push_back(t);
+ SetDockingTile(t, true);
+ }
+ if (IsTileType(tile, MP_INDUSTRY)) {
+ Station *st = Industry::GetByTile(tile)->neutral_station;
+ if (st != nullptr) {
+ st->docking_station.Add(t);
+ st->docking_tiles.push_back(t);
+ SetDockingTile(t, true);
+ }
+ }
+ }
+}
+
void MakeWaterKeepingClass(TileIndex tile, Owner o)
{
WaterClass wc = GetWaterClass(tile);
@@ -204,6 +252,7 @@ void MakeWaterKeepingClass(TileIndex tile, Owner o)
default: break;
}
+ if (wc != WATER_CLASS_INVALID) CheckForDockingTile(tile);
MarkTileDirtyByTile(tile);
}
@@ -303,6 +352,8 @@ static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag
}
MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
+ CheckForDockingTile(tile - delta);
+ CheckForDockingTile(tile + delta);
MarkTileDirtyByTile(tile);
MarkTileDirtyByTile(tile - delta);
MarkTileDirtyByTile(tile + delta);
@@ -462,6 +513,7 @@ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
MarkTileDirtyByTile(tile);
MarkCanalsAndRiversAroundDirty(tile);
+ CheckForDockingTile(tile);
}
cost.AddCost(_price[PR_BUILD_CANAL]);
@@ -510,8 +562,10 @@ static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
Company::Get(owner)->infrastructure.water--;
DirtyCompanyInfrastructureWindows(owner);
}
+ bool remove = IsDockingTile(tile);
DoClearSquare(tile);
MarkCanalsAndRiversAroundDirty(tile);
+ if (remove) RemoveDockingTile(tile);
}
return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
@@ -531,8 +585,10 @@ static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
ret = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
}
if (flags & DC_EXEC) {
+ bool remove = IsDockingTile(tile);
DoClearSquare(tile);
MarkCanalsAndRiversAroundDirty(tile);
+ if (remove) RemoveDockingTile(tile);
}
return ret;
}
@@ -1118,6 +1174,8 @@ void DoFloodTile(TileIndex target)
/* update signals if needed */
UpdateSignalsInBuffer();
+
+ if (IsPossibleDockingTile(target)) CheckForDockingTile(target);
}
cur_company.Restore();
diff --git a/src/water_map.h b/src/water_map.h
index b87f9b2a13..bb006ba633 100644
--- a/src/water_map.h
+++ b/src/water_map.h
@@ -69,6 +69,8 @@ enum LockPart {
LOCK_PART_UPPER = 2, ///< Upper part of a lock.
};
+bool IsPossibleDockingTile(TileIndex t);
+
/**
* Get the water tile type at a tile.
* @param t Water tile to query.
@@ -346,6 +348,27 @@ static inline bool HasTileWaterGround(TileIndex t)
return HasTileWaterClass(t) && IsTileOnWater(t) && !IsCoastTile(t);
}
+/**
+ * Set the docking tile state of a tile. This is used by pathfinders to reach their destination.
+ * As well as water tiles, half-rail tiles, buoys and aqueduct ends can also be docking tiles.
+ * @param t the tile
+ * @param b the docking tile state
+ */
+static inline void SetDockingTile(TileIndex t, bool b)
+{
+ assert(IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE));
+ SB(_m[t].m1, 7, 1, b ? 1 : 0);
+}
+
+/**
+ * Checks whether the tile is marked as a dockling tile.
+ * @return true iff the tile is marked as a docking tile.
+ */
+static inline bool IsDockingTile(TileIndex t)
+{
+ return (IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)) && HasBit(_m[t].m1, 7);
+}
+
/**
* Helper function to make a coast tile.
@@ -356,6 +379,7 @@ static inline void MakeShore(TileIndex t)
SetTileType(t, MP_WATER);
SetTileOwner(t, OWNER_WATER);
SetWaterClass(t, WATER_CLASS_SEA);
+ SetDockingTile(t, false);
_m[t].m2 = 0;
_m[t].m3 = 0;
_m[t].m4 = 0;
@@ -376,6 +400,7 @@ static inline void MakeWater(TileIndex t, Owner o, WaterClass wc, uint8 random_b
SetTileType(t, MP_WATER);
SetTileOwner(t, o);
SetWaterClass(t, wc);
+ SetDockingTile(t, false);
_m[t].m2 = 0;
_m[t].m3 = 0;
_m[t].m4 = random_bits;
@@ -429,6 +454,7 @@ static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart pa
SetTileType(t, MP_WATER);
SetTileOwner(t, o);
SetWaterClass(t, original_water_class);
+ SetDockingTile(t, false);
_m[t].m2 = did;
_m[t].m3 = 0;
_m[t].m4 = 0;
@@ -451,6 +477,7 @@ static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirecti
SetTileType(t, MP_WATER);
SetTileOwner(t, o);
SetWaterClass(t, original_water_class);
+ SetDockingTile(t, false);
_m[t].m2 = 0;
_m[t].m3 = 0;
_m[t].m4 = 0;
diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp
index 957b31ca34..c1c0125e46 100644
--- a/src/waypoint_cmd.cpp
+++ b/src/waypoint_cmd.cpp
@@ -336,6 +336,7 @@ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (wp->town == nullptr) MakeDefaultName(wp);
MakeBuoy(tile, wp->index, GetWaterClass(tile));
+ CheckForDockingTile(tile);
MarkTileDirtyByTile(tile);
wp->UpdateVirtCoord();
| | | |