From ad070b91ea532e89efd74123ada0dfd9924de0e4 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:43:45 +0000 Subject: [PATCH 01/28] Chunnel patch 28392: Add: Basic tunnel pool. (no save) https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- source.list | 1 + src/tunnel_base.h | 40 ++++++++++++++++++++++++++++++++++++++++ src/tunnel_map.cpp | 13 +++++++++++++ src/tunnel_map.h | 23 +++++++++++++++++++---- src/tunnelbridge_cmd.cpp | 24 ++++++++++++++++++++---- src/tunnelbridge_map.h | 2 +- 6 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 src/tunnel_base.h diff --git a/source.list b/source.list index b63ccc58de..125198c23c 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 diff --git a/src/tunnel_base.h b/src/tunnel_base.h new file mode 100644 index 0000000000..f03325eabc --- /dev/null +++ b/src/tunnel_base.h @@ -0,0 +1,40 @@ +/* $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. + + Tunnel(TileIndex tile_n = INVALID_TILE) : tile_n(tile_n) {} + ~Tunnel(); + + static inline Tunnel *GetByTile(TileIndex tile) + { + return Tunnel::Get(GetTunnelIndex(tile)); + } +}; + +#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..5c6ab5d598 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -12,8 +12,21 @@ #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) + +/** + * Clean up a tunnel tile + */ +Tunnel::~Tunnel() +{ + if (CleaningPool()) return; +} /** * Gets the other end of the tunnel. Where a vehicle would reappear when it diff --git a/src/tunnel_map.h b/src/tunnel_map.h index ec798587a4..c1c1e45e24 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -14,6 +14,7 @@ #include "road_map.h" +typedef uint16 TunnelID; ///< Type for the unique identifier of tunnels. /** * Is this a tunnel (entrance)? @@ -37,6 +38,18 @@ 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); @@ -48,11 +61,11 @@ bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir); * @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].m2 = id; _m[t].m3 = 0; _m[t].m4 = 0; _m[t].m5 = TRANSPORT_ROAD << 2 | d; @@ -70,11 +83,11 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTyp * @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; 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); @@ -84,4 +97,6 @@ static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailTyp _me[t].m7 = 0; } + + #endif /* TUNNEL_MAP_H */ diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 6cd8c04367..e5c20dffe2 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" @@ -49,6 +50,7 @@ #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. @@ -747,10 +749,20 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, 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; + Tunnel *t = new Tunnel(tn); + t->tile_s = ts; + 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 +772,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); } @@ -873,6 +885,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags) DirtyCompanyInfrastructureWindows(owner); } + delete Tunnel::GetByTile(tile); + DoClearSquare(tile); DoClearSquare(endtile); @@ -895,6 +909,8 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags) } } + delete Tunnel::GetByTile(tile); + DoClearSquare(tile); DoClearSquare(endtile); } 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" From 67e7d12eb7ff439a570d102b48233c10082f1220 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:44:22 +0000 Subject: [PATCH 02/28] Chunnel patch 28393: Codechange: Rework GetOtherTunnelEnd proc. https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- src/saveload/afterload.cpp | 45 +++++++++++++++++++++++++++++++++++++- src/tunnel_map.cpp | 16 ++------------ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index f91ed3989e..47b175d1e2 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" @@ -563,6 +564,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. @@ -2009,6 +2029,29 @@ bool AfterLoadGame() } } + /* Tunnel pool has to be initiated before reservations. */ + 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()) return false; + + Tunnel *t = new Tunnel(start_tile); + t->tile_s = end_tile; + + DEBUG(misc, 0, "Tun start %#x, index=%#x", t->tile_n, t->index); + DEBUG(misc, 0, "Tun end %#x, index=%#x", t->tile_s, t->index); + + _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. */ @@ -2643,7 +2686,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/tunnel_map.cpp b/src/tunnel_map.cpp index 5c6ab5d598..e46d7af947 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -36,20 +36,8 @@ Tunnel::~Tunnel() */ 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; } From f7ced74c222e64317ccb44cc8ea4e447b94826f5 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:45:06 +0000 Subject: [PATCH 03/28] Chunnel patch 28394: Codechange: Rework IsTunnelInWay proc. https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- src/tunnel_map.cpp | 40 ++++++++++++++++------------------------ src/tunnel_map.h | 1 - src/tunnelbridge_cmd.cpp | 8 +------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index e46d7af947..1771b1d82b 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -40,28 +40,6 @@ TileIndex GetOtherTunnelEnd(TileIndex 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) -{ - 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; -} - /** * Is there a tunnel in the way in any direction? * @param tile the tile to search from. @@ -70,6 +48,20 @@ bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir) */ bool IsTunnelInWay(TileIndex tile, int z) { - 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); + + Tunnel *t; + FOR_ALL_TUNNELS(t) { + 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 + } + + return true; + } + return false; } diff --git a/src/tunnel_map.h b/src/tunnel_map.h index c1c1e45e24..3621f54eb6 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -52,7 +52,6 @@ static inline TunnelID GetTunnelIndex(TileIndex t) TileIndex GetOtherTunnelEnd(TileIndex); bool IsTunnelInWay(TileIndex, int z); -bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir); /** * Makes a road tunnel entrance diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index e5c20dffe2..9091fb2502 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -663,12 +663,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; @@ -688,7 +682,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, if (start_z == end_z) break; - if (!_cheats.crossing_tunnels.value && IsTunnelInWayDir(end_tile, start_z, tunnel_in_way_dir)) { + if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z)) { return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); } From dee526c6d6b1a608c04341da6ee1cec98e395a93 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:46:13 +0000 Subject: [PATCH 04/28] Chunnel patch 28395: Add: Basic chunnel. Creating tunnels under water. https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- src/tunnelbridge_cmd.cpp | 30 +++++++++++++++++++++++++++++- src/water_map.h | 3 ++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 9091fb2502..599bfcf758 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -680,13 +680,38 @@ 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) { + /* Handle chunnels only on sea level. */ + if (end_z > 0) break; + if (!IsValidTile(end_tile + delta) || !IsValidTile(end_tile + delta * 2)) break; + + /* Test if we are on a shore. */ + if (!IsCoastTile(end_tile) && !HasTileWaterGround(end_tile + delta) && !HasTileWaterGround(end_tile + delta * 2)) 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; + + end_tile += delta; + tiles++; + } + } if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z)) { return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); } tiles++; + } + /* The cost of the digging. */ + for (int i = tiles; i > 0; i--) { if (tiles == tiles_bump) { tiles_coef++; tiles_bump *= 2; @@ -2044,6 +2069,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); @@ -2070,6 +2096,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); @@ -2079,6 +2106,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/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); } /** From dfce9cc4c0491a84bb300e33fd1cad32147c3612 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:47:20 +0000 Subject: [PATCH 05/28] Chunnel patch 28396: Codechange: Chunnel portal must have minimal length of 4 tiles. https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- src/lang/english.txt | 1 + src/terraform_cmd.cpp | 2 +- src/tunnel_base.h | 1 + src/tunnel_map.cpp | 9 ++++++++- src/tunnel_map.h | 2 +- src/tunnelbridge_cmd.cpp | 14 ++++++++++++-- 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index e8f26e201a..ff19eab31d 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5002,6 +5002,7 @@ 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 have minimal three tiles at the begin and end. # Object related errors STR_ERROR_TOO_MANY_OBJECTS :{WHITE}... too many objects diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp index 1e9888e489..0b82c60efe 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, false)) { _terraform_err_tile = tile; // highlight the tile above the tunnel return_cmd_error(STR_ERROR_EXCAVATION_WOULD_DAMAGE); } diff --git a/src/tunnel_base.h b/src/tunnel_base.h index f03325eabc..66b0871a36 100644 --- a/src/tunnel_base.h +++ b/src/tunnel_base.h @@ -24,6 +24,7 @@ struct Tunnel : TunnelPool::PoolItem<&_tunnel_pool> { TileIndex tile_n; // North tile of tunnel. TileIndex tile_s; // South tile of tunnel. + bool is_chunnel; Tunnel(TileIndex tile_n = INVALID_TILE) : tile_n(tile_n) {} ~Tunnel(); diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index 1771b1d82b..358e83efc9 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -44,9 +44,10 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) * 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 not_allowed Only terra forming does not search between tunnel portals. * @return true if and only if there is a tunnel. */ -bool IsTunnelInWay(TileIndex tile, int z) +bool IsTunnelInWay(TileIndex tile, int z, bool not_allowed) { uint x = TileX(tile); uint y = TileY(tile); @@ -61,6 +62,12 @@ bool IsTunnelInWay(TileIndex tile, int z) if (TileY(t->tile_n) != y || (int)TileHeight(t->tile_n) != z) continue; // dir DIAGDIR_SW } + if (t->is_chunnel > not_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 3621f54eb6..5d77822c6a 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -51,7 +51,7 @@ static inline TunnelID GetTunnelIndex(TileIndex t) } TileIndex GetOtherTunnelEnd(TileIndex); -bool IsTunnelInWay(TileIndex, int z); +bool IsTunnelInWay(TileIndex, int z, bool not_allowed = true); /** * Makes a road tunnel entrance diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 599bfcf758..67d76b4c8c 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -670,6 +670,10 @@ 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 = 1; /* Number of tiles at which the cost increase coefficient per tile is halved */ int tiles_bump = 25; @@ -691,6 +695,9 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, /* A shore was found so pass the water and find a proper shore tile that potentially * could have a tunnel portal behind. */ + is_chunnel = true; + if (head_tiles < 4 && is_chunnel) break; // Not enough lead way to go under water. + head_tiles = 0; for (;;) { if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER); @@ -704,10 +711,10 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, tiles++; } } - if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z)) { + if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z, false)) { return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); } - + head_tiles++; tiles++; } /* The cost of the digging. */ @@ -730,6 +737,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, if (tiles > _settings_game.construction.max_tunnel_length) return_cmd_error(STR_ERROR_TUNNEL_TOO_LONG); + if (head_tiles < 4 && is_chunnel) return_cmd_error(STR_ERROR_TUNNEL_RAMP_TOO_SHORT); + if (HasTileWaterGround(end_tile)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER); /* Clear the tile in any case */ @@ -777,6 +786,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, if (!Tunnel::CanAllocateItem()) return CMD_ERROR; Tunnel *t = new Tunnel(tn); t->tile_s = ts; + t->is_chunnel = is_chunnel; if (transport_type == TRANSPORT_RAIL) { if (!IsTunnelTile(start_tile) && c != NULL) c->infrastructure.rail[railtype] += num_pieces; From 1ac8f66de112502c5fafb6e7f1df8cbaa10e2e52 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:50:47 +0000 Subject: [PATCH 06/28] Chunnel patch 28397: Codechange: Chunnels can't pass oilrigs. https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- src/industry_cmd.cpp | 6 ++++++ src/lang/english.txt | 1 + src/tunnelbridge_cmd.cpp | 8 ++++++++ 3 files changed, 15 insertions(+) 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 ff19eab31d..6d0796da49 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5003,6 +5003,7 @@ STR_ERROR_TUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel w 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 have minimal three tiles at the begin and end. +STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL :{WHITE}No oil rigs allowed above underwater tunnels. # Object related errors STR_ERROR_TOO_MANY_OBJECTS :{WHITE}... too many objects diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 67d76b4c8c..df03af37a6 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -44,6 +44,8 @@ #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" @@ -707,6 +709,12 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, 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)) return_cmd_error(STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL); + end_tile += delta; tiles++; } From f173b74e81ee482ee0988222784ed6e4420d6a3e Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:51:37 +0000 Subject: [PATCH 07/28] Chunnel patch 28398: Codechange: Save Tunnel Pool. https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- source.list | 1 + src/saveload/afterload.cpp | 29 ++++++++++++---------- src/saveload/saveload.cpp | 4 ++- src/saveload/tunnel_sl.cpp | 50 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 src/saveload/tunnel_sl.cpp diff --git a/source.list b/source.list index 125198c23c..58f5a9fc6e 100644 --- a/source.list +++ b/source.list @@ -671,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/saveload/afterload.cpp b/src/saveload/afterload.cpp index 47b175d1e2..76f3349ea5 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2030,23 +2030,26 @@ bool AfterLoadGame() } /* Tunnel pool has to be initiated before reservations. */ - 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 (IsSavegameVersionBefore(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()) return false; + if (!Tunnel::CanAllocateItem()) return false; - Tunnel *t = new Tunnel(start_tile); - t->tile_s = end_tile; + Tunnel *t = new Tunnel(start_tile); + t->tile_s = end_tile; + t->is_chunnel = 0; - DEBUG(misc, 0, "Tun start %#x, index=%#x", t->tile_n, t->index); - DEBUG(misc, 0, "Tun end %#x, index=%#x", t->tile_s, t->index); + DEBUG(misc, 0, "Tun start %#x, index=%#x", t->tile_n, t->index); + DEBUG(misc, 0, "Tun end %#x, index=%#x", t->tile_s, t->index); - _m[start_tile].m2 = t->index; - _m[end_tile].m2 = t->index; + _m[start_tile].m2 = t->index; + _m[end_tile].m2 = t->index; + } } } } diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index cf82659e17..3376d074b8 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -270,7 +270,7 @@ * 194 26881 1.5.x, 1.6.0 * 195 27572 1.6.x */ -extern const uint16 SAVEGAME_VERSION = 195; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 196; ///< Current savegame version of OpenTTD. const uint16 SAVEGAME_VERSION_EXT = 0x8000; ///< Savegame extension indicator mask SavegameType _savegame_type; ///< type of savegame we are loading @@ -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..f477ef6e7b --- /dev/null +++ b/src/saveload/tunnel_sl.cpp @@ -0,0 +1,50 @@ +/* $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, 196, SL_MAX_VERSION), + SLE_CONDVAR(Tunnel, tile_s, SLE_UINT32, 196, SL_MAX_VERSION), + SLE_CONDVAR(Tunnel, is_chunnel, SLE_BOOL, 196, 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); + } +} + + +extern const ChunkHandler _tunnel_chunk_handlers[] = { + { 'TUNN', Save_TUNN, Load_TUNN, NULL, NULL, CH_ARRAY | CH_LAST}, +}; From 7808370f864404caa67f0dcd2c64ffdaa29fdbe1 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:53:11 +0000 Subject: [PATCH 08/28] Chunnel patch 28399: debug [modified] Don't remove other debug outputs https://www.tt-forums.net/viewtopic.php?p=1183416#p1183416 --- src/misc_gui.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 996a72036f..ece37bea41 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 chunel: %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); From 15c8e676556aadc4ea01d8ce68618a390572b047 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:57:59 +0000 Subject: [PATCH 09/28] Chunnel patch 28400: Codechange: Chunnels can now pass normal tunnels, fixed tile highlighting, fixed ramp detection. https://www.tt-forums.net/viewtopic.php?p=1183580#p1183580 --- src/lang/english.txt | 1 + src/tunnelbridge_cmd.cpp | 48 +++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 6d0796da49..e3e81916dc 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5004,6 +5004,7 @@ STR_ERROR_UNABLE_TO_EXCAVATE_LAND :{WHITE}Unable t STR_ERROR_TUNNEL_TOO_LONG :{WHITE}... tunnel too long STR_ERROR_TUNNEL_RAMP_TOO_SHORT :{WHITE}... Ramp too short, tunnels under water have minimal three tiles at the begin and end. 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/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index df03af37a6..ba73ffc7c4 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -675,10 +675,12 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, /* flag for chunnels. */ bool is_chunnel = false; /* Number of chunnel head tiles. */ - int head_tiles = 1; + 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 (;;) { @@ -687,19 +689,31 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, end_tileh = GetTileSlope(end_tile, &end_z); if (start_z == end_z) { - /* Handle chunnels only on sea level. */ - if (end_z > 0) break; - - if (!IsValidTile(end_tile + delta) || !IsValidTile(end_tile + delta * 2)) break; + _build_tunnel_endtile = found_tunnel_tile != INVALID_TILE ? found_tunnel_tile : end_tile; /* Test if we are on a shore. */ - if (!IsCoastTile(end_tile) && !HasTileWaterGround(end_tile + delta) && !HasTileWaterGround(end_tile + delta * 2)) break; + 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; + } /* A shore was found so pass the water and find a proper shore tile that potentially * could have a tunnel portal behind. */ - is_chunnel = true; - if (head_tiles < 4 && is_chunnel) break; // Not enough lead way to go under water. - head_tiles = 0; for (;;) { if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER); @@ -713,14 +727,24 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, 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)) return_cmd_error(STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL); + 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, false)) { - return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + 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++; @@ -745,8 +769,6 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, if (tiles > _settings_game.construction.max_tunnel_length) return_cmd_error(STR_ERROR_TUNNEL_TOO_LONG); - if (head_tiles < 4 && is_chunnel) return_cmd_error(STR_ERROR_TUNNEL_RAMP_TOO_SHORT); - if (HasTileWaterGround(end_tile)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER); /* Clear the tile in any case */ From 7e6215f271cca6894bc1e45feaefe083270bf280 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sat, 4 Mar 2017 11:58:59 +0000 Subject: [PATCH 10/28] Chunnel patch 28401: Add: Tile description chunnel. https://www.tt-forums.net/viewtopic.php?p=1183580#p1183580 --- src/lang/english.txt | 3 +++ src/tunnelbridge_cmd.cpp | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index e3e81916dc..a819404a06 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3042,8 +3042,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 passing water STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL :Railway tunnel with signal simulation +STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL_CHUNNEL :Railway tunnel with signal simulation passing water STR_LAI_TUNNEL_DESCRIPTION_ROAD :Road tunnel +STR_LAI_TUNNEL_DESCRIPTION_ROAD_CHUNNEL :Road tunnel passing water STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL :Railway bridge with signal simulation STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL :Steel suspension rail bridge diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index ba73ffc7c4..b06c20fe65 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -1839,7 +1839,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]; } From 13a716980ae433e459dcc2e56c7ac21a4fbd9c18 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 12:10:54 +0000 Subject: [PATCH 11/28] Chunnel: String text adjustments. --- src/lang/english.txt | 8 ++++---- src/misc_gui.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index a819404a06..0b3352e4cc 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3042,11 +3042,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 passing water +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 passing water +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 passing water +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 @@ -5005,7 +5005,7 @@ 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 have minimal three tiles at the begin and end. +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_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. diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index ece37bea41..ed859e502b 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -129,7 +129,7 @@ public: 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 chunel: %u" , Tunnel::GetByTile(tile)->is_chunnel); + 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); From 21bbb42d7e5b2b0a12ef5e6284cc62cbe54645cf Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 12:21:24 +0000 Subject: [PATCH 12/28] Chunnel: Adjust arguments of IsTunnelInWay --- src/terraform_cmd.cpp | 2 +- src/tunnel_map.cpp | 6 +++--- src/tunnel_map.h | 2 +- src/tunnelbridge_cmd.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp index 0b82c60efe..71a171c665 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, false)) { + if (direction == -1 && IsTunnelInWay(tile, z_min, true)) { _terraform_err_tile = tile; // highlight the tile above the tunnel return_cmd_error(STR_ERROR_EXCAVATION_WOULD_DAMAGE); } diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index 358e83efc9..5ffb409c47 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -44,10 +44,10 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) * 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 not_allowed Only terra forming does not search between tunnel portals. + * @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 not_allowed) +bool IsTunnelInWay(TileIndex tile, int z, bool chunnel_allowed) { uint x = TileX(tile); uint y = TileY(tile); @@ -62,7 +62,7 @@ bool IsTunnelInWay(TileIndex tile, int z, bool not_allowed) if (TileY(t->tile_n) != y || (int)TileHeight(t->tile_n) != z) continue; // dir DIAGDIR_SW } - if (t->is_chunnel > not_allowed) { + 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; diff --git a/src/tunnel_map.h b/src/tunnel_map.h index 5d77822c6a..2ea126523f 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -51,7 +51,7 @@ static inline TunnelID GetTunnelIndex(TileIndex t) } TileIndex GetOtherTunnelEnd(TileIndex); -bool IsTunnelInWay(TileIndex, int z, bool not_allowed = true); +bool IsTunnelInWay(TileIndex, int z, bool chunnel_allowed = false); /** * Makes a road tunnel entrance diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index b06c20fe65..e0be7f26e1 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -740,7 +740,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, head_tiles = 0; found_tunnel_tile = INVALID_TILE; } - if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z, false)) { + if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z, 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; From 04244e72152b554dfb0e0f962b575666cedd0ad8 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 12:22:31 +0000 Subject: [PATCH 13/28] Chunnel: Fix typo in name of GetOtherTunnelBridgeEndOld --- src/saveload/afterload.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 76f3349ea5..b56947490f 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -564,7 +564,7 @@ static inline bool MayHaveBridgeAbove(TileIndex t) IsTileType(t, MP_WATER) || IsTileType(t, MP_TUNNELBRIDGE) || IsTileType(t, MP_OBJECT); } -TileIndex GetOtherTunnelBridgeEndOLd(TileIndex tile) +TileIndex GetOtherTunnelBridgeEndOld(TileIndex tile) { DiagDirection dir = GetTunnelBridgeDirection(tile); TileIndexDiff delta = TileOffsByDiagDir(dir); @@ -2036,7 +2036,7 @@ bool AfterLoadGame() DiagDirection dir = GetTunnelBridgeDirection(t); if (dir == DIAGDIR_SE || dir == DIAGDIR_SW) { TileIndex start_tile = t; - TileIndex end_tile = GetOtherTunnelBridgeEndOLd(start_tile); + TileIndex end_tile = GetOtherTunnelBridgeEndOld(start_tile); if (!Tunnel::CanAllocateItem()) return false; @@ -2689,7 +2689,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 ? GetOtherTunnelBridgeEndOLd(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 From ee0d6e2fe69ffe31fc612da664149c7f37f356a5 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 12:23:30 +0000 Subject: [PATCH 14/28] Chunnel: Remove debug prints from afterload code. --- src/saveload/afterload.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index b56947490f..0c467f56b0 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2044,9 +2044,6 @@ bool AfterLoadGame() t->tile_s = end_tile; t->is_chunnel = 0; - DEBUG(misc, 0, "Tun start %#x, index=%#x", t->tile_n, t->index); - DEBUG(misc, 0, "Tun end %#x, index=%#x", t->tile_s, t->index); - _m[start_tile].m2 = t->index; _m[end_tile].m2 = t->index; } From aac5b2d4ad236e4bfc0e549852350ead0c026891 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 12:36:01 +0000 Subject: [PATCH 15/28] Chunnel: Adjust struct Tunnel constructor --- src/saveload/afterload.cpp | 4 +--- src/tunnel_base.h | 3 ++- src/tunnelbridge_cmd.cpp | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 0c467f56b0..b852c48009 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2040,9 +2040,7 @@ bool AfterLoadGame() if (!Tunnel::CanAllocateItem()) return false; - Tunnel *t = new Tunnel(start_tile); - t->tile_s = end_tile; - t->is_chunnel = 0; + const Tunnel *t = new Tunnel(start_tile, end_tile, false); _m[start_tile].m2 = t->index; _m[end_tile].m2 = t->index; diff --git a/src/tunnel_base.h b/src/tunnel_base.h index 66b0871a36..6d088bb8f3 100644 --- a/src/tunnel_base.h +++ b/src/tunnel_base.h @@ -26,7 +26,8 @@ struct Tunnel : TunnelPool::PoolItem<&_tunnel_pool> { TileIndex tile_s; // South tile of tunnel. bool is_chunnel; - Tunnel(TileIndex tile_n = INVALID_TILE) : tile_n(tile_n) {} + 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) diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index e0be7f26e1..a57ce8c1f9 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -814,9 +814,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, if(start_tile > end_tile) Swap(tn, ts); if (!Tunnel::CanAllocateItem()) return CMD_ERROR; - Tunnel *t = new Tunnel(tn); - t->tile_s = ts; - t->is_chunnel = is_chunnel; + const Tunnel *t = new Tunnel(tn, ts, is_chunnel); if (transport_type == TRANSPORT_RAIL) { if (!IsTunnelTile(start_tile) && c != NULL) c->infrastructure.rail[railtype] += num_pieces; From 75410ddae4a457a84db054d680799e0334071f79 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 12:39:32 +0000 Subject: [PATCH 16/28] Chunnel: Save/load changes --- src/saveload/afterload.cpp | 2 +- src/saveload/extended_ver_sl.cpp | 1 + src/saveload/extended_ver_sl.h | 1 + src/saveload/saveload.cpp | 2 +- src/saveload/tunnel_sl.cpp | 6 +++--- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index b852c48009..1791870e0a 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2030,7 +2030,7 @@ bool AfterLoadGame() } /* Tunnel pool has to be initiated before reservations. */ - if (IsSavegameVersionBefore(196)) { + if (SlXvIsFeatureMissing(XSLFI_CHUNNEL)) { for (TileIndex t = 0; t < map_size; t++) { if (IsTunnelTile(t)) { DiagDirection dir = GetTunnelBridgeDirection(t); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 3480e15c5e..8384f95857 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 3376d074b8..0e25fdefd2 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -270,7 +270,7 @@ * 194 26881 1.5.x, 1.6.0 * 195 27572 1.6.x */ -extern const uint16 SAVEGAME_VERSION = 196; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 195; ///< Current savegame version of OpenTTD. const uint16 SAVEGAME_VERSION_EXT = 0x8000; ///< Savegame extension indicator mask SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/saveload/tunnel_sl.cpp b/src/saveload/tunnel_sl.cpp index f477ef6e7b..310b25a0b2 100644 --- a/src/saveload/tunnel_sl.cpp +++ b/src/saveload/tunnel_sl.cpp @@ -18,9 +18,9 @@ static const SaveLoad _tunnel_desc[] = { - SLE_CONDVAR(Tunnel, tile_n, SLE_UINT32, 196, SL_MAX_VERSION), - SLE_CONDVAR(Tunnel, tile_s, SLE_UINT32, 196, SL_MAX_VERSION), - SLE_CONDVAR(Tunnel, is_chunnel, SLE_BOOL, 196, SL_MAX_VERSION), + 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, is_chunnel, SLE_BOOL, 0, SL_MAX_VERSION), SLE_END() }; From ab38623ea68a5e00fdbff08aac85f1d45d77a024 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 12:43:48 +0000 Subject: [PATCH 17/28] Chunnel: Adjust documentation and whitespace in tunnel_map.h --- src/tunnel_map.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tunnel_map.h b/src/tunnel_map.h index 2ea126523f..b684437057 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -57,6 +57,7 @@ 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 */ @@ -79,10 +80,11 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, TunnelID id, DiagDirecti * 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,TunnelID id, 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); @@ -96,6 +98,4 @@ static inline void MakeRailTunnel(TileIndex t, Owner o,TunnelID id, DiagDirectio _me[t].m7 = 0; } - - #endif /* TUNNEL_MAP_H */ From 02b33e7f64504cbc15fcdeedab9501c12998ebfe Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 13:06:43 +0000 Subject: [PATCH 18/28] Chunnel: Add error text for too many tunnels. --- src/lang/english.txt | 1 + src/saveload/afterload.cpp | 7 ++++++- src/tunnelbridge_cmd.cpp | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 0b3352e4cc..5327476bde 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5006,6 +5006,7 @@ STR_ERROR_TUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel w 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. diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 1791870e0a..5066c55598 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2038,7 +2038,12 @@ bool AfterLoadGame() TileIndex start_tile = t; TileIndex end_tile = GetOtherTunnelBridgeEndOld(start_tile); - if (!Tunnel::CanAllocateItem()) return false; + 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); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index a57ce8c1f9..22a862c6e1 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -813,7 +813,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, TileIndex ts = end_tile; if(start_tile > end_tile) Swap(tn, ts); - if (!Tunnel::CanAllocateItem()) return CMD_ERROR; + 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) { From 212f6ce6ef8f28d73784cef6424c9d8f174db5d1 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 13:23:52 +0000 Subject: [PATCH 19/28] Chunnel: Add setting to enable construction, default off. --- src/lang/english.txt | 3 +++ src/settings_gui.cpp | 1 + src/settings_type.h | 1 + src/table/settings.ini | 10 ++++++++++ src/tunnelbridge_cmd.cpp | 2 +- 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 5327476bde..31a87d6262 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. 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/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 22a862c6e1..0bd2e54bb8 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -692,7 +692,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, _build_tunnel_endtile = found_tunnel_tile != INVALID_TILE ? found_tunnel_tile : end_tile; /* Test if we are on a shore. */ - if (end_z == 0 && + 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)))) { From 87142ed840bb76361bc7a7219d643e49358487dd Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 23:39:27 +0000 Subject: [PATCH 20/28] Add a PreCleanPool() static method to pool item types. --- src/core/pool_func.hpp | 1 + src/core/pool_type.hpp | 7 +++++++ 2 files changed, 8 insertions(+) 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: From afd8c6b867f6d170b4a6fb0615419462a996f918 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 4 Mar 2017 23:49:55 +0000 Subject: [PATCH 21/28] Chunnel: Increase tunnel pool size, make tunnel IDs 32 bit. Add lookup mechanism for tunnel IDs which don't fit in 16 bits in M2. --- src/saveload/tunnel_sl.cpp | 1 + src/tunnel_base.h | 12 ++++++++++-- src/tunnel_map.cpp | 34 ++++++++++++++++++++++++++++++++++ src/tunnel_map.h | 13 +++++++++---- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/saveload/tunnel_sl.cpp b/src/saveload/tunnel_sl.cpp index 310b25a0b2..470de84590 100644 --- a/src/saveload/tunnel_sl.cpp +++ b/src/saveload/tunnel_sl.cpp @@ -41,6 +41,7 @@ static void Load_TUNN() while ((index = SlIterateArray()) != -1) { Tunnel *tunnel = new (index) Tunnel(); SlObject(tunnel, _tunnel_desc); + tunnel->UpdateIndexes(); } } diff --git a/src/tunnel_base.h b/src/tunnel_base.h index 6d088bb8f3..92408abde8 100644 --- a/src/tunnel_base.h +++ b/src/tunnel_base.h @@ -17,7 +17,7 @@ struct Tunnel; -typedef Pool TunnelPool; +typedef Pool TunnelPool; extern TunnelPool _tunnel_pool; struct Tunnel : TunnelPool::PoolItem<&_tunnel_pool> { @@ -27,13 +27,21 @@ struct Tunnel : TunnelPool::PoolItem<&_tunnel_pool> { 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(); + Tunnel(TileIndex tile_n, TileIndex tile_s, bool is_chunnel) : tile_n(tile_n), tile_s(tile_s), 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) diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index 5ffb409c47..cd049f7397 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -13,6 +13,7 @@ #include "tunnelbridge_map.h" #include "core/pool_func.hpp" +#include #include "safeguards.h" @@ -20,12 +21,45 @@ TunnelPool _tunnel_pool("Tunnel"); INSTANTIATE_POOL_METHODS(Tunnel) +static std::unordered_map tunnel_tile_index_map; + /** * 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); + } +} + +/** + * 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 pool is about to be cleaned + */ +void Tunnel::PreCleanPool() +{ + tunnel_tile_index_map.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; } /** diff --git a/src/tunnel_map.h b/src/tunnel_map.h index b684437057..55f6ce7eea 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -14,7 +14,9 @@ #include "road_map.h" -typedef uint16 TunnelID; ///< Type for the unique identifier of tunnels. +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)? @@ -46,8 +48,11 @@ static inline bool IsTunnelTile(TileIndex t) */ static inline TunnelID GetTunnelIndex(TileIndex t) { + extern TunnelID GetTunnelIndexByLookup(TileIndex t); + assert(IsTunnelTile(t)); - return _m[t].m2; + TunnelID map_id = _m[t].m2; + return map_id == TUNNEL_ID_MAP_LOOKUP ? GetTunnelIndexByLookup(t) : map_id; } TileIndex GetOtherTunnelEnd(TileIndex); @@ -65,7 +70,7 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, TunnelID id, DiagDirecti { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = id; + _m[t].m2 = (id >= TUNNEL_ID_MAP_LOOKUP) ? TUNNEL_ID_MAP_LOOKUP : id; _m[t].m3 = 0; _m[t].m4 = 0; _m[t].m5 = TRANSPORT_ROAD << 2 | d; @@ -88,7 +93,7 @@ static inline void MakeRailTunnel(TileIndex t, Owner o, TunnelID id, DiagDirecti { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = id; + _m[t].m2 = (id >= TUNNEL_ID_MAP_LOOKUP) ? TUNNEL_ID_MAP_LOOKUP : id; 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); From ad15b47f723f4c0ee1d6d9a2fd8029cff8112b64 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Mar 2017 14:06:34 +0000 Subject: [PATCH 22/28] Chunnel: Fix setting of tunnel ID in load conversion, add method to set ID. --- src/saveload/afterload.cpp | 5 +++-- src/tunnel_map.h | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 5066c55598..94529fc456 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -58,6 +58,7 @@ #include "../error.h" #include "../disaster_vehicle.h" #include "../tracerestrict.h" +#include "../tunnel_map.h" #include "saveload_internal.h" @@ -2047,8 +2048,8 @@ bool AfterLoadGame() const Tunnel *t = new Tunnel(start_tile, end_tile, false); - _m[start_tile].m2 = t->index; - _m[end_tile].m2 = t->index; + SetTunnelIndex(start_tile, t->index); + SetTunnelIndex(end_tile, t->index); } } } diff --git a/src/tunnel_map.h b/src/tunnel_map.h index 55f6ce7eea..41102cb27e 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -58,6 +58,18 @@ static inline TunnelID GetTunnelIndex(TileIndex t) TileIndex GetOtherTunnelEnd(TileIndex); bool IsTunnelInWay(TileIndex, int z, bool chunnel_allowed = false); +/** + * 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 @@ -70,12 +82,12 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, TunnelID id, DiagDirecti { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = (id >= TUNNEL_ID_MAP_LOOKUP) ? TUNNEL_ID_MAP_LOOKUP : id; _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); @@ -93,7 +105,6 @@ static inline void MakeRailTunnel(TileIndex t, Owner o, TunnelID id, DiagDirecti { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = (id >= TUNNEL_ID_MAP_LOOKUP) ? TUNNEL_ID_MAP_LOOKUP : id; 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); @@ -101,6 +112,7 @@ static inline void MakeRailTunnel(TileIndex t, Owner o, TunnelID id, DiagDirecti _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 */ From 89a0a9c182185518869b1d6ce4ab92789286182b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Mar 2017 14:32:17 +0000 Subject: [PATCH 23/28] Chunnel: Use hash table for tunnel search, change tunnel fields and savegame format. --- src/saveload/afterload.cpp | 3 +- src/saveload/tunnel_sl.cpp | 1 + src/tunnel_base.h | 5 +-- src/tunnel_map.cpp | 70 ++++++++++++++++++++++++++------------ src/tunnelbridge_cmd.cpp | 2 +- 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 94529fc456..288faf2373 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2046,7 +2046,7 @@ bool AfterLoadGame() return false; } - const Tunnel *t = new Tunnel(start_tile, end_tile, false); + const Tunnel *t = new Tunnel(start_tile, end_tile, TileHeight(start_tile), false); SetTunnelIndex(start_tile, t->index); SetTunnelIndex(end_tile, t->index); @@ -2055,7 +2055,6 @@ bool AfterLoadGame() } } - /* 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. */ diff --git a/src/saveload/tunnel_sl.cpp b/src/saveload/tunnel_sl.cpp index 470de84590..d50b4bcd83 100644 --- a/src/saveload/tunnel_sl.cpp +++ b/src/saveload/tunnel_sl.cpp @@ -20,6 +20,7 @@ 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() }; diff --git a/src/tunnel_base.h b/src/tunnel_base.h index 92408abde8..06196c2359 100644 --- a/src/tunnel_base.h +++ b/src/tunnel_base.h @@ -24,12 +24,13 @@ struct Tunnel : TunnelPool::PoolItem<&_tunnel_pool> { TileIndex tile_n; // North tile of tunnel. TileIndex tile_s; // South tile of tunnel. - bool is_chunnel; + uint8 height; // Tunnel height + bool is_chunnel; // Whether this tunnel is a chunnel Tunnel() {} ~Tunnel(); - Tunnel(TileIndex tile_n, TileIndex tile_s, bool is_chunnel) : tile_n(tile_n), tile_s(tile_s), is_chunnel(is_chunnel) + 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(); } diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index cd049f7397..7b546848a4 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -22,6 +22,21 @@ 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 @@ -34,6 +49,17 @@ Tunnel::~Tunnel() 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); } /** @@ -45,6 +71,8 @@ void Tunnel::UpdateIndexes() 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); } /** @@ -53,6 +81,7 @@ void Tunnel::UpdateIndexes() void Tunnel::PreCleanPool() { tunnel_tile_index_map.clear(); + tunnel_axis_height_index.clear(); } TunnelID GetTunnelIndexByLookup(TileIndex t) @@ -74,6 +103,24 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) return t->tile_n == tile ? t->tile_s : t->tile_n; } +static inline bool IsTunnelInWaySingleAxis(TileIndex tile, int z, bool chunnel_allowed, 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; + + if (t->is_chunnel && chunnel_allowed) { + /* 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. @@ -83,26 +130,5 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) */ bool IsTunnelInWay(TileIndex tile, int z, bool chunnel_allowed) { - uint x = TileX(tile); - uint y = TileY(tile); - - Tunnel *t; - FOR_ALL_TUNNELS(t) { - 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; + return IsTunnelInWaySingleAxis(tile, z, chunnel_allowed, false, 1) || IsTunnelInWaySingleAxis(tile, z, chunnel_allowed, true, TileOffsByDiagDir(DIAGDIR_SE)); } diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 0bd2e54bb8..30df9ce9ef 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -814,7 +814,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, 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); + 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; From 500ef496946d3cca2275fc0b2579512231525fd8 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Mar 2017 14:38:23 +0000 Subject: [PATCH 24/28] Chunnel: Double cost of building and removing chunnels. --- src/tunnelbridge_cmd.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 30df9ce9ef..ada7d362ed 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -804,6 +804,8 @@ 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; @@ -919,6 +921,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) { @@ -974,7 +978,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags) 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)); } From ec9f0371e8d47a3286d2ade3f35a629279c66128 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Mar 2017 18:05:04 +0000 Subject: [PATCH 25/28] Chunnel: Adjust z position of vehicles in chunnels to go "under" the water. --- src/ground_vehicle.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++ src/ground_vehicle.hpp | 8 ++++-- src/roadveh_cmd.cpp | 3 +- src/train_cmd.cpp | 12 +++++++- 4 files changed, 81 insertions(+), 4 deletions(-) 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 67676fb919..5585fe7760 100644 --- a/src/ground_vehicle.hpp +++ b/src/ground_vehicle.hpp @@ -228,17 +228,21 @@ struct GroundVehicle : public SpecializedVehicle { 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) { + this->UpdateZPositionInWormhole(); + } else if (new_tile) { this->UpdateZPositionAndInclination(); } else { this->UpdateZPosition(); 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/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 */ From 4c9f7b73b3466cd5bfa4b3e1a8f222ea9da48310 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Mar 2017 18:37:35 +0000 Subject: [PATCH 26/28] Chunnel: Add a ground veh flag for (maybe) being in a chunnel Use this to avoid inclination change code in non-chunnel wormholes. --- src/ground_vehicle.hpp | 8 +++++++- src/tunnelbridge_cmd.cpp | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp index 5585fe7760..367c9c3e39 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) }; /** @@ -225,6 +227,10 @@ struct GroundVehicle : public SpecializedVehicle { 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)); } @@ -241,7 +247,7 @@ struct GroundVehicle : public SpecializedVehicle { int old_z = this->z_pos; if (in_wormhole) { - this->UpdateZPositionInWormhole(); + if (HasBit(this->gv_flags, GVF_CHUNNEL_BIT)) this->UpdateZPositionInWormhole(); } else if (new_tile) { this->UpdateZPositionAndInclination(); } else { diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index ada7d362ed..f9a91535b5 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -2108,6 +2108,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; } @@ -2133,6 +2134,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 { From 93a43da80999a227d6cbc70f716b8d6f13cdcf84 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Mar 2017 19:13:42 +0000 Subject: [PATCH 27/28] Chunnel: Prevent chunnels from intersecting with each other. --- src/terraform_cmd.cpp | 2 +- src/tunnel_map.cpp | 11 +++++++---- src/tunnel_map.h | 11 ++++++++++- src/tunnelbridge_cmd.cpp | 22 +++++++++++++++++++++- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp index 71a171c665..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, true)) { + 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/tunnel_map.cpp b/src/tunnel_map.cpp index 7b546848a4..b679bc0e00 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -103,14 +103,17 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) return t->tile_n == tile ? t->tile_s : t->tile_n; } -static inline bool IsTunnelInWaySingleAxis(TileIndex tile, int z, bool chunnel_allowed, bool y_axis, TileIndexDiff tile_diff) +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; - if (t->is_chunnel && chunnel_allowed) { + 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; @@ -128,7 +131,7 @@ static inline bool IsTunnelInWaySingleAxis(TileIndex tile, int z, bool chunnel_a * @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 chunnel_allowed) +bool IsTunnelInWay(TileIndex tile, int z, IsTunnelInWayFlags flags) { - return IsTunnelInWaySingleAxis(tile, z, chunnel_allowed, false, 1) || IsTunnelInWaySingleAxis(tile, z, chunnel_allowed, true, TileOffsByDiagDir(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 41102cb27e..875775eff4 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -56,7 +56,16 @@ static inline TunnelID GetTunnelIndex(TileIndex t) } TileIndex GetOtherTunnelEnd(TileIndex); -bool IsTunnelInWay(TileIndex, int z, bool chunnel_allowed = false); + +/** 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. diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index f9a91535b5..f86a3e84a3 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -740,7 +740,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, head_tiles = 0; found_tunnel_tile = INVALID_TILE; } - if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z, true)) { + 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; @@ -749,6 +749,26 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, 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) { From de24fed9d11c95dc15ec72a5f635cf260b626303 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Sun, 12 Mar 2017 20:33:50 +0000 Subject: [PATCH 28/28] Chunnel: Tunnels are only allowed to cross sea. --- src/lang/english.txt | 1 + src/tunnelbridge_cmd.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 31a87d6262..1e781370e0 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5012,6 +5012,7 @@ STR_ERROR_TUNNEL_RAMP_TOO_SHORT :{WHITE}... ramp 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/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index f86a3e84a3..0ed3a53db0 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -734,9 +734,10 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, end_tile += delta; tiles++; + if (IsTileType(end_tile, MP_WATER) && IsSea(end_tile)) is_chunnel = true; } /* The water was passed */ - is_chunnel = true; + if (!is_chunnel) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA); head_tiles = 0; found_tunnel_tile = INVALID_TILE; }