Merge branch 'chunnel' into jgrpp

# Conflicts:
#	src/ground_vehicle.hpp
pull/16/head
Jonathan G Rennison 7 years ago
commit c1a26459d8

@ -404,6 +404,7 @@ transparency.h
transparency_gui.h
transport_type.h
tunnelbridge.h
tunnel_base.h
vehicle_base.h
vehicle_func.h
vehicle_gui.h
@ -670,6 +671,7 @@ saveload/strings_sl.cpp
saveload/story_sl.cpp
saveload/subsidy_sl.cpp
saveload/town_sl.cpp
saveload/tunnel_sl.cpp
saveload/vehicle_sl.cpp
saveload/waypoint_sl.cpp
saveload/signal_sl.cpp

@ -196,6 +196,7 @@ DEFINE_POOL_METHOD(void)::FreeItem(size_t index)
DEFINE_POOL_METHOD(void)::CleanPool()
{
this->cleaning = true;
Titem::PreCleanPool();
for (size_t i = 0; i < this->first_unused; i++) {
delete this->Get(i); // 'delete NULL;' is very valid
}

@ -286,6 +286,13 @@ struct Pool : PoolBase {
* @note it's called only when !CleaningPool()
*/
static inline void PostDestructor(size_t index) { }
/**
* Dummy function called before a pool is about to be cleaned.
* If you want to use it, override it in PoolItem's subclass.
* @note it's called only when CleaningPool()
*/
static inline void PreCleanPool() { }
};
private:

@ -13,6 +13,8 @@
#include "train.h"
#include "roadveh.h"
#include "depot_map.h"
#include "tunnel_base.h"
#include "slope_type.h"
#include "safeguards.h"
@ -280,6 +282,66 @@ 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;
bool going_north;
Slope slope_north;
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);
going_north = (this->direction == DIR_NW);
slope_north = SLOPE_NW;
} 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);
going_north = (this->direction == DIR_NE);
slope_north = SLOPE_NE;
}
Slope slope = SLOPE_FLAT;
int delta;
if ((delta = pos_coord - north_coord) <= 3) {
this->z_pos = TILE_HEIGHT * (delta == 3 ? -2 : -1);
if (delta != 2) {
slope = slope_north;
SetBit(this->gv_flags, going_north ? 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_north;
SetBit(this->gv_flags, going_north ? 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 */

@ -16,6 +16,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? */
@ -54,6 +55,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)
};
/**
@ -228,19 +230,27 @@ struct GroundVehicle : public SpecializedVehicle<T, Type> {
#ifdef _DEBUG
assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos));
#endif
if (HasBit(this->gv_flags, GVF_CHUNNEL_BIT) && !IsTunnelTile(this->tile)) {
ClrBit(this->gv_flags, GVF_CHUNNEL_BIT);
}
}
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();

@ -21,6 +21,7 @@
#include "cheat_type.h"
#include "genworld.h"
#include "tree_map.h"
#include "tunnel_map.h"
#include "newgrf_cargo.h"
#include "newgrf_debug.h"
#include "newgrf_industrytiles.h"
@ -1457,6 +1458,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();
}

@ -1267,6 +1267,9 @@ STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :When enabled, i
STR_CONFIG_SETTING_SHIP_COLLISION_AVOIDANCE :Ships avoid collisions: {STRING2}
STR_CONFIG_SETTING_SHIP_COLLISION_AVOIDANCE_HELPTEXT :When enabled, ships try to avoid passing through each other. Requires 90° turns to be forbidden.
STR_CONFIG_SETTING_CHUNNEL :Allow construction of tunnels under water: {STRING2}
STR_CONFIG_SETTING_CHUNNEL_HELPTEXT :When enabled, tunnels can be constructed under bodies of water at sea level. This requires the tunnel ends to be least 3 tiles away from the shore.
STR_CONFIG_SETTING_NO_TRAIN_CRASH_OTHER_COMPANY :Trains from different companies may not crash into each other: {STRING2}
STR_CONFIG_SETTING_NO_TRAIN_CRASH_OTHER_COMPANY_HELPTEXT :This setting is primarily to prevent untrusted players deliberately causing crashes involving other companies' trains in multi-player rail infrastructure sharing games.
@ -3042,8 +3045,11 @@ 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_RAILROAD_SIGNAL :Railway tunnel with signal simulation
STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL_CHUNNEL :Railway tunnel with signal simulation (underwater)
STR_LAI_TUNNEL_DESCRIPTION_ROAD :Road tunnel
STR_LAI_TUNNEL_DESCRIPTION_ROAD_CHUNNEL :Road tunnel (underwater)
STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL :Railway bridge with signal simulation
STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL :Steel suspension rail bridge
@ -5002,6 +5008,11 @@ 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.
STR_ERROR_CHUNNEL_ONLY_OVER_SEA :{WHITE}Underwater tunnels are only allowed to cross sea...
# Object related errors
STR_ERROR_TOO_MANY_OBJECTS :{WHITE}... too many objects

@ -26,6 +26,7 @@
#include "core/geometry_func.hpp"
#include "newgrf_debug.h"
#include "zoom_func.h"
#include "tunnelbridge_map.h"
#include "widgets/misc_widget.h"
@ -123,6 +124,13 @@ public:
# define LANDINFOD_LEVEL 1
#endif
DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
if(IsTunnelTile(tile)) {
DEBUG(misc, LANDINFOD_LEVEL, "tunnel pool size: %u", (uint)Tunnel::GetPoolSize());
DEBUG(misc, LANDINFOD_LEVEL, "index: %#x" , Tunnel::GetByTile(tile)->index);
DEBUG(misc, LANDINFOD_LEVEL, "north tile: %#x" , Tunnel::GetByTile(tile)->tile_n);
DEBUG(misc, LANDINFOD_LEVEL, "south tile: %#x" , Tunnel::GetByTile(tile)->tile_s);
DEBUG(misc, LANDINFOD_LEVEL, "is chunnel: %u" , Tunnel::GetByTile(tile)->is_chunnel);
}
DEBUG(misc, LANDINFOD_LEVEL, "type = %#x", _m[tile].type);
DEBUG(misc, LANDINFOD_LEVEL, "height = %#x", _m[tile].height);
DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1);

@ -1184,6 +1184,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->IsDrawn()) v->Vehicle::UpdateViewport(true);
return true;
}
@ -1555,7 +1556,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;
}

@ -13,6 +13,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"
@ -57,6 +58,7 @@
#include "../error.h"
#include "../disaster_vehicle.h"
#include "../tracerestrict.h"
#include "../tunnel_map.h"
#include "saveload_internal.h"
@ -563,6 +565,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.
@ -2014,6 +2035,31 @@ bool AfterLoadGame()
}
}
/* Tunnel pool has to be initiated before reservations. */
if (SlXvIsFeatureMissing(XSLFI_CHUNNEL)) {
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, TileHeight(start_tile), false);
SetTunnelIndex(start_tile, t->index);
SetTunnelIndex(end_tile, 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. */
@ -2648,7 +2694,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
@ -3335,6 +3381,11 @@ bool AfterLoadGame()
_settings_game.economy.town_cargo_scale_factor = _settings_game.economy.old_town_cargo_factor * 10;
}
/* Set day length factor to 1 if loading a pre day length savegame */
if (SlXvIsFeatureMissing(XSLFI_VARIABLE_DAY_LENGTH) && SlXvIsFeatureMissing(XSLFI_SPRINGPP)) {
_settings_game.economy.day_length_factor = 1;
}
/* Road stops is 'only' updating some caches */
AfterLoadRoadStops();
AfterLoadLabelMaps();

@ -74,6 +74,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_EXTENDED_GAMELOG, XSCF_NULL, 1, 1, "extended_gamelog", NULL, NULL, NULL },
{ XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", NULL, NULL, NULL },
{ XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 1, 1, "custom_bridge_heads", NULL, NULL, NULL },
{ XSLFI_CHUNNEL, XSCF_NULL, 1, 1, "chunnel", NULL, NULL, "TUNN" },
{ XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker
};

@ -48,6 +48,7 @@ enum SlXvFeatureIndex {
XSLFI_EXTENDED_GAMELOG, ///< Extended gamelog
XSLFI_STATION_CATCHMENT_INC, ///< Station catchment radius increase
XSLFI_CUSTOM_BRIDGE_HEADS, ///< Custom bridge heads
XSLFI_CHUNNEL, ///< Tunnels under water (channel tunnel)
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

@ -465,6 +465,7 @@ extern const ChunkHandler _plan_chunk_handlers[];
extern const ChunkHandler _template_replacement_chunk_handlers[];
extern const ChunkHandler _template_vehicle_chunk_handlers[];
extern const ChunkHandler _bridge_signal_chunk_handlers[];
extern const ChunkHandler _tunnel_chunk_handlers[];
/** Array of all chunks in a savegame, \c NULL terminated. */
static const ChunkHandler * const _chunk_handlers[] = {
@ -508,6 +509,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
_template_replacement_chunk_handlers,
_template_vehicle_chunk_handlers,
_bridge_signal_chunk_handlers,
_tunnel_chunk_handlers,
NULL,
};

@ -0,0 +1,52 @@
/* $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, 0, SL_MAX_VERSION),
SLE_CONDVAR(Tunnel, tile_s, SLE_UINT32, 0, SL_MAX_VERSION),
SLE_CONDVAR(Tunnel, height, SLE_UINT8, 0, SL_MAX_VERSION),
SLE_CONDVAR(Tunnel, is_chunnel, SLE_BOOL, 0, SL_MAX_VERSION),
SLE_END()
};
static void Save_TUNN()
{
Tunnel *tunnel;
FOR_ALL_TUNNELS(tunnel) {
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);
tunnel->UpdateIndexes();
}
}
extern const ChunkHandler _tunnel_chunk_handlers[] = {
{ 'TUNN', Save_TUNN, Load_TUNN, NULL, NULL, CH_ARRAY | CH_LAST},
};

@ -1701,6 +1701,7 @@ static SettingsContainer &GetSettingsTree()
limitations->Add(new SettingEntry("construction.max_bridge_length"));
limitations->Add(new SettingEntry("construction.max_bridge_height"));
limitations->Add(new SettingEntry("construction.max_tunnel_length"));
limitations->Add(new SettingEntry("construction.chunnel"));
limitations->Add(new SettingEntry("station.never_expire_airports"));
limitations->Add(new SettingEntry("vehicle.never_expire_vehicles"));
limitations->Add(new SettingEntry("vehicle.max_trains"));

@ -355,6 +355,7 @@ struct ConstructionSettings {
byte simulated_wormhole_signals; ///< simulate signals in tunnel
bool enable_build_river; ///< enable building rivers in-game
uint8 road_custom_bridge_heads; ///< allow construction of road custom bridge heads
bool chunnel; ///< allow construction of tunnels under water
uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames?
uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed?

@ -597,6 +597,16 @@ str = STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH
strhelp = STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH_HELPTEXT
strval = STR_CONFIG_SETTING_TILE_LENGTH
[SDT_BOOL]
base = GameSettings
var = construction.chunnel
def = false
str = STR_CONFIG_SETTING_CHUNNEL
strhelp = STR_CONFIG_SETTING_CHUNNEL_HELPTEXT
from = 0
cat = SC_BASIC
patxname = ""chunnel.construction.chunnel""
[SDT_VAR]
base = GameSettings
var = construction.simulated_wormhole_signals

@ -270,7 +270,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, ITIWF_IGNORE_CHUNNEL)) {
_terraform_err_tile = tile; // highlight the tile above the tunnel
return_cmd_error(STR_ERROR_EXCAVATION_WOULD_DAMAGE);
}

@ -1685,6 +1685,7 @@ static void UpdateStatusAfterSwap(Train *v)
}
v->UpdatePosition();
if (v->track == TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true);
v->UpdateViewport(true, true);
}
@ -3903,6 +3904,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 == NULL) {
/* This is the first vehicle in the train */
AffectSpeedByZChange(v, old_z);
}
}
if (v->IsDrawn()) v->Vehicle::UpdateViewport(true);
continue;
}
@ -3919,7 +3929,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
/* 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 == NULL) {
/* This is the first vehicle in the train */

