From 2549def764960439b83c4eb5aa1c55a55f20e781 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Fri, 24 Mar 2017 22:25:59 +0000 Subject: [PATCH 1/4] Chunnel: Use separate container function for chunnel related conditions. --- src/lang/english.txt | 3 +- src/tunnelbridge_cmd.cpp | 196 +++++++++++++++++++++++---------------- 2 files changed, 117 insertions(+), 82 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 1e781370e0..5b326ea157 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5006,9 +5006,10 @@ STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL :{WHITE}Site uns STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST :{WHITE}Must demolish tunnel first STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY :{WHITE}Another tunnel in the way STR_ERROR_TUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel would end out of the map +STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel under water would end out of the map STR_ERROR_UNABLE_TO_EXCAVATE_LAND :{WHITE}Unable to excavate land for other end of tunnel STR_ERROR_TUNNEL_TOO_LONG :{WHITE}... tunnel too long -STR_ERROR_TUNNEL_RAMP_TOO_SHORT :{WHITE}... ramp too short, tunnels under water must have a ramp at least three tiles long at both ends. +STR_ERROR_CHUNNEL_RAMP :{WHITE}... only ramp length between 4 and {NUM} tiles is allowed for tunnels under water. 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/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 0ed3a53db0..db93cff6a9 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -602,6 +602,103 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u return cost; } +/** + * Check if the amount of tiles of the chunnel ramp is between allowed limits. + * @param tile the actual tile. + * @param ramp ramp_start tile. + * @param delta the tile offset. + * @return an empty string if between limits or a formatted string for the error message. + */ +static inline StringID IsRampBetweenLimits(TileIndex ramp_start, TileIndex tile, TileIndexDiff delta) +{ + uint min_length = 4; + uint max_length = 7; + if (Delta(ramp_start, tile) < (uint)abs(delta) * min_length || Delta(ramp_start, tile - delta) > (uint)abs(delta) * max_length) { + SetDParam(0, max_length); + return (STR_ERROR_CHUNNEL_RAMP); + } + + return STR_NULL; +} + +/** + * See if chunnel building is possible. + * All chunnel related issues are tucked away in one procedure + * @pre only on z level 0. + * @param tile start tile of tunnel. + * @param direction the direction we want to build. + * @param is_chunnel pointer to set if chunnel is allowed or not. + * @param sea_tiles pointer for the amount of tiles used to cross a sea. + * @return an error message or if success the is_chunnel flag is set to true and the amount of tiles needed to cross the water is returned. + */ +static inline CommandCost IsChunnel(TileIndex tile, DiagDirection direction , bool &is_chunnel, int &sea_tiles) +{ + int start_z = 0; + int end_z; + bool crossed_sea = false; + TileIndex ramp_start = tile; + StringID err_msg; + + if(GetTileZ(tile) > 0) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA); + + TileIndexDiff delta = TileOffsByDiagDir(direction); + Slope end_tileh; + for (;;) { + tile += delta; + if (!IsValidTile(tile)) return_cmd_error(STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER); + end_tileh = GetTileSlope(tile, &end_z); + + if (start_z == end_z) { + _build_tunnel_endtile = tile; + + /* Check if end ramp was too short or too long after crossing the sea. */ + err_msg = IsRampBetweenLimits(ramp_start, tile, delta); + if(crossed_sea && err_msg > STR_NULL) return_cmd_error(err_msg); + + /* Handle chunnels only on sea level and only one time crossing. */ + if (!crossed_sea && + (IsCoastTile(tile) || + (IsValidTile(tile + delta) && HasTileWaterGround(tile + delta)) || + (IsValidTile(tile + delta * 2) && HasTileWaterGround(tile + delta * 2)))) { + + /* A shore was found, check if start ramp was too short or too long. */ + err_msg = IsRampBetweenLimits(ramp_start, tile, delta); + if(err_msg > STR_NULL) return_cmd_error(err_msg); + + /* Pass the water and find a proper shore tile that potentially + * could have a tunnel portal behind. */ + for (;;) { + if (!IsValidTile(tile)) return_cmd_error(STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER); + _build_tunnel_endtile = tile; + + end_tileh = GetTileSlope(tile); + if(direction == DIAGDIR_NE && (end_tileh & SLOPE_NE) == SLOPE_NE) break; + if(direction == DIAGDIR_SE && (end_tileh & SLOPE_SE) == SLOPE_SE) break; + if(direction == DIAGDIR_SW && (end_tileh & SLOPE_SW) == SLOPE_SW) break; + if(direction == DIAGDIR_NW && (end_tileh & SLOPE_NW) == SLOPE_NW) break; + + /* No drilling under oil rigs.*/ + if ((IsTileType(tile, MP_STATION) && IsOilRig(tile)) || + (IsTileType(tile, MP_INDUSTRY) && + GetIndustryGfx(tile) >= GFX_OILRIG_1 && + GetIndustryGfx(tile) <= GFX_OILRIG_5)) return_cmd_error(STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL); + + if (IsTileType(tile, MP_WATER) && IsSea(tile)) crossed_sea = true; + if (!_cheats.crossing_tunnels.value && IsTunnelInWay(tile, start_z)) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + tile += delta; + sea_tiles++; + } + if (!crossed_sea) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA); + ramp_start = tile; + } else { + break; + } + } + } + is_chunnel = crossed_sea; + + return CommandCost(); +} /** * Build Tunnel. @@ -672,16 +769,19 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, int tiles_coef = 3; /* Number of tiles from start of tunnel */ int tiles = 0; - /* flag for chunnels. */ - bool is_chunnel = false; - /* Number of chunnel head tiles. */ - int head_tiles = 0; /* Number of tiles at which the cost increase coefficient per tile is halved */ int tiles_bump = 25; + /* flags for chunnels. */ + bool is_chunnel = false; + bool crossed_sea = false; + /* Number of tiles counted for crossing sea */ + int sea_tiles = 0; - TileIndex found_tunnel_tile = INVALID_TILE; + if(start_z == 0 && _settings_game.construction.chunnel) { + CommandCost chunnel_test = IsChunnel(start_tile, direction, is_chunnel, sea_tiles); + if (chunnel_test.Failed()) return chunnel_test; + } - CommandCost cost(EXPENSES_CONSTRUCTION); Slope end_tileh; for (;;) { end_tile += delta; @@ -689,88 +789,22 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, end_tileh = GetTileSlope(end_tile, &end_z); if (start_z == end_z) { - _build_tunnel_endtile = found_tunnel_tile != INVALID_TILE ? found_tunnel_tile : end_tile; - - /* Test if we are on a shore. */ - if (end_z == 0 && _settings_game.construction.chunnel && - (IsCoastTile(end_tile) || - (IsValidTile(end_tile + delta) && HasTileWaterGround(end_tile + delta)) || - (IsValidTile(end_tile + delta * 2) && HasTileWaterGround(end_tile + delta * 2)))) { - if (!is_chunnel) { - /*We are about to pass water for the first time so check if not to close to other tunnel */ - if (tiles + 1 < head_tiles + 4 && found_tunnel_tile != INVALID_TILE) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY_FOR_CHUNNEL); - if (tiles + 1 < 4) return_cmd_error(STR_ERROR_TUNNEL_RAMP_TOO_SHORT); - } - } else {/* We are leaving.*/ - if (is_chunnel) { - /* Check if there is enough ramp space to come up. */ - if (head_tiles < 4 && found_tunnel_tile != INVALID_TILE) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY_FOR_CHUNNEL); - if (head_tiles < 4) return_cmd_error(STR_ERROR_TUNNEL_RAMP_TOO_SHORT); - } else { - if (found_tunnel_tile != INVALID_TILE) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); - } + if (is_chunnel && !crossed_sea){ + end_tile += sea_tiles * delta; + tiles += sea_tiles; + crossed_sea = true; + } else { break; } - - /* A shore was found so pass the water and find a proper shore tile that potentially - * could have a tunnel portal behind. */ - for (;;) { - if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER); - - end_tileh = GetTileSlope(end_tile); - if(direction == DIAGDIR_NE && (end_tileh & SLOPE_NE) == SLOPE_NE) break; - if(direction == DIAGDIR_SE && (end_tileh & SLOPE_SE) == SLOPE_SE) break; - if(direction == DIAGDIR_SW && (end_tileh & SLOPE_SW) == SLOPE_SW) break; - if(direction == DIAGDIR_NW && (end_tileh & SLOPE_NW) == SLOPE_NW) break; - - /* No drilling under oil rigs.*/ - if ((IsTileType(end_tile, MP_STATION) && IsOilRig(end_tile)) || - (IsTileType(end_tile, MP_INDUSTRY) && - GetIndustryGfx(end_tile) >= GFX_OILRIG_1 && - GetIndustryGfx(end_tile) <= GFX_OILRIG_5)) { - _build_tunnel_endtile = end_tile; - return_cmd_error(STR_ERROR_NO_DRILLING_ABOVE_CHUNNEL); - } - - end_tile += delta; - tiles++; - if (IsTileType(end_tile, MP_WATER) && IsSea(end_tile)) is_chunnel = true; - } - /* The water was passed */ - if (!is_chunnel) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA); - head_tiles = 0; - found_tunnel_tile = INVALID_TILE; } - if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z, ITIWF_IGNORE_CHUNNEL)) { - if (found_tunnel_tile == INVALID_TILE || is_chunnel) { // Remember the first or the last when we pass a tunnel. - found_tunnel_tile = end_tile; - head_tiles = 0; - } + if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z)) { + _build_tunnel_endtile = end_tile; + return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); } - 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. */ + CommandCost cost(EXPENSES_CONSTRUCTION); for (int i = tiles; i > 0; i--) { if (tiles == tiles_bump) { tiles_coef++; From 612c825bb6c97ea366cbd06e8027f2acba8242b3 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 24 Mar 2017 22:51:29 +0000 Subject: [PATCH 2/4] Chunnel: Minor whitespace/scope changes, rename function. --- src/tunnelbridge_cmd.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index db93cff6a9..cac04d08ff 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -615,7 +615,7 @@ static inline StringID IsRampBetweenLimits(TileIndex ramp_start, TileIndex tile, uint max_length = 7; if (Delta(ramp_start, tile) < (uint)abs(delta) * min_length || Delta(ramp_start, tile - delta) > (uint)abs(delta) * max_length) { SetDParam(0, max_length); - return (STR_ERROR_CHUNNEL_RAMP); + return STR_ERROR_CHUNNEL_RAMP; } return STR_NULL; @@ -631,29 +631,29 @@ static inline StringID IsRampBetweenLimits(TileIndex ramp_start, TileIndex tile, * @param sea_tiles pointer for the amount of tiles used to cross a sea. * @return an error message or if success the is_chunnel flag is set to true and the amount of tiles needed to cross the water is returned. */ -static inline CommandCost IsChunnel(TileIndex tile, DiagDirection direction , bool &is_chunnel, int &sea_tiles) +static inline CommandCost CanBuildChunnel(TileIndex tile, DiagDirection direction, bool &is_chunnel, int &sea_tiles) { - int start_z = 0; - int end_z; + const int start_z = 0; bool crossed_sea = false; TileIndex ramp_start = tile; - StringID err_msg; - if(GetTileZ(tile) > 0) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA); + if (GetTileZ(tile) > 0) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA); - TileIndexDiff delta = TileOffsByDiagDir(direction); - Slope end_tileh; + const TileIndexDiff delta = TileOffsByDiagDir(direction); for (;;) { tile += delta; if (!IsValidTile(tile)) return_cmd_error(STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER); - end_tileh = GetTileSlope(tile, &end_z); + int end_z; + Slope end_tileh = GetTileSlope(tile, &end_z); if (start_z == end_z) { _build_tunnel_endtile = tile; /* Check if end ramp was too short or too long after crossing the sea. */ - err_msg = IsRampBetweenLimits(ramp_start, tile, delta); - if(crossed_sea && err_msg > STR_NULL) return_cmd_error(err_msg); + if (crossed_sea) { + StringID err_msg = IsRampBetweenLimits(ramp_start, tile, delta); + if (err_msg > STR_NULL) return_cmd_error(err_msg); + } /* Handle chunnels only on sea level and only one time crossing. */ if (!crossed_sea && @@ -662,8 +662,8 @@ static inline CommandCost IsChunnel(TileIndex tile, DiagDirection direction , bo (IsValidTile(tile + delta * 2) && HasTileWaterGround(tile + delta * 2)))) { /* A shore was found, check if start ramp was too short or too long. */ - err_msg = IsRampBetweenLimits(ramp_start, tile, delta); - if(err_msg > STR_NULL) return_cmd_error(err_msg); + StringID err_msg = IsRampBetweenLimits(ramp_start, tile, delta); + if (err_msg > STR_NULL) return_cmd_error(err_msg); /* Pass the water and find a proper shore tile that potentially * could have a tunnel portal behind. */ @@ -672,10 +672,10 @@ static inline CommandCost IsChunnel(TileIndex tile, DiagDirection direction , bo _build_tunnel_endtile = tile; end_tileh = GetTileSlope(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; + if (direction == DIAGDIR_NE && (end_tileh & SLOPE_NE) == SLOPE_NE) break; + if (direction == DIAGDIR_SE && (end_tileh & SLOPE_SE) == SLOPE_SE) break; + if (direction == DIAGDIR_SW && (end_tileh & SLOPE_SW) == SLOPE_SW) break; + if (direction == DIAGDIR_NW && (end_tileh & SLOPE_NW) == SLOPE_NW) break; /* No drilling under oil rigs.*/ if ((IsTileType(tile, MP_STATION) && IsOilRig(tile)) || @@ -777,8 +777,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, /* Number of tiles counted for crossing sea */ int sea_tiles = 0; - if(start_z == 0 && _settings_game.construction.chunnel) { - CommandCost chunnel_test = IsChunnel(start_tile, direction, is_chunnel, sea_tiles); + if (start_z == 0 && _settings_game.construction.chunnel) { + CommandCost chunnel_test = CanBuildChunnel(start_tile, direction, is_chunnel, sea_tiles); if (chunnel_test.Failed()) return chunnel_test; } From c8820824672e18e2edec9f54b5c5cce09d03911f Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 27 Mar 2017 18:34:55 +0100 Subject: [PATCH 3/4] Chunnel: Fix swap of vehicle chunnel flag when reversing consist. --- src/train_cmd.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 679efbb188..dd5f70bf2f 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1635,8 +1635,10 @@ static void SwapTrainFlags(uint16 *swap_flag1, uint16 *swap_flag2) /* Clear the flags */ ClrBit(*swap_flag1, GVF_GOINGUP_BIT); ClrBit(*swap_flag1, GVF_GOINGDOWN_BIT); + ClrBit(*swap_flag1, GVF_CHUNNEL_BIT); ClrBit(*swap_flag2, GVF_GOINGUP_BIT); ClrBit(*swap_flag2, GVF_GOINGDOWN_BIT); + ClrBit(*swap_flag2, GVF_CHUNNEL_BIT); /* Reverse the rail-flags (if needed) */ if (HasBit(flag1, GVF_GOINGUP_BIT)) { @@ -1649,6 +1651,12 @@ static void SwapTrainFlags(uint16 *swap_flag1, uint16 *swap_flag2) } else if (HasBit(flag2, GVF_GOINGDOWN_BIT)) { SetBit(*swap_flag1, GVF_GOINGUP_BIT); } + if (HasBit(flag1, GVF_CHUNNEL_BIT)) { + SetBit(*swap_flag2, GVF_CHUNNEL_BIT); + } + if (HasBit(flag2, GVF_CHUNNEL_BIT)) { + SetBit(*swap_flag1, GVF_CHUNNEL_BIT); + } } /** From eddd635f00bcc546014d30fe103e81996712a583 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Fri, 31 Mar 2017 01:54:33 +0100 Subject: [PATCH 4/4] Chunnel: Make error reporting consistent with tunnel length reporting at cursor. Error reporting should be in tune with occurrence of events. --- src/lang/english.txt | 2 +- src/tunnelbridge_cmd.cpp | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 5b326ea157..0c6a2c598a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5009,7 +5009,7 @@ STR_ERROR_TUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel w STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel under water 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_CHUNNEL_RAMP :{WHITE}... only ramp length between 4 and {NUM} tiles is allowed for tunnels under water. +STR_ERROR_CHUNNEL_RAMP :{WHITE}... only ramp length between 5 and {NUM} tiles is allowed for tunnels under water. 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/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index cac04d08ff..15e02d17d3 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -613,8 +613,9 @@ static inline StringID IsRampBetweenLimits(TileIndex ramp_start, TileIndex tile, { uint min_length = 4; uint max_length = 7; - if (Delta(ramp_start, tile) < (uint)abs(delta) * min_length || Delta(ramp_start, tile - delta) > (uint)abs(delta) * max_length) { - SetDParam(0, max_length); + if (Delta(ramp_start, tile) < (uint)abs(delta) * min_length || (uint)abs(delta) * max_length < Delta(ramp_start, tile)) { + /* Add 1 in message to have consistency with cursor count in game. */ + SetDParam(0, max_length + 1); return STR_ERROR_CHUNNEL_RAMP; } @@ -643,17 +644,11 @@ static inline CommandCost CanBuildChunnel(TileIndex tile, DiagDirection directio for (;;) { tile += delta; if (!IsValidTile(tile)) return_cmd_error(STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER); + _build_tunnel_endtile = tile; int end_z; Slope end_tileh = GetTileSlope(tile, &end_z); if (start_z == end_z) { - _build_tunnel_endtile = tile; - - /* Check if end ramp was too short or too long after crossing the sea. */ - if (crossed_sea) { - StringID err_msg = IsRampBetweenLimits(ramp_start, tile, delta); - if (err_msg > STR_NULL) return_cmd_error(err_msg); - } /* Handle chunnels only on sea level and only one time crossing. */ if (!crossed_sea && @@ -668,9 +663,6 @@ static inline CommandCost CanBuildChunnel(TileIndex tile, DiagDirection directio /* Pass the water and find a proper shore tile that potentially * could have a tunnel portal behind. */ for (;;) { - if (!IsValidTile(tile)) return_cmd_error(STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER); - _build_tunnel_endtile = tile; - end_tileh = GetTileSlope(tile); if (direction == DIAGDIR_NE && (end_tileh & SLOPE_NE) == SLOPE_NE) break; if (direction == DIAGDIR_SE && (end_tileh & SLOPE_SE) == SLOPE_SE) break; @@ -685,15 +677,25 @@ static inline CommandCost CanBuildChunnel(TileIndex tile, DiagDirection directio if (IsTileType(tile, MP_WATER) && IsSea(tile)) crossed_sea = true; if (!_cheats.crossing_tunnels.value && IsTunnelInWay(tile, start_z)) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + tile += delta; + if (!IsValidTile(tile)) return_cmd_error(STR_ERROR_CHUNNEL_THROUGH_MAP_BORDER); + _build_tunnel_endtile = tile; sea_tiles++; } if (!crossed_sea) return_cmd_error(STR_ERROR_CHUNNEL_ONLY_OVER_SEA); ramp_start = tile; } else { + /* Check if end ramp was too short or too long after crossing the sea. */ + if (crossed_sea) { + StringID err_msg = IsRampBetweenLimits(ramp_start, tile, delta); + if (err_msg > STR_NULL) return_cmd_error(err_msg); + } + break; } } + if (!_cheats.crossing_tunnels.value && IsTunnelInWay(tile, start_z)) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); } is_chunnel = crossed_sea;