summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/ground_vehicle.cpp65
-rw-r--r--src/ground_vehicle.hpp14
-rw-r--r--src/industry_cmd.cpp6
-rw-r--r--src/lang/english.txt7
-rw-r--r--src/roadveh_cmd.cpp3
-rw-r--r--src/saveload/CMakeLists.txt1
-rw-r--r--src/saveload/afterload.cpp48
-rw-r--r--src/saveload/saveload.cpp2
-rw-r--r--src/saveload/tunnel_sl.cpp48
-rw-r--r--src/table/settings/misc_settings.ini4
-rw-r--r--src/terraform_cmd.cpp2
-rw-r--r--src/train_cmd.cpp14
-rw-r--r--src/transparency.h1
-rw-r--r--src/transparency_gui.cpp7
-rw-r--r--src/tunnel_base.h39
-rw-r--r--src/tunnel_map.cpp75
-rw-r--r--src/tunnel_map.h26
-rw-r--r--src/tunnelbridge_cmd.cpp111
-rw-r--r--src/tunnelbridge_map.h2
-rw-r--r--src/vehicle.cpp14
-rw-r--r--src/vehicle_base.h6
-rw-r--r--src/widgets/transparency_widget.h1
23 files changed, 423 insertions, 74 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 631eaf6de..908e04128 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -446,6 +446,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 f6e44c2e2..08879b084 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 15aac1f36..b08a23d0e 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 8be44488d..69727bfd7 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"
@@ -1523,6 +1524,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 44d226cbf..09f6c1f67 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -2402,6 +2402,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
+STR_TRANSPARENT_TUNNELS_TOOLTIP :{BLACK}Toggle transparency for vehicles in tunnels. Ctrl+Click to lock.
STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Set objects invisible instead of transparent
# Linkgraph legend window
@@ -2811,7 +2812,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
@@ -4652,6 +4655,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 86c2de78e..67259c783 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -1178,6 +1178,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;
}
@@ -1554,7 +1555,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 32bcc57ac..f1fce31c2 100644
--- a/src/saveload/CMakeLists.txt
+++ b/src/saveload/CMakeLists.txt
@@ -41,6 +41,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 df1b4e44f..9696e390d 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"
@@ -537,6 +538,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.
@@ -1942,6 +1962,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. */
@@ -2545,7 +2591,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 c8a1a0d29..6ce5493b7 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -252,6 +252,7 @@ static const std::vector<ChunkHandlerRef> &ChunkHandlers()
extern const ChunkHandlerTable _airport_chunk_handlers;
extern const ChunkHandlerTable _object_chunk_handlers;
extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
+ extern const ChunkHandlerTable _tunnel_chunk_handlers[];
/** List of all chunks in a savegame. */
static const ChunkHandlerTable _chunk_handler_tables[] = {
@@ -288,6 +289,7 @@ static const std::vector<ChunkHandlerRef> &ChunkHandlers()
_airport_chunk_handlers,
_object_chunk_handlers,
_persistent_storage_chunk_handlers,
+ _tunnel_chunk_handlers,
};
static std::vector<ChunkHandlerRef> _chunk_handlers;
diff --git a/src/saveload/tunnel_sl.cpp b/src/saveload/tunnel_sl.cpp
new file mode 100644
index 000000000..7ad37b12b
--- /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/settings/misc_settings.ini b/src/table/settings/misc_settings.ini
index daca4a817..ae630d2fd 100644
--- a/src/table/settings/misc_settings.ini
+++ b/src/table/settings/misc_settings.ini
@@ -289,7 +289,7 @@ type = SLE_UINT
var = _transparency_opt
def = 0
min = 0
-max = 0x1FF
+max = 0x3FF
cat = SC_BASIC
[SDTG_VAR]
@@ -298,7 +298,7 @@ type = SLE_UINT
var = _transparency_lock
def = 0
min = 0
-max = 0x1FF
+max = 0x3FF
cat = SC_BASIC
[SDTG_VAR]
diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp
index eaed9e71c..ddb34c065 100644
--- a/src/terraform_cmd.cpp
+++ b/src/terraform_cmd.cpp
@@ -269,7 +269,7 @@ CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
}
}
/* Check if tunnel would take damage */
- if (direction == -1 && IsTunnelInWay(t, z_min)) {
+ if (direction == -1 && IsTunnelInWay(t, z_min, true)) {
_terraform_err_tile = t; // 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 17e298c33..1112e9237 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -1577,6 +1577,7 @@ static void UpdateStatusAfterSwap(Train *v)
}
v->UpdatePosition();
+ if (v->track == TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true);
v->UpdateViewport(true, true);
}
@@ -3340,6 +3341,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;
}
@@ -3353,7 +3363,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 */
@@ -3866,7 +3876,7 @@ static bool TrainLocoHandler(Train *v, bool mode)
}
for (Train *u = v; u != nullptr; u = u->Next()) {
- if ((u->vehstatus & VS_HIDDEN) != 0) continue;
+ if (!u->IsDrawn()) continue;
u->UpdateViewport(false, false);
}
diff --git a/src/transparency.h b/src/transparency.h
index 54ba24e93..08b13ca9b 100644
--- a/src/transparency.h
+++ b/src/transparency.h
@@ -29,6 +29,7 @@ enum TransparencyOption {
TO_STRUCTURES, ///< other objects such as transmitters and lighthouses
TO_CATENARY, ///< catenary
TO_LOADING, ///< loading indicators
+ TO_TUNNELS, ///< vehicles in tunnels
TO_END,
TO_INVALID, ///< Invalid transparency option
};
diff --git a/src/transparency_gui.cpp b/src/transparency_gui.cpp
index 153dcb5d0..611094cde 100644
--- a/src/transparency_gui.cpp
+++ b/src/transparency_gui.cpp
@@ -50,7 +50,8 @@ public:
case WID_TT_BRIDGES:
case WID_TT_STRUCTURES:
case WID_TT_CATENARY:
- 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);
break;
@@ -58,6 +59,7 @@ public:
case WID_TT_BUTTONS:
for (uint i = WID_TT_BEGIN; i < WID_TT_END; i++) {
if (i == WID_TT_LOADING) continue; // Do not draw button for invisible loading indicators.
+ if (i == WID_TT_TUNNELS) continue; // Do not draw button for invisible vehicles in tunnels.
const NWidgetBase *wi = this->GetWidget<NWidgetBase>(i);
DrawFrameRect(wi->pos_x + 1, r.top + 2, wi->pos_x + wi->current_x - 2, r.bottom - 2, COLOUR_PALE_GREEN,
@@ -69,7 +71,7 @@ public:
void OnClick(Point pt, int widget, int click_count) override
{
- if (widget >= WID_TT_BEGIN && widget < WID_TT_END) {
+ if (IsInsideMM(widget, WID_TT_BEGIN, WID_TT_END)) {
if (_ctrl_pressed) {
/* toggle the bit of the transparencies lock variable */
ToggleTransparencyLock((TransparencyOption)(widget - WID_TT_BEGIN));
@@ -139,6 +141,7 @@ static const NWidgetPart _nested_transparency_widgets[] = {
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_STRUCTURES), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRANSMITTER, STR_TRANSPARENT_STRUCTURES_TOOLTIP),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_CATENARY), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_BUILD_X_ELRAIL, STR_TRANSPARENT_CATENARY_TOOLTIP),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_LOADING), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRAINLIST, STR_TRANSPARENT_LOADING_TOOLTIP),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_TUNNELS), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_TRANSPARENT_TUNNELS_TOOLTIP),
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 000000000..4a2493fa7
--- /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 9de523580..6234193e1 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 c347626d5..45589dacb 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 5181dfdca..a0e33641d 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.
@@ -681,12 +684,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;
@@ -694,9 +691,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 (;;) {
@@ -704,13 +707,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;
@@ -778,21 +837,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);
}
@@ -883,6 +953,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
}
Money base_cost = TunnelBridgeClearCost(tile, PR_CLEAR_TUNNEL);
+ 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) {
@@ -903,6 +975,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
DirtyCompanyInfrastructureWindows(owner);
}
+ delete Tunnel::GetByTile(tile);
+
DoClearSquare(tile);
DoClearSquare(endtile);
@@ -919,12 +993,14 @@ 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, len * base_cost);
+ return CommandCost(EXPENSES_CONSTRUCTION, len * base_cost * (is_chunnel ? 2 : 1));
}
@@ -1900,6 +1976,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;
}
@@ -1907,6 +1984,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);
@@ -1923,6 +2001,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 {
@@ -1932,6 +2011,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;
@@ -1940,6 +2020,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 62d3c14b2..37e00457e 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 955f9bde0..8f6a28bd1 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -285,6 +285,14 @@ uint Vehicle::Crash(bool flooded)
return RandomRange(pass + 1); // Randomise deceased passengers.
}
+bool Vehicle::IsDrawn() const
+{
+ return !(this->vehstatus & VS_HIDDEN) ||
+ (!IsTransparencySet(TO_TUNNELS) &&
+ ((this->type == VEH_TRAIN && Train::From(this)->track == TRACK_BIT_WORMHOLE) ||
+ (this->type == VEH_ROAD && RoadVehicle::From(this)->state == RVSB_WORMHOLE)));
+}
+
/**
* Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
@@ -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 */
- bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
+ bool shadowed = (v->vehstatus & (VS_SHADOW | VS_HIDDEN)) != 0;
if (v->type == VEH_EFFECT) {
/* Check whether the vehicle shall be transparent/invisible due to GUI settings.
@@ -1144,7 +1152,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi)
while (v != nullptr) {
- if (!(v->vehstatus & VS_HIDDEN) &&
+ if (v->IsDrawn() &&
l <= v->coord.right + xb &&
t <= v->coord.bottom + yb &&
r >= v->coord.left - xb &&
@@ -1210,7 +1218,7 @@ Vehicle *CheckClickOnVehicle(const Viewport *vp, int x, int y)
y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
for (Vehicle *v : Vehicle::Iterate()) {
- if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
+ if (v->IsDrawn() && !(v->vehstatus & VS_UNCLICKABLE) &&
x >= v->coord.left && x <= v->coord.right &&
y >= v->coord.top && y <= v->coord.bottom) {
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index a5549b444..91d4ac0f9 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -367,6 +367,12 @@ public:
uint GetConsistTotalCapacity() const;
/**
+ * Is this vehicle drawn?
+ * @return true if it is drawn
+ */
+ bool IsDrawn() const;
+
+ /**
* Marks the vehicles to be redrawn and updates cached variables
*
* This method marks the area of the vehicle on the screen as dirty.
diff --git a/src/widgets/transparency_widget.h b/src/widgets/transparency_widget.h
index 2b096e733..099d48a33 100644
--- a/src/widgets/transparency_widget.h
+++ b/src/widgets/transparency_widget.h
@@ -23,6 +23,7 @@ enum TransparencyToolbarWidgets {
WID_TT_STRUCTURES, ///< Object structure transparency toggle button.
WID_TT_CATENARY, ///< Catenary transparency toggle button.
WID_TT_LOADING, ///< Loading indicators transparency toggle button.
+ WID_TT_TUNNELS, ///< Vehicles in tunnels toggle button.
WID_TT_END, ///< End of toggle buttons.
/* Panel with buttons for invisibility */