@ -0,0 +1,51 @@
/* $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, 1048576> 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.
uint8 height; // Tunnel height
bool is_chunnel; // Whether this tunnel is a chunnel
Tunnel() {}
~Tunnel();
Tunnel(TileIndex tile_n, TileIndex tile_s, uint8 height, bool is_chunnel) : tile_n(tile_n), tile_s(tile_s), height(height), is_chunnel(is_chunnel)
{
this->UpdateIndexes();
}
void UpdateIndexes();
static inline Tunnel *GetByTile(TileIndex tile)
{
return Tunnel::Get(GetTunnelIndex(tile));
}
static void PreCleanPool();
};
#define FOR_ALL_TUNNELS_FROM(var, start) FOR_ALL_ITEMS_FROM(Tunnel, tunnel_index, var, start)
#define FOR_ALL_TUNNELS(var) FOR_ALL_TUNNELS_FROM(var, 0)
#endif /* TUNNEL_BASE_H */

@ -12,63 +12,126 @@
#include "stdafx.h"
#include "tunnelbridge_map.h"
#include "core/pool_func.hpp"
#include <unordered_map>
#include "safeguards.h"
/** All tunnel portals tucked away in a pool. */
TunnelPool _tunnel_pool("Tunnel");
INSTANTIATE_POOL_METHODS(Tunnel)
static std::unordered_map<TileIndex, TunnelID> tunnel_tile_index_map;
static std::unordered_multimap<uint64, Tunnel*> tunnel_axis_height_index;
static uint64 GetTunnelAxisHeightCacheKey(TileIndex tile, uint8 height, bool y_axis) {
if (y_axis) {
// tunnel extends along Y axis (DIAGDIR_SE from north end), has same X values
return TileX(tile) | (((uint64) height) << 24) | (((uint64) 1) << 32);
} else {
// tunnel extends along X axis (DIAGDIR_SW from north end), has same Y values
return TileY(tile) | (((uint64) height) << 24);
}
}
static inline uint64 GetTunnelAxisHeightCacheKey(const Tunnel* t) {
return GetTunnelAxisHeightCacheKey(t->tile_n, t->height, t->tile_s - t->tile_n > MapMaxX());
}
/**
* 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;
if (this->index >= TUNNEL_ID_MAP_LOOKUP) {
tunnel_tile_index_map.erase(this->tile_n);
tunnel_tile_index_map.erase(this->tile_s);
}
auto range = tunnel_axis_height_index.equal_range(GetTunnelAxisHeightCacheKey(this));
bool have_erased = false;
for (auto it = range.first; it != range.second; ++it) {
if (it->second == this) {
tunnel_axis_height_index.erase(it);
have_erased = true;
break;
}
}
assert(have_erased);
}
/**
* Update tunnel indexes
*/
void Tunnel::UpdateIndexes()
{
if (this->index >= TUNNEL_ID_MAP_LOOKUP) {
tunnel_tile_index_map[this->tile_n] = this->index;
tunnel_tile_index_map[this->tile_s] = this->index;
}
tunnel_axis_height_index.emplace(GetTunnelAxisHeightCacheKey(this), this);
}
/**
* Tunnel pool is about to be cleaned
*/
void Tunnel::PreCleanPool()
{
tunnel_tile_index_map.clear();
tunnel_axis_height_index.clear();
}
TunnelID GetTunnelIndexByLookup(TileIndex t)
{
auto iter = tunnel_tile_index_map.find(t);
assert_msg(iter != tunnel_tile_index_map.end(), "tile: 0x%X", t);
return iter->second;
}
/**
* 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;
Tunnel *t = Tunnel::GetByTile(tile);
return t->tile_n == tile ? t->tile_s : t->tile_n;
}
do {
tile -= delta;
if (!IsValidTile(tile)) return false;
height = GetTileZ(tile);
} while (z < height);
static inline bool IsTunnelInWaySingleAxis(TileIndex tile, int z, IsTunnelInWayFlags flags, bool y_axis, TileIndexDiff tile_diff)
{
const auto tunnels = tunnel_axis_height_index.equal_range(GetTunnelAxisHeightCacheKey(tile, z, y_axis));
for (auto it = tunnels.first; it != tunnels.second; ++it) {
const Tunnel *t = it->second;
if (t->tile_n > tile || tile > t->tile_s) continue;
return z == height && IsTunnelTile(tile) && GetTunnelBridgeDirection(tile) == dir;
if (!t->is_chunnel && (flags & ITIWF_CHUNNEL_ONLY)) {
continue;
}
if (t->is_chunnel && (flags & ITIWF_IGNORE_CHUNNEL)) {
/* Only if tunnel was built over water is terraforming is allowed between portals. */
const TileIndexDiff delta = tile_diff * 4; // 4 tiles ramp.
if (tile < t->tile_n + delta || t->tile_s - delta < tile) return true;
continue;
}
return true;
}
return false;
}
/**
* 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, IsTunnelInWayFlags flags)
{
return IsTunnelInWayDir(tile, z, (TileX(tile) > (MapMaxX() / 2)) ? DIAGDIR_NE : DIAGDIR_SW) ||
IsTunnelInWayDir(tile, z, (TileY(tile) > (MapMaxY() / 2)) ? DIAGDIR_NW : DIAGDIR_SE);
return IsTunnelInWaySingleAxis(tile, z, flags, false, 1) || IsTunnelInWaySingleAxis(tile, z, flags, true, TileOffsByDiagDir(DIAGDIR_SE));
}

@ -14,6 +14,9 @@
#include "road_map.h"
typedef uint32 TunnelID; ///< Type for the unique identifier of tunnels.
static const TunnelID TUNNEL_ID_MAP_LOOKUP = 0xFFFF; ///< Sentinel ID value to store in m2 to indiciate that the ID should be looked up instead
/**
* Is this a tunnel (entrance)?
@ -37,27 +40,63 @@ 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)
{
extern TunnelID GetTunnelIndexByLookup(TileIndex t);
assert(IsTunnelTile(t));
TunnelID map_id = _m[t].m2;
return map_id == TUNNEL_ID_MAP_LOOKUP ? GetTunnelIndexByLookup(t) : map_id;
}
TileIndex GetOtherTunnelEnd(TileIndex);
bool IsTunnelInWay(TileIndex, int z);
bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir);
/** Flags for miscellaneous industry tile specialities */
enum IsTunnelInWayFlags {
ITIWF_NONE = 0,
ITIWF_IGNORE_CHUNNEL = 1 << 0, ///< Chunnel mid-parts are ignored, used when terraforming.
ITIWF_CHUNNEL_ONLY = 1 << 1, ///< Only check for chunnels
};
DECLARE_ENUM_AS_BIT_SET(IsTunnelInWayFlags)
bool IsTunnelInWay(TileIndex, int z, IsTunnelInWayFlags flags = ITIWF_NONE);
/**
* Set the index of tunnel tile.
* @param t the tile
* @param id the tunnel ID
* @pre IsTunnelTile(t)
*/
static inline void SetTunnelIndex(TileIndex t, TunnelID id)
{
assert(IsTunnelTile(t));
_m[t].m2 = (id >= TUNNEL_ID_MAP_LOOKUP) ? TUNNEL_ID_MAP_LOOKUP : id;
}
/**
* 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, RoadTypes r)
static inline void MakeRoadTunnel(TileIndex t, Owner o, TunnelID id, DiagDirection d, RoadTypes r)
{
SetTileType(t, MP_TUNNELBRIDGE);
SetTileOwner(t, o);
_m[t].m2 = 0;
_m[t].m3 = 0;
_m[t].m4 = 0;
_m[t].m5 = TRANSPORT_ROAD << 2 | d;
SB(_me[t].m6, 2, 4, 0);
_me[t].m7 = 0;
SetTunnelIndex(t, id);
SetRoadOwner(t, ROADTYPE_ROAD, o);
if (o != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, o);
SetRoadTypes(t, r);
@ -67,14 +106,14 @@ 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;
SB(_m[t].m1, 7, 1, GB(r, 4, 1));
SB(_m[t].m3, 0, 4, GB(r, 0, 4));
SB(_m[t].m3, 4, 4, 0);
@ -82,6 +121,7 @@ static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailTyp
_m[t].m5 = TRANSPORT_RAIL << 2 | d;
SB(_me[t].m6, 2, 4, 0);
_me[t].m7 = 0;
SetTunnelIndex(t, id);
}
#endif /* TUNNEL_MAP_H */

