summaryrefslogtreecommitdiff
path: root/openttd-git
diff options
context:
space:
mode:
authorErich Eckner <git@eckner.net>2021-01-03 22:08:37 +0100
committerErich Eckner <git@eckner.net>2021-01-03 22:08:37 +0100
commit14db446f7428ae55907b15b10e89539a7eefc6ab (patch)
treeca770ea23c6697178366c7751a0ceded3723e428 /openttd-git
parenteda62ccedb226e760a41bf0f5af379b0add6b072 (diff)
downloadarchlinuxewe.git.save-14db446f7428ae55907b15b10e89539a7eefc6ab.tar.xz
openttd-git: fix chunnel.patch
Diffstat (limited to 'openttd-git')
-rw-r--r--openttd-git/PKGBUILD13
-rw-r--r--openttd-git/chunnel-pre.patch12
-rw-r--r--openttd-git/chunnel.patch914
3 files changed, 924 insertions, 15 deletions
diff --git a/openttd-git/PKGBUILD b/openttd-git/PKGBUILD
index 4c1d5542..bd7a052c 100644
--- a/openttd-git/PKGBUILD
+++ b/openttd-git/PKGBUILD
@@ -2,7 +2,7 @@
pkgname=openttd-git
pkgver=29779.8596b43b2
_commit=${pkgver#*.}
-pkgrel=1
+pkgrel=2
pkgdesc="A FOSS clone of Transport Tycoon Deluxe."
arch=('x86_64' 'i686' 'pentium4')
url="http://www.openttd.org"
@@ -43,6 +43,7 @@ backup=()
options=()
source=(
"${pkgname}::git://github.com/OpenTTD/OpenTTD.git#commit=${_commit}"
+ "chunnel-pre.patch"
"chunnel.patch"
"signaltunnel.patch"
"sloped-stations.patch"
@@ -50,7 +51,8 @@ source=(
'opntitle.dat'
)
sha512sums=('SKIP'
- 'a6cf640d876aa1e2d20ec9242a602bca8a3e2dfabe036b36bf71872d47ab06d668c24d175c54c5f6e54d3c135ebcbcf3c09b5d762fddceb15f2cc87baad9d76f'
+ 'fa238b9ea9a2d334bdabcbc667b87eae87748d510d6d2bb3308f5186b6b613b7a50623e42c075cd4261cfd4aa4a6822902d7d6536e1805a1ac00a6e885bc9ae7'
+ '74283b8adb001a010aa05a8cee33b620371544e2909317f4c7156852e4ea440cdf8793dbe8957d646734ee309fdb5924915e0d967d78d43329ea4fc211e7e8ef'
'497f395320edff647dd58c53abc451f706db9934fc847c95ecbbb33d519f67fe94ecd913e13bd7d770320370ea088b675e6583e8b59b69e2d3ee587a62ad2261'
'11c87d30824bca5738e46c14422c622cdfe4d7e55a5b3a4fe5e77a0a14ceee742bfa8f6c0e8212e9b38c2a1d1db3a4f525c7a70a0b0ed03751dee2539d3da929'
'f18a389800e5641ebc55d4322089d97a851125ae6d19ff944d3648b3b603128fe0fcb7ba2210cbd13ac8a0839003144299c217b994bdc0bc5e601fbff63308d2'
@@ -92,11 +94,16 @@ prepare() {
' cmake/scripts/FindVersion.cmake
# for _p in "${srcdir}/"{signaltunnel,sloped-stations,underground,chunnel}.patch; do
- for _p in "${srcdir}/"{signaltunnel,underground,chunnel}.patch; do
+ for _p in "${srcdir}/"{signaltunnel,underground}.patch; do
>&2 echo "patching ${_p##*/} ..."
patch -p1 -i "${_p}"
>&2 echo "... ok"
done
+ (
+ cd "${srcdir}"
+ patch -p2 -i "chunnel-pre.patch" -o -
+ ) \
+ | patch -p1
}
build() {
diff --git a/openttd-git/chunnel-pre.patch b/openttd-git/chunnel-pre.patch
new file mode 100644
index 00000000..9b42f5a9
--- /dev/null
+++ b/openttd-git/chunnel-pre.patch
@@ -0,0 +1,12 @@
+diff --git a/openttd-git/chunnel.patch b/openttd-git/chunnel.patch
+index 33ab7405..6ad137ae 100644
+--- a/openttd-git/chunnel.patch
++++ b/openttd-git/chunnel.patch
+@@ -1018,6 +1018,7 @@ index e014944a9f..cb6239f597 100644
+ const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
+
+ while (v != nullptr) {
++ if (LayerIndex(v->tile) == dpi->layer)
+ - if (!(v->vehstatus & VS_HIDDEN) &&
+ + if (v->IsDrawn() &&
+ l <= v->coord.right &&
diff --git a/openttd-git/chunnel.patch b/openttd-git/chunnel.patch
index c26ea894..33ab7405 100644
--- a/openttd-git/chunnel.patch
+++ b/openttd-git/chunnel.patch
@@ -1,8 +1,178 @@
+diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
+index bbe66b7645..7fb18d527a 100644
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -442,6 +442,7 @@ add_files(
+ tree_cmd.cpp
+ tree_gui.cpp
+ tree_map.h
++ tunnel_base.h
+ tunnel_map.cpp
+ tunnel_map.h
+ tunnelbridge.h
+diff --git a/src/ground_vehicle.cpp b/src/ground_vehicle.cpp
+index 0f1915c964..0a734a5b55 100644
+--- a/src/ground_vehicle.cpp
++++ b/src/ground_vehicle.cpp
+@@ -11,6 +11,8 @@
+ #include "train.h"
+ #include "roadveh.h"
+ #include "depot_map.h"
++#include "tunnel_base.h"
++#include "slope_type.h"
+
+ #include "safeguards.h"
+
+@@ -203,6 +205,69 @@ bool GroundVehicle<T, Type>::IsChainInDepot() const
+ return true;
+ }
+
++/**
++ * Updates vehicle's Z inclination inside a wormhole, where applicable.
++ */
++template <class T, VehicleType Type>
++void GroundVehicle<T, Type>::UpdateZPositionInWormhole()
++{
++ if (!IsTunnel(this->tile)) return;
++
++ const Tunnel *t = Tunnel::GetByTile(this->tile);
++ if (!t->is_chunnel) return;
++
++ TileIndex pos_tile = TileVirtXY(this->x_pos, this->y_pos);
++
++ ClrBit(this->gv_flags, GVF_GOINGUP_BIT);
++ ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT);
++
++ if (pos_tile == t->tile_n || pos_tile == t->tile_s) {
++ this->z_pos = 0;
++ return;
++ }
++
++ int north_coord, south_coord, pos_coord;
++ Slope slope = SLOPE_FLAT;
++ int delta;
++ if (t->tile_s - t->tile_n > MapMaxX()) {
++ // tunnel extends along Y axis (DIAGDIR_SE from north end), has same X values
++ north_coord = TileY(t->tile_n);
++ south_coord = TileY(t->tile_s);
++ pos_coord = TileY(pos_tile);
++
++ if ((delta = pos_coord - north_coord) <= 3) {
++ this->z_pos = TILE_HEIGHT * (delta == 3 ? -2 : -1);
++ if (delta != 2) {
++ slope = SLOPE_NW;
++ SetBit(this->gv_flags, this->direction == DIR_NW ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT);
++ }
++ } else if ((delta = south_coord - pos_coord) <= 3) {
++ this->z_pos = TILE_HEIGHT * (delta == 3 ? -2 : -1);
++ if (delta != 2) {
++ slope = SLOPE_ELEVATED ^ SLOPE_NW;
++ SetBit(this->gv_flags, this->direction == DIR_NW ? GVF_GOINGDOWN_BIT : GVF_GOINGUP_BIT);
++ }
++ }
++ } else {
++ // tunnel extends along X axis (DIAGDIR_SW from north end), has same Y values
++ north_coord = TileX(t->tile_n);
++ south_coord = TileX(t->tile_s);
++ pos_coord = TileX(pos_tile);
++
++ if ((delta = pos_coord - north_coord) <= 3) {
++ this->z_pos = TILE_HEIGHT * (delta == 3 ? -3 : (delta == 2 ? -2 : -1));
++ slope = SLOPE_NE;
++ SetBit(this->gv_flags, this->direction == DIR_NE ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT);
++ } else if ((delta = south_coord - pos_coord) <= 3) {
++ this->z_pos = TILE_HEIGHT * (delta == 3 ? -3 : (delta == 2 ? -2 : -1));
++ slope = SLOPE_ELEVATED ^ SLOPE_NE;
++ SetBit(this->gv_flags, this->direction == DIR_NE ? GVF_GOINGDOWN_BIT : GVF_GOINGUP_BIT);
++ }
++ }
++
++ if (slope != SLOPE_FLAT) this->z_pos += GetPartialPixelZ(this->x_pos & 0xF, this->y_pos & 0xF, slope);
++}
++
+ /* Instantiation for Train */
+ template struct GroundVehicle<Train, VEH_TRAIN>;
+ /* Instantiation for RoadVehicle */
+diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp
+index af6e25c806..ae4e9ead11 100644
+--- a/src/ground_vehicle.hpp
++++ b/src/ground_vehicle.hpp
+@@ -14,6 +14,7 @@
+ #include "vehicle_gui.h"
+ #include "landscape.h"
+ #include "window_func.h"
++#include "tunnel_map.h"
+ #include "widgets/vehicle_widget.h"
+
+ /** What is the status of our acceleration? */
+@@ -52,6 +53,7 @@ enum GroundVehicleFlags {
+ GVF_GOINGUP_BIT = 0, ///< Vehicle is currently going uphill. (Cached track information for acceleration)
+ GVF_GOINGDOWN_BIT = 1, ///< Vehicle is currently going downhill. (Cached track information for acceleration)
+ GVF_SUPPRESS_IMPLICIT_ORDERS = 2, ///< Disable insertion and removal of automatic orders until the vehicle completes the real order.
++ GVF_CHUNNEL_BIT = 3, ///< Vehicle may currently be in a chunnel. (Cached track information for inclination changes)
+ };
+
+ /**
+@@ -220,20 +222,28 @@ struct GroundVehicle : public SpecializedVehicle<T, Type> {
+ this->z_pos += HasBit(this->gv_flags, GVF_GOINGUP_BIT) ? d : -d;
+ }
+
++ if (HasBit(this->gv_flags, GVF_CHUNNEL_BIT) && !IsTunnelTile(this->tile)) {
++ ClrBit(this->gv_flags, GVF_CHUNNEL_BIT);
++ }
++
+ assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos));
+ }
+
++ void UpdateZPositionInWormhole();
++
+ /**
+ * Checks if the vehicle is in a slope and sets the required flags in that case.
+ * @param new_tile True if the vehicle reached a new tile.
+ * @param update_delta Indicates to also update the delta.
+ * @return Old height of the vehicle.
+ */
+- inline int UpdateInclination(bool new_tile, bool update_delta)
++ inline int UpdateInclination(bool new_tile, bool update_delta, bool in_wormhole = false)
+ {
+ int old_z = this->z_pos;
+
+- if (new_tile) {
++ if (in_wormhole) {
++ if (HasBit(this->gv_flags, GVF_CHUNNEL_BIT)) this->UpdateZPositionInWormhole();
++ } else if (new_tile) {
+ this->UpdateZPositionAndInclination();
+ } else {
+ this->UpdateZPosition();
+diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp
+index 816146212f..5491936557 100644
+--- a/src/industry_cmd.cpp
++++ b/src/industry_cmd.cpp
+@@ -20,6 +20,7 @@
+ #include "company_base.h"
+ #include "genworld.h"
+ #include "tree_map.h"
++#include "tunnel_map.h"
+ #include "newgrf_cargo.h"
+ #include "newgrf_debug.h"
+ #include "newgrf_industrytiles.h"
+@@ -1521,6 +1522,11 @@ static CommandCost CheckIfIndustryIsAllowed(TileIndex tile, int type, const Town
+ return_cmd_error(STR_ERROR_CAN_ONLY_BE_BUILT_NEAR_TOWN_CENTER);
+ }
+
++ if (type == IT_OIL_RIG &&
++ (IsTunnelInWay(tile, 0) ||
++ IsTunnelInWay(tile + TileDiffXY(0, 1), 0) ||
++ IsTunnelInWay(tile + TileDiffXY(1, 2), 0))) return_cmd_error(STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL);
++
+ return CommandCost();
+ }
+
diff --git a/src/lang/english.txt b/src/lang/english.txt
-index 8a79d87ea6..c6169a78ec 100644
+index 1751e360f0..ce8baa7a4c 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
-@@ -2337,6 +2337,7 @@ STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Toggle t
+@@ -2325,6 +2325,7 @@ STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Toggle t
STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Toggle transparency for structures like lighthouses and antennas. Ctrl+Click to lock
STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Toggle transparency for catenary. Ctrl+Click to lock
STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Toggle transparency for loading indicators. Ctrl+Click to lock
@@ -10,6 +180,215 @@ index 8a79d87ea6..c6169a78ec 100644
STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Set objects invisible instead of transparent
# Linkgraph legend window
+@@ -2724,7 +2725,9 @@ STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT :Ship depot
+ # Industries come directly from their industry names
+
+ STR_LAI_TUNNEL_DESCRIPTION_RAILROAD :Railway tunnel
++STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_CHUNNEL :Railway tunnel (underwater)
+ STR_LAI_TUNNEL_DESCRIPTION_ROAD :Road tunnel
++STR_LAI_TUNNEL_DESCRIPTION_ROAD_CHUNNEL :Road tunnel (underwater)
+
+ STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL :Steel suspension rail bridge
+ STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL :Steel girder rail bridge
+@@ -4542,6 +4545,10 @@ STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY :{WHITE}Another
+ STR_ERROR_TUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel would end out of the map
+ STR_ERROR_UNABLE_TO_EXCAVATE_LAND :{WHITE}Unable to excavate land for other end of tunnel
+ STR_ERROR_TUNNEL_TOO_LONG :{WHITE}... tunnel too long
++STR_ERROR_TUNNEL_RAMP_TOO_SHORT :{WHITE}... ramp too short, tunnels under water must have a ramp at least three tiles long at both ends.
++STR_ERROR_TUNNEL_TOO_MANY :{WHITE}... too many tunnels
++STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL :{WHITE}No oil rigs allowed above underwater tunnels.
++STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY_FOR_CHUNNEL :{WHITE}Three tiles are needed to pass under the other tunnel.
+
+ # Object related errors
+ STR_ERROR_TOO_MANY_OBJECTS :{WHITE}... too many objects
+diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
+index 0e490fbc21..73c4648926 100644
+--- a/src/roadveh_cmd.cpp
++++ b/src/roadveh_cmd.cpp
+@@ -1182,6 +1182,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
+ v->x_pos = gp.x;
+ v->y_pos = gp.y;
+ v->UpdatePosition();
++ RoadZPosAffectSpeed(v, v->UpdateInclination(false, false, true));
+ if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true);
+ return true;
+ }
+@@ -1549,7 +1550,7 @@ again:
+ v->x_pos = x;
+ v->y_pos = y;
+ v->UpdatePosition();
+- RoadZPosAffectSpeed(v, v->UpdateInclination(false, true));
++ RoadZPosAffectSpeed(v, v->UpdateInclination(false, true, v->state == RVSB_WORMHOLE));
+ return true;
+ }
+
+diff --git a/src/saveload/CMakeLists.txt b/src/saveload/CMakeLists.txt
+index 52f103fa7e..c888a899cd 100644
+--- a/src/saveload/CMakeLists.txt
++++ b/src/saveload/CMakeLists.txt
+@@ -38,6 +38,7 @@ add_files(
+ story_sl.cpp
+ subsidy_sl.cpp
+ town_sl.cpp
++ tunnel_sl.cpp
+ vehicle_sl.cpp
+ waypoint_sl.cpp
+ )
+diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
+index 329152ab27..7ebefb7ad7 100644
+--- a/src/saveload/afterload.cpp
++++ b/src/saveload/afterload.cpp
+@@ -11,6 +11,7 @@
+ #include "../void_map.h"
+ #include "../signs_base.h"
+ #include "../depot_base.h"
++#include "../tunnel_base.h"
+ #include "../fios.h"
+ #include "../gamelog_internal.h"
+ #include "../network/network.h"
+@@ -534,6 +535,25 @@ static inline bool MayHaveBridgeAbove(TileIndex t)
+ IsTileType(t, MP_WATER) || IsTileType(t, MP_TUNNELBRIDGE) || IsTileType(t, MP_OBJECT);
+ }
+
++TileIndex GetOtherTunnelBridgeEndOld(TileIndex tile)
++{
++ DiagDirection dir = GetTunnelBridgeDirection(tile);
++ TileIndexDiff delta = TileOffsByDiagDir(dir);
++ int z = GetTileZ(tile);
++
++ dir = ReverseDiagDir(dir);
++ do {
++ tile += delta;
++ } while (
++ !IsTunnelTile(tile) ||
++ GetTunnelBridgeDirection(tile) != dir ||
++ GetTileZ(tile) != z
++ );
++
++ return tile;
++}
++
++
+ /**
+ * Perform a (large) amount of savegame conversion *magic* in order to
+ * load older savegames and to fill the caches for various purposes.
+@@ -1944,6 +1964,32 @@ bool AfterLoadGame()
+ }
+ }
+
++ /* Tunnel pool has to be initiated before reservations. */
++ if (IsSavegameVersionBefore(SLV_196)) {
++ for (TileIndex t = 0; t < map_size; t++) {
++ if (IsTunnelTile(t)) {
++ DiagDirection dir = GetTunnelBridgeDirection(t);
++ if (dir == DIAGDIR_SE || dir == DIAGDIR_SW) {
++ TileIndex start_tile = t;
++ TileIndex end_tile = GetOtherTunnelBridgeEndOld(start_tile);
++
++ if (!Tunnel::CanAllocateItem()) {
++ SetSaveLoadError(STR_ERROR_TUNNEL_TOO_MANY);
++ /* Restore the signals */
++ ResetSignalHandlers();
++ return false;
++ }
++
++ const Tunnel *t = new Tunnel(start_tile, end_tile, false);
++
++ _m[start_tile].m2 = t->index;
++ _m[end_tile].m2 = t->index;
++ }
++ }
++ }
++ }
++
++
+ /* Move the signal variant back up one bit for PBS. We don't convert the old PBS
+ * format here, as an old layout wouldn't work properly anyway. To be safe, we
+ * clear any possible PBS reservations as well. */
+@@ -2547,7 +2593,7 @@ bool AfterLoadGame()
+ } else if (dir == ReverseDiagDir(vdir)) { // Leaving tunnel
+ hidden = frame < TILE_SIZE - _tunnel_visibility_frame[dir];
+ /* v->tile changes at the moment when the vehicle leaves the tunnel. */
+- v->tile = hidden ? GetOtherTunnelBridgeEnd(vtile) : vtile;
++ v->tile = hidden ? GetOtherTunnelBridgeEndOld(vtile) : vtile;
+ } else {
+ /* We could get here in two cases:
+ * - for road vehicles, it is reversing at the end of the tunnel
+diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
+index 1b79ad926b..69e8b60905 100644
+--- a/src/saveload/saveload.cpp
++++ b/src/saveload/saveload.cpp
+@@ -248,6 +248,7 @@ extern const ChunkHandler _linkgraph_chunk_handlers[];
+ extern const ChunkHandler _airport_chunk_handlers[];
+ extern const ChunkHandler _object_chunk_handlers[];
+ extern const ChunkHandler _persistent_storage_chunk_handlers[];
++extern const ChunkHandler _tunnel_chunk_handlers[];
+
+ /** Array of all chunks in a savegame, \c nullptr terminated. */
+ static const ChunkHandler * const _chunk_handlers[] = {
+@@ -284,6 +285,8 @@ static const ChunkHandler * const _chunk_handlers[] = {
+ _airport_chunk_handlers,
+ _object_chunk_handlers,
+ _persistent_storage_chunk_handlers,
++ _tunnel_chunk_handlers,
++
+ nullptr,
+ };
+
+diff --git a/src/saveload/tunnel_sl.cpp b/src/saveload/tunnel_sl.cpp
+new file mode 100644
+index 0000000000..7ad37b12b9
+--- /dev/null
++++ b/src/saveload/tunnel_sl.cpp
+@@ -0,0 +1,48 @@
++/* $Id$ */
++
++/*
++ * 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 <http://www.gnu.org/licenses/>.
++ */
++
++/** @file tunnel_sl.cpp Code handling saving and loading of tunnels */
++
++#include "../stdafx.h"
++#include "../tunnel_base.h"
++
++#include "saveload.h"
++
++#include "../safeguards.h"
++
++
++static const SaveLoad _tunnel_desc[] = {
++ SLE_CONDVAR(Tunnel, tile_n, SLE_UINT32, SLV_196, SL_MAX_VERSION),
++ SLE_CONDVAR(Tunnel, tile_s, SLE_UINT32, SLV_196, SL_MAX_VERSION),
++ SLE_CONDVAR(Tunnel, is_chunnel, SLE_BOOL, SLV_196, SL_MAX_VERSION),
++ SLE_END()
++};
++
++static void Save_TUNN()
++{
++ for(Tunnel *tunnel : Tunnel::Iterate()) {
++ SlSetArrayIndex(tunnel->index);
++ SlObject(tunnel, _tunnel_desc);
++ }
++}
++
++static void Load_TUNN()
++{
++ int index;
++
++ while ((index = SlIterateArray()) != -1) {
++ Tunnel *tunnel = new (index) Tunnel();
++ SlObject(tunnel, _tunnel_desc);
++ }
++}
++
++
++extern const ChunkHandler _tunnel_chunk_handlers[] = {
++ { 'TUNN', Save_TUNN, Load_TUNN, nullptr, nullptr, CH_ARRAY | CH_LAST},
++};
diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini
index edad848165..f0322ee359 100644
--- a/src/table/misc_settings.ini
@@ -32,11 +411,57 @@ index edad848165..f0322ee359 100644
cat = SC_BASIC
[SDTG_VAR]
+diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp
+index 85edb73128..1a902dd59c 100644
+--- a/src/terraform_cmd.cpp
++++ b/src/terraform_cmd.cpp
+@@ -268,7 +268,7 @@ CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
+ }
+ }
+ /* Check if tunnel would take damage */
+- if (direction == -1 && IsTunnelInWay(tile, z_min)) {
++ if (direction == -1 && IsTunnelInWay(tile, z_min, true)) {
+ _terraform_err_tile = tile; // highlight the tile above the tunnel
+ return_cmd_error(STR_ERROR_EXCAVATION_WOULD_DAMAGE);
+ }
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
-index 5d02764b55..6438e18661 100644
+index 5d02764b55..cc0455f8f9 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
-@@ -3853,7 +3853,7 @@ static bool TrainLocoHandler(Train *v, bool mode)
+@@ -1563,6 +1563,7 @@ static void UpdateStatusAfterSwap(Train *v)
+ }
+
+ v->UpdatePosition();
++ if (v->track == TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true);
+ v->UpdateViewport(true, true);
+ }
+
+@@ -3326,6 +3327,15 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
+ v->x_pos = gp.x;
+ v->y_pos = gp.y;
+ v->UpdatePosition();
++ if (v->track == TRACK_BIT_WORMHOLE) {
++ /* update the Z position of the vehicle */
++ int old_z = v->UpdateInclination(false, false, true);
++
++ if (prev == nullptr) {
++ /* This is the first vehicle in the train */
++ AffectSpeedByZChange(v, old_z);
++ }
++ }
+ if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true);
+ continue;
+ }
+@@ -3339,7 +3349,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
+ v->UpdatePosition();
+
+ /* update the Z position of the vehicle */
+- int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false);
++ int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false, v->track == TRACK_BIT_WORMHOLE);
+
+ if (prev == nullptr) {
+ /* This is the first vehicle in the train */
+@@ -3853,7 +3863,7 @@ static bool TrainLocoHandler(Train *v, bool mode)
}
for (Train *u = v; u != nullptr; u = u->Next()) {
@@ -58,7 +483,7 @@ index 54ba24e933..08b13ca9b9 100644
TO_INVALID, ///< Invalid transparency option
};
diff --git a/src/transparency_gui.cpp b/src/transparency_gui.cpp
-index 153dcb5d03..55ac1a75de 100644
+index 153dcb5d03..611094cdeb 100644
--- a/src/transparency_gui.cpp
+++ b/src/transparency_gui.cpp
@@ -50,7 +50,8 @@ public:
@@ -66,7 +491,7 @@ index 153dcb5d03..55ac1a75de 100644
case WID_TT_STRUCTURES:
case WID_TT_CATENARY:
- case WID_TT_LOADING: {
-+ case WID_TT_LOADING:
++ case WID_TT_LOADING:
+ case WID_TT_TUNNELS: {
uint i = widget - WID_TT_BEGIN;
if (HasBit(_transparency_lock, i)) DrawSprite(SPR_LOCK, PAL_NONE, r.left + 1, r.top + 1);
@@ -96,11 +521,476 @@ index 153dcb5d03..55ac1a75de 100644
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(1, 1), EndContainer(),
EndContainer(),
/* Panel with 'invisibility' buttons. */
+diff --git a/src/tunnel_base.h b/src/tunnel_base.h
+new file mode 100644
+index 0000000000..4a2493fa7f
+--- /dev/null
++++ b/src/tunnel_base.h
+@@ -0,0 +1,39 @@
++/* $Id$ */
++
++/*
++ * 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 <http://www.gnu.org/licenses/>.
++ */
++
++/** @file tunnel_base.h Base for all tunnels */
++
++#ifndef TUNNEL_BASE_H
++#define TUNNEL_BASE_H
++
++#include "tunnel_map.h"
++#include "core/pool_type.hpp"
++
++struct Tunnel;
++
++typedef Pool<Tunnel, TunnelID, 64, 64000> TunnelPool;
++extern TunnelPool _tunnel_pool;
++
++struct Tunnel : TunnelPool::PoolItem<&_tunnel_pool> {
++
++ TileIndex tile_n; // North tile of tunnel.
++ TileIndex tile_s; // South tile of tunnel.
++ bool is_chunnel;
++
++ Tunnel() {}
++ Tunnel(TileIndex tile_n, TileIndex tile_s, bool is_chunnel) : tile_n(tile_n), tile_s(tile_s), is_chunnel(is_chunnel) {}
++ ~Tunnel();
++
++ static inline Tunnel *GetByTile(TileIndex tile)
++ {
++ return Tunnel::Get(GetTunnelIndex(tile));
++ }
++};
++
++#endif /* TUNNEL_BASE_H */
+diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp
+index 9de5235801..6234193e1e 100644
+--- a/src/tunnel_map.cpp
++++ b/src/tunnel_map.cpp
+@@ -10,63 +10,62 @@
+ #include "stdafx.h"
+ #include "tunnelbridge_map.h"
+
++#include "core/pool_func.hpp"
++
+ #include "safeguards.h"
+
++/** All tunnel portals tucked away in a pool. */
++TunnelPool _tunnel_pool("Tunnel");
++INSTANTIATE_POOL_METHODS(Tunnel)
+
+ /**
+- * Gets the other end of the tunnel. Where a vehicle would reappear when it
+- * enters at the given tile.
+- * @param tile the tile to search from.
+- * @return the tile of the other end of the tunnel.
++ * Clean up a tunnel tile
+ */
+-TileIndex GetOtherTunnelEnd(TileIndex tile)
++Tunnel::~Tunnel()
+ {
+- DiagDirection dir = GetTunnelBridgeDirection(tile);
+- TileIndexDiff delta = TileOffsByDiagDir(dir);
+- int z = GetTileZ(tile);
+-
+- dir = ReverseDiagDir(dir);
+- do {
+- tile += delta;
+- } while (
+- !IsTunnelTile(tile) ||
+- GetTunnelBridgeDirection(tile) != dir ||
+- GetTileZ(tile) != z
+- );
+-
+- return tile;
++ if (CleaningPool()) return;
+ }
+
+-
+ /**
+- * Is there a tunnel in the way in the given direction?
++ * Gets the other end of the tunnel. Where a vehicle would reappear when it
++ * enters at the given tile.
+ * @param tile the tile to search from.
+- * @param z the 'z' to search on.
+- * @param dir the direction to start searching to.
+- * @return true if and only if there is a tunnel.
++ * @return the tile of the other end of the tunnel.
+ */
+-bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir)
++TileIndex GetOtherTunnelEnd(TileIndex tile)
+ {
+- TileIndexDiff delta = TileOffsByDiagDir(dir);
+- int height;
+-
+- do {
+- tile -= delta;
+- if (!IsValidTile(tile)) return false;
+- height = GetTileZ(tile);
+- } while (z < height);
+-
+- return z == height && IsTunnelTile(tile) && GetTunnelBridgeDirection(tile) == dir;
++ Tunnel *t = Tunnel::GetByTile(tile);
++ return t->tile_n == tile ? t->tile_s : t->tile_n;
+ }
+
+ /**
+ * Is there a tunnel in the way in any direction?
+ * @param tile the tile to search from.
+ * @param z the 'z' to search on.
++ * @param chunnel_allowed True if chunnel mid-parts are allowed, used when terraforming.
+ * @return true if and only if there is a tunnel.
+ */
+-bool IsTunnelInWay(TileIndex tile, int z)
++bool IsTunnelInWay(TileIndex tile, int z, bool chunnel_allowed)
+ {
+- return IsTunnelInWayDir(tile, z, (TileX(tile) > (MapMaxX() / 2)) ? DIAGDIR_NE : DIAGDIR_SW) ||
+- IsTunnelInWayDir(tile, z, (TileY(tile) > (MapMaxY() / 2)) ? DIAGDIR_NW : DIAGDIR_SE);
++ uint x = TileX(tile);
++ uint y = TileY(tile);
++
++ for(Tunnel *t : Tunnel::Iterate()) {
++ if (t->tile_n > tile || tile > t->tile_s) continue;
++
++ if (t->tile_s - t->tile_n > MapMaxX()){
++ if (TileX(t->tile_n) != x || (int)TileHeight(t->tile_n) != z) continue; // dir DIAGDIR_SE
++ } else {
++ if (TileY(t->tile_n) != y || (int)TileHeight(t->tile_n) != z) continue; // dir DIAGDIR_SW
++ }
++
++ if (t->is_chunnel && chunnel_allowed) {
++ /* Only if tunnel was build over water terraforming is allowed between portals. */
++ TileIndexDiff delta = GetTunnelBridgeDirection(t->tile_n) == DIAGDIR_SE ? TileOffsByDiagDir(DIAGDIR_SE) * 4 : 4; // 4 tiles ramp.
++ if (tile < t->tile_n + delta || t->tile_s - delta < tile) return true;
++ continue;
++ }
++ return true;
++ }
++ return false;
+ }
+diff --git a/src/tunnel_map.h b/src/tunnel_map.h
+index c347626d55..45589dacb4 100644
+--- a/src/tunnel_map.h
++++ b/src/tunnel_map.h
+@@ -13,6 +13,7 @@
+ #include "rail_map.h"
+ #include "road_map.h"
+
++typedef uint16 TunnelID; ///< Type for the unique identifier of tunnels.
+
+ /**
+ * Is this a tunnel (entrance)?
+@@ -36,22 +37,34 @@ static inline bool IsTunnelTile(TileIndex t)
+ return IsTileType(t, MP_TUNNELBRIDGE) && IsTunnel(t);
+ }
+
++/**
++ * Get the index of tunnel tile.
++ * @param t the tile
++ * @pre IsTunnelTile(t)
++ * @return TunnelID
++ */
++static inline TunnelID GetTunnelIndex(TileIndex t)
++{
++ assert(IsTunnelTile(t));
++ return _m[t].m2;
++}
++
+ TileIndex GetOtherTunnelEnd(TileIndex);
+-bool IsTunnelInWay(TileIndex, int z);
+-bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir);
++bool IsTunnelInWay(TileIndex, int z, bool chunnel_allowed = false);
+
+ /**
+ * Makes a road tunnel entrance
+ * @param t the entrance of the tunnel
+ * @param o the owner of the entrance
++ * @param id the tunnel ID
+ * @param d the direction facing out of the tunnel
+ * @param r the road type used in the tunnel
+ */
+-static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadType road_rt, RoadType tram_rt)
++static inline void MakeRoadTunnel(TileIndex t, Owner o, TunnelID id, DiagDirection d, RoadType road_rt, RoadType tram_rt)
+ {
+ SetTileType(t, MP_TUNNELBRIDGE);
+ SetTileOwner(t, o);
+- _m[t].m2 = 0;
++ _m[t].m2 = id;
+ _m[t].m3 = 0;
+ _m[t].m4 = 0;
+ _m[t].m5 = TRANSPORT_ROAD << 2 | d;
+@@ -67,14 +80,15 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTyp
+ * Makes a rail tunnel entrance
+ * @param t the entrance of the tunnel
+ * @param o the owner of the entrance
++ * @param id the tunnel ID
+ * @param d the direction facing out of the tunnel
+ * @param r the rail type used in the tunnel
+ */
+-static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailType r)
++static inline void MakeRailTunnel(TileIndex t, Owner o, TunnelID id, DiagDirection d, RailType r)
+ {
+ SetTileType(t, MP_TUNNELBRIDGE);
+ SetTileOwner(t, o);
+- _m[t].m2 = 0;
++ _m[t].m2 = id;
+ _m[t].m3 = 0;
+ _m[t].m4 = 0;
+ _m[t].m5 = TRANSPORT_RAIL << 2 | d;
+diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
+index 0423cde1f9..159f06f3fd 100644
+--- a/src/tunnelbridge_cmd.cpp
++++ b/src/tunnelbridge_cmd.cpp
+@@ -40,12 +40,15 @@
+ #include "water.h"
+ #include "company_gui.h"
+ #include "station_func.h"
++#include "station_map.h"
++#include "industry_map.h"
+
+ #include "table/strings.h"
+ #include "table/bridge_land.h"
+
+ #include "safeguards.h"
+
++
+ BridgeSpec _bridge[MAX_BRIDGES]; ///< The specification of all bridges.
+ TileIndex _build_tunnel_endtile; ///< The end of a tunnel; as hidden return from the tunnel build command for GUI purposes.
+
+@@ -649,12 +652,6 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
+ * position, because of increased-cost-by-length: 'cost += cost >> 3' */
+
+ TileIndexDiff delta = TileOffsByDiagDir(direction);
+- DiagDirection tunnel_in_way_dir;
+- if (DiagDirToAxis(direction) == AXIS_Y) {
+- tunnel_in_way_dir = (TileX(start_tile) < (MapMaxX() / 2)) ? DIAGDIR_SW : DIAGDIR_NE;
+- } else {
+- tunnel_in_way_dir = (TileY(start_tile) < (MapMaxX() / 2)) ? DIAGDIR_SE : DIAGDIR_NW;
+- }
+
+ TileIndex end_tile = start_tile;
+
+@@ -662,9 +659,15 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
+ int tiles_coef = 3;
+ /* Number of tiles from start of tunnel */
+ int tiles = 0;
++ /* flag for chunnels. */
++ bool is_chunnel = false;
++ /* Number of chunnel head tiles. */
++ int head_tiles = 0;
+ /* Number of tiles at which the cost increase coefficient per tile is halved */
+ int tiles_bump = 25;
+
++ TileIndex found_tunnel_tile = INVALID_TILE;
++
+ CommandCost cost(EXPENSES_CONSTRUCTION);
+ Slope end_tileh;
+ for (;;) {
+@@ -672,13 +675,69 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
+ if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER);
+ end_tileh = GetTileSlope(end_tile, &end_z);
+
+- if (start_z == end_z) break;
++ if (start_z == end_z) {
++ _build_tunnel_endtile = found_tunnel_tile != INVALID_TILE ? found_tunnel_tile : end_tile;
++
++ /* Test if we are on a shore. */
++ if (end_z == 0 &&
++ (IsCoastTile(end_tile) ||
++ (IsValidTile(end_tile + delta) && HasTileWaterGround(end_tile + delta)) ||
++ (IsValidTile(end_tile + delta * 2) && HasTileWaterGround(end_tile + delta * 2)))) {
++ if (!is_chunnel) {
++ /*We are about to pass water for the first time so check if not to close to other tunnel */
++ if (tiles + 1 < head_tiles + 4 && found_tunnel_tile != INVALID_TILE) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY_FOR_CHUNNEL);
++ if (tiles + 1 < 4) return_cmd_error(STR_ERROR_TUNNEL_RAMP_TOO_SHORT);
++ }
++ } else {/* We are leaving.*/
++ if (is_chunnel) {
++ /* Check if there is enough ramp space to come up. */
++ if (head_tiles < 4 && found_tunnel_tile != INVALID_TILE) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY_FOR_CHUNNEL);
++ if (head_tiles < 4) return_cmd_error(STR_ERROR_TUNNEL_RAMP_TOO_SHORT);
++ } else {
++ if (found_tunnel_tile != INVALID_TILE) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY);
++ }
++ break;
++ }
+
+- if (!_cheats.crossing_tunnels.value && IsTunnelInWayDir(end_tile, start_z, tunnel_in_way_dir)) {
+- return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY);
+- }
++ /* A shore was found so pass the water and find a proper shore tile that potentially
++ * could have a tunnel portal behind. */
++ for (;;) {
++ if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER);
++
++ end_tileh = GetTileSlope(end_tile);
++ if(direction == DIAGDIR_NE && (end_tileh & SLOPE_NE) == SLOPE_NE) break;
++ if(direction == DIAGDIR_SE && (end_tileh & SLOPE_SE) == SLOPE_SE) break;
++ if(direction == DIAGDIR_SW && (end_tileh & SLOPE_SW) == SLOPE_SW) break;
++ if(direction == DIAGDIR_NW && (end_tileh & SLOPE_NW) == SLOPE_NW) break;
++
++ /* No drilling under oil rigs.*/
++ if ((IsTileType(end_tile, MP_STATION) && IsOilRig(end_tile)) ||
++ (IsTileType(end_tile, MP_INDUSTRY) &&
++ GetIndustryGfx(end_tile) >= GFX_OILRIG_1 &&
++ GetIndustryGfx(end_tile) <= GFX_OILRIG_5)) {
++ _build_tunnel_endtile = end_tile;
++ return_cmd_error(STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL);
++ }
+
++ end_tile += delta;
++ tiles++;
++ }
++ /* The water was passed */
++ is_chunnel = true;
++ head_tiles = 0;
++ found_tunnel_tile = INVALID_TILE;
++ }
++ if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z, (tiles + 1 < 4 || (is_chunnel && head_tiles < 4) ? false : true))) {
++ if (found_tunnel_tile == INVALID_TILE || is_chunnel) { // Remember the first or the last when we pass a tunnel.
++ found_tunnel_tile = end_tile;
++ head_tiles = 0;
++ }
++ }
++ head_tiles++;
+ tiles++;
++ }
++ /* The cost of the digging. */
++ for (int i = tiles; i > 0; i--) {
+ if (tiles == tiles_bump) {
+ tiles_coef++;
+ tiles_bump *= 2;
+@@ -746,21 +805,32 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
+ default: NOT_REACHED();
+ }
+
++ if (is_chunnel) cost.MultiplyCost(2);
++
+ if (flags & DC_EXEC) {
+ Company *c = Company::GetIfValid(company);
+ uint num_pieces = (tiles + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
++
++ /* The most northern tile first. */
++ TileIndex tn = start_tile;
++ TileIndex ts = end_tile;
++ if(start_tile > end_tile) Swap(tn, ts);
++
++ if (!Tunnel::CanAllocateItem()) return_cmd_error(STR_ERROR_TUNNEL_TOO_MANY);
++ const Tunnel *t = new Tunnel(tn, ts, is_chunnel);
++
+ if (transport_type == TRANSPORT_RAIL) {
+ if (c != nullptr) c->infrastructure.rail[railtype] += num_pieces;
+- MakeRailTunnel(start_tile, company, direction, railtype);
+- MakeRailTunnel(end_tile, company, ReverseDiagDir(direction), railtype);
++ MakeRailTunnel(start_tile, company, t->index, direction, railtype);
++ MakeRailTunnel(end_tile, company, t->index, ReverseDiagDir(direction), railtype);
+ AddSideToSignalBuffer(start_tile, INVALID_DIAGDIR, company);
+ YapfNotifyTrackLayoutChange(start_tile, DiagDirToDiagTrack(direction));
+ } else {
+ if (c != nullptr) c->infrastructure.road[roadtype] += num_pieces * 2; // A full diagonal road has two road bits.
+ RoadType road_rt = RoadTypeIsRoad(roadtype) ? roadtype : INVALID_ROADTYPE;
+ RoadType tram_rt = RoadTypeIsTram(roadtype) ? roadtype : INVALID_ROADTYPE;
+- MakeRoadTunnel(start_tile, company, direction, road_rt, tram_rt);
+- MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), road_rt, tram_rt);
++ MakeRoadTunnel(start_tile, company, t->index, direction, road_rt, tram_rt);
++ MakeRoadTunnel(end_tile, company, t->index, ReverseDiagDir(direction), road_rt, tram_rt);
+ }
+ DirtyCompanyInfrastructureWindows(company);
+ }
+@@ -850,6 +920,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
+ ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM, flags);
+ }
+
++ const bool is_chunnel = Tunnel::GetByTile(tile)->is_chunnel;
++
+ uint len = GetTunnelBridgeLength(tile, endtile) + 2; // Don't forget the end tiles.
+
+ if (flags & DC_EXEC) {
+@@ -870,6 +942,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
+ DirtyCompanyInfrastructureWindows(owner);
+ }
+
++ delete Tunnel::GetByTile(tile);
++
+ DoClearSquare(tile);
+ DoClearSquare(endtile);
+
+@@ -886,11 +960,13 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
+ UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
+ UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
+
++ delete Tunnel::GetByTile(tile);
++
+ DoClearSquare(tile);
+ DoClearSquare(endtile);
+ }
+ }
+- return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len);
++ return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len * (is_chunnel ? 2 : 1));
+ }
+
+
+@@ -1866,6 +1942,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
+ if (frame == _tunnel_visibility_frame[dir]) {
+ t->tile = tile;
+ t->track = TRACK_BIT_WORMHOLE;
++ if (Tunnel::GetByTile(tile)->is_chunnel) SetBit(t->gv_flags, GVF_CHUNNEL_BIT);
+ t->vehstatus |= VS_HIDDEN;
+ return VETSB_ENTERED_WORMHOLE;
+ }
+@@ -1873,6 +1950,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
+
+ if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
+ /* We're at the tunnel exit ?? */
++ if (t->tile != tile && GetOtherTunnelEnd(t->tile) != tile) return VETSB_CONTINUE; // In chunnel
+ t->tile = tile;
+ t->track = DiagDirToDiagTrackBits(vdir);
+ assert(t->track);
+@@ -1889,6 +1967,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
+ assert(frame == rv->frame + 1);
+ rv->tile = tile;
+ rv->state = RVSB_WORMHOLE;
++ if (Tunnel::GetByTile(tile)->is_chunnel) SetBit(rv->gv_flags, GVF_CHUNNEL_BIT);
+ rv->vehstatus |= VS_HIDDEN;
+ return VETSB_ENTERED_WORMHOLE;
+ } else {
+@@ -1898,6 +1977,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
+
+ /* We're at the tunnel exit ?? */
+ if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
++ if (rv->tile != tile && GetOtherTunnelEnd(rv->tile) != tile) return VETSB_CONTINUE; // In chunnel
+ rv->tile = tile;
+ rv->state = DiagDirToDiagTrackdir(vdir);
+ rv->frame = frame;
+@@ -1906,6 +1986,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
+ }
+ }
+ } else { // IsBridge(tile)
++ if (v->vehstatus & VS_HIDDEN) return VETSB_CONTINUE; // Building bridges between chunnel portals allowed.
+ if (v->type != VEH_SHIP) {
+ /* modify speed of vehicle */
+ uint16 spd = GetBridgeSpec(GetBridgeType(tile))->speed;
+diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h
+index 62d3c14b2d..37e00457e4 100644
+--- a/src/tunnelbridge_map.h
++++ b/src/tunnelbridge_map.h
+@@ -11,7 +11,7 @@
+ #define TUNNELBRIDGE_MAP_H
+
+ #include "bridge_map.h"
+-#include "tunnel_map.h"
++#include "tunnel_base.h"
+
+
+ /**
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
-index 956b449602..abce02af64 100644
+index e014944a9f..cb6239f597 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
-@@ -286,6 +286,14 @@ uint Vehicle::Crash(bool flooded)
+@@ -285,6 +285,14 @@ uint Vehicle::Crash(bool flooded)
return RandomRange(pass + 1); // Randomise deceased passengers.
}
@@ -115,7 +1005,7 @@ index 956b449602..abce02af64 100644
/**
* Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
-@@ -1083,7 +1091,7 @@ static void DoDrawVehicle(const Vehicle *v)
+@@ -1082,7 +1090,7 @@ static void DoDrawVehicle(const Vehicle *v)
if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
/* Check whether the vehicle shall be transparent due to the game state */
@@ -124,16 +1014,16 @@ index 956b449602..abce02af64 100644
if (v->type == VEH_EFFECT) {
/* Check whether the vehicle shall be transparent/invisible due to GUI settings.
-@@ -1141,7 +1149,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi)
+@@ -1139,7 +1147,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi)
+ const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
while (v != nullptr) {
- if (LayerIndex(v->tile) == dpi->layer)
- if (!(v->vehstatus & VS_HIDDEN) &&
+ if (v->IsDrawn() &&
l <= v->coord.right &&
t <= v->coord.bottom &&
r >= v->coord.left &&
-@@ -1176,7 +1184,7 @@ Vehicle *CheckClickOnVehicle(const Viewport *vp, int x, int y)
+@@ -1174,7 +1182,7 @@ Vehicle *CheckClickOnVehicle(const Viewport *vp, int x, int y)
y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
for (Vehicle *v : Vehicle::Iterate()) {