diff --git a/source.list b/source.list index b63ccc58de..58f5a9fc6e 100644 --- a/source.list +++ b/source.list @@ -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 diff --git a/src/core/pool_func.hpp b/src/core/pool_func.hpp index 5569addbd7..a5375e13fc 100644 --- a/src/core/pool_func.hpp +++ b/src/core/pool_func.hpp @@ -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 } diff --git a/src/core/pool_type.hpp b/src/core/pool_type.hpp index 52b946c225..91986d3a18 100644 --- a/src/core/pool_type.hpp +++ b/src/core/pool_type.hpp @@ -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: diff --git a/src/ground_vehicle.cpp b/src/ground_vehicle.cpp index e6bc103c46..9e01b42e6a 100644 --- a/src/ground_vehicle.cpp +++ b/src/ground_vehicle.cpp @@ -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::IsChainInDepot() const return true; } +/** + * Updates vehicle's Z inclination inside a wormhole, where applicable. + */ +template +void GroundVehicle::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; /* Instantiation for RoadVehicle */ diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp index 346ee82d13..78e539dbb7 100644 --- a/src/ground_vehicle.hpp +++ b/src/ground_vehicle.hpp @@ -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 { #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(); diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 856d80fe97..70dd732158 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -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(); } diff --git a/src/lang/english.txt b/src/lang/english.txt index e8f26e201a..1e781370e0 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -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 diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 996a72036f..ed859e502b 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -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); diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 91a14d5df0..8c8a8b42da 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -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; } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 5c2b72b527..b89790e8ea 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -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(); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index df7437a4e0..5b11a856f4 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -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 }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 36a82104f6..835410010d 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -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 diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index cf82659e17..0e25fdefd2 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -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, }; diff --git a/src/saveload/tunnel_sl.cpp b/src/saveload/tunnel_sl.cpp new file mode 100644 index 0000000000..d50b4bcd83 --- /dev/null +++ b/src/saveload/tunnel_sl.cpp @@ -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 . + */ + +/** @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}, +}; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index d90a5b6864..c49bc96351 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -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")); diff --git a/src/settings_type.h b/src/settings_type.h index 372ec8a267..967016c96c 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -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? diff --git a/src/table/settings.ini b/src/table/settings.ini index 35d84f781d..e37ccdd841 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -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 diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp index 1e9888e489..3657e7f6eb 100644 --- a/src/terraform_cmd.cpp +++ b/src/terraform_cmd.cpp @@ -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); } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 2743e17e45..679efbb188 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -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 */ diff --git a/src/tunnel_base.h b/src/tunnel_base.h new file mode 100644 index 0000000000..06196c2359 --- /dev/null +++ b/src/tunnel_base.h @@ -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 . + */ + +/** @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 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 */ diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index 4e6d5a7e13..b679bc0e00 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -12,8 +12,84 @@ #include "stdafx.h" #include "tunnelbridge_map.h" +#include "core/pool_func.hpp" +#include + #include "safeguards.h" +/** All tunnel portals tucked away in a pool. */ +TunnelPool _tunnel_pool("Tunnel"); +INSTANTIATE_POOL_METHODS(Tunnel) + +static std::unordered_map tunnel_tile_index_map; +static std::unordered_multimap 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()); +} + +/** + * Clean up a tunnel tile + */ +Tunnel::~Tunnel() +{ + 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; +} /** * Gets the other end of the tunnel. Where a vehicle would reappear when it @@ -23,52 +99,39 @@ */ TileIndex GetOtherTunnelEnd(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; + Tunnel *t = Tunnel::GetByTile(tile); + return t->tile_n == tile ? t->tile_s : t->tile_n; } - -/** - * Is there a tunnel in the way in the given direction? - * @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. - */ -bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir) +static inline bool IsTunnelInWaySingleAxis(TileIndex tile, int z, IsTunnelInWayFlags flags, bool y_axis, TileIndexDiff tile_diff) { - TileIndexDiff delta = TileOffsByDiagDir(dir); - int height; + 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; - do { - tile -= delta; - if (!IsValidTile(tile)) return false; - height = GetTileZ(tile); - } while (z < height); - - 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)); } diff --git a/src/tunnel_map.h b/src/tunnel_map.h index ec798587a4..875775eff4 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -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 */ diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 6cd8c04367..0ed3a53db0 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -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; - if (!_cheats.crossing_tunnels.value && IsTunnelInWayDir(end_tile, start_z, tunnel_in_way_dir)) { - return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + /* 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; + } + + /* 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; diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index 150bf80a0b..98f6e9190d 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -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" diff --git a/src/water_map.h b/src/water_map.h index ab249a8279..fcfd0d5324 100644 --- a/src/water_map.h +++ b/src/water_map.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); } /**