@ -27,6 +27,7 @@
#include "autoslope.h"
#include "tunnelbridge_map.h"
#include "bridge_signal_map.h"
#include "tunnel_base.h"
#include "strings_func.h"
#include "date_func.h"
#include "clear_func.h"
@ -43,12 +44,15 @@
#include "water.h"
#include "company_gui.h"
#include "viewport_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.
@ -661,12 +665,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;
@ -674,9 +672,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 (;;) {
@ -684,13 +688,90 @@ 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 && _settings_game.construction.chunnel &&
(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++;
if (IsTileType(end_tile, MP_WATER) && IsSea(end_tile)) is_chunnel = true;
}
/* The water was passed */
if (!is_chunnel) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA);
head_tiles = 0;
found_tunnel_tile = INVALID_TILE;
}
if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z, ITIWF_IGNORE_CHUNNEL)) {
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++;
}
if (is_chunnel && !_cheats.crossing_tunnels.value) {
/*
* Chunnel check: second pass
*
* Make sure that we don't intersect with any other chunnels
*/
TileIndex check_tile = start_tile;
for (;;) {
check_tile += delta;
if (check_tile == end_tile) break;
if (IsTunnelInWay(check_tile, start_z, ITIWF_CHUNNEL_ONLY)) {
_build_tunnel_endtile = check_tile;
return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY);
}
}
}
/* The cost of the digging. */
for (int i = tiles; i > 0; i--) {
if (tiles == tiles_bump) {
tiles_coef++;
tiles_bump *= 2;
@ -744,13 +825,24 @@ 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, TileHeight(tn), is_chunnel);
if (transport_type == TRANSPORT_RAIL) {
if (!IsTunnelTile(start_tile) && c != NULL) 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 {
@ -760,8 +852,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
c->infrastructure.road[rt] += num_pieces * 2; // A full diagonal road has two road bits.
}
}
MakeRoadTunnel(start_tile, company, direction, rts);
MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), rts);
MakeRoadTunnel(start_tile, company, t->index, direction, rts);
MakeRoadTunnel(end_tile, company, t->index, ReverseDiagDir(direction), rts);
}
DirtyCompanyInfrastructureWindows(company);
}
@ -850,6 +942,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) {
@ -873,6 +967,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
DirtyCompanyInfrastructureWindows(owner);
}
delete Tunnel::GetByTile(tile);
DoClearSquare(tile);
DoClearSquare(endtile);
@ -895,13 +991,15 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
}
}
delete Tunnel::GetByTile(tile);
DoClearSquare(tile);
DoClearSquare(endtile);
}
ViewportMapInvalidateTunnelCacheByTile(tile);
ViewportMapInvalidateTunnelCacheByTile(endtile);
}
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len);
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len * (is_chunnel ? 2 : 1));
}
@ -1764,7 +1862,11 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
TransportType tt = GetTunnelBridgeTransportType(tile);
if (IsTunnel(tile)) {
td->str = (tt == TRANSPORT_RAIL) ? IsTunnelBridgeWithSignalSimulation(tile) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL : STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD;
if (Tunnel::GetByTile(tile)->is_chunnel) {
td->str = (tt == TRANSPORT_RAIL) ? IsTunnelBridgeWithSignalSimulation(tile) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL_CHUNNEL : STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_CHUNNEL : STR_LAI_TUNNEL_DESCRIPTION_ROAD_CHUNNEL;
} else {
td->str = (tt == TRANSPORT_RAIL) ? IsTunnelBridgeWithSignalSimulation(tile) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL : STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD;
}
} else { // IsBridge(tile)
td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : IsTunnelBridgeWithSignalSimulation(tile) ? STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt];
}
@ -2027,6 +2129,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;
}
@ -2034,6 +2137,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);
@ -2051,6 +2155,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti
rv->tile = tile;
rv->cur_image_valid_dir = INVALID_DIR;
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 {
@ -2060,6 +2165,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->cur_image_valid_dir = INVALID_DIR;
rv->state = DiagDirToDiagTrackdir(vdir);
@ -2069,6 +2175,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;

@ -13,7 +13,7 @@
#define TUNNELBRIDGE_MAP_H
#include "bridge_map.h"
#include "tunnel_map.h"
#include "tunnel_base.h"
#include "cmd_helper.h"
#include "signal_type.h"

@ -14,6 +14,7 @@
#include "depot_type.h"
#include "tile_map.h"
#include "tree_map.h"
/**
* Bit field layout of m5 for water tiles.
@ -204,7 +205,7 @@ static inline bool IsCoast(TileIndex t)
*/
static inline bool IsCoastTile(TileIndex t)
{
return IsTileType(t, MP_WATER) && IsCoast(t);
return (IsTileType(t, MP_WATER) && IsCoast(t)) || (IsTileType(t, MP_TREES) && GetTreeGround(t) == TREE_GROUND_SHORE);
}
/**

Loading…
Cancel
Save