diff --git a/Makefile b/Makefile index acbaf2955c..1d8aaf25d2 100644 --- a/Makefile +++ b/Makefile @@ -669,6 +669,7 @@ SRCS += town_gui.c SRCS += train_cmd.c SRCS += train_gui.c SRCS += tree_cmd.c +SRCS += tunnel_map.c SRCS += tunnelbridge_cmd.c SRCS += unmovable_cmd.c SRCS += vehicle.c diff --git a/clear_cmd.c b/clear_cmd.c index c3b0cb47ca..1cf3505540 100644 --- a/clear_cmd.c +++ b/clear_cmd.c @@ -9,6 +9,7 @@ #include "tile.h" #include "viewport.h" #include "command.h" +#include "tunnel_map.h" #include "variables.h" #include "table/sprites.h" @@ -86,23 +87,59 @@ static void TerraformAddDirtyTileAround(TerraformerState *ts, TileIndex tile) static int TerraformProc(TerraformerState *ts, TileIndex tile, int mode) { int r; + int32 ret; assert(tile < MapSize()); - if ((r=TerraformAllowTileProcess(ts, tile)) <= 0) - return r; + r = TerraformAllowTileProcess(ts, tile); + if (r <= 0) return r; - if (!IsTileType(tile, MP_RAILWAY)) { - int32 ret = DoCommandByTile(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR); + if (IsTileType(tile, MP_RAILWAY)) { + static const TrackBits safe_track[] = { TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER, TRACK_BIT_RIGHT }; + static const Slope unsafe_slope[] = { SLOPE_S, SLOPE_W, SLOPE_N, SLOPE_E }; - if (CmdFailed(ret)) { + Slope tileh; + uint z; + + // Nothing could be built at the steep slope - this avoids a bug + // when you have a single diagonal track in one corner on a + // basement and then you raise/lower the other corner. + tileh = GetTileSlope(tile, &z); + if (tileh == unsafe_slope[mode] || + tileh == ComplementSlope(unsafe_slope[mode])) { _terraform_err_tile = tile; + _error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK; return -1; } - ts->cost += ret; + // If we have a single diagonal track there, the other side of + // tile can be terraformed. + if (IsPlainRailTile(tile) && GetTrackBits(tile) == safe_track[mode]) { + /* If terraforming downwards prevent damaging a potential tunnel below. + * This check is only necessary for flat tiles, because if the tile is + * non-flat, then the corner opposing the rail is raised. Only this corner + * can be lowered and this is a safe action + */ + if (tileh == SLOPE_FLAT && + ts->direction == -1 && + IsTunnelInWay(tile, z - TILE_HEIGHT)) { + _terraform_err_tile = tile; + _error_message = STR_1002_EXCAVATION_WOULD_DAMAGE; + return -1; + } + return 0; + } } + ret = DoCommandByTile(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR); + + if (ret == CMD_ERROR) { + _terraform_err_tile = tile; + return -1; + } + + ts->cost += ret; + if (ts->tile_table_count >= 625) return -1; ts->tile_table[ts->tile_table_count++] = tile; @@ -233,35 +270,26 @@ int32 CmdTerraformLand(int x, int y, uint32 flags, uint32 p1, uint32 p2) return CMD_ERROR; } - { /* Check if tunnel or track would take damage */ + if (direction == -1) { + /* Check if tunnel would take damage */ int count; TileIndex *ti = ts.tile_table; for (count = ts.tile_table_count; count != 0; count--, ti++) { - uint a, b, c, d, r, min; + uint z, t; TileIndex tile = *ti; - _terraform_err_tile = tile; + z = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0)); + t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0)); + if (t <= z) z = t; + t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1)); + if (t <= z) z = t; + t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1)); + if (t <= z) z = t; - a = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0)); - b = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0)); - c = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1)); - d = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1)); - - r = GetTileh(a, b, c, d, &min); - - if (IsTileType(tile, MP_RAILWAY)) { - if (IsSteepTileh(r)) return_cmd_error(STR_1008_MUST_REMOVE_RAILROAD_TRACK); - - if (IsPlainRailTile(tile)) { - extern const TrackBits _valid_tileh_slopes[2][15]; - if (GetTrackBits(tile) & ~_valid_tileh_slopes[0][r]) return_cmd_error(STR_1008_MUST_REMOVE_RAILROAD_TRACK); - } else return_cmd_error(STR_5800_OBJECT_IN_THE_WAY); + if (IsTunnelInWay(tile, z * TILE_HEIGHT)) { + return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE); } - - if (direction == -1 && !CheckTunnelInWay(tile, min)) return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE); - - _terraform_err_tile = 0; } } diff --git a/openttd.dsp b/openttd.dsp index e030677486..7b4189d66e 100644 --- a/openttd.dsp +++ b/openttd.dsp @@ -922,6 +922,10 @@ SOURCE=.\tree_cmd.c # End Source File # Begin Source File +SOURCE=.\tunnel_map.c +# End Source File +# Begin Source File + SOURCE=.\tunnelbridge_cmd.c # End Source File # Begin Source File diff --git a/openttd.vcproj b/openttd.vcproj index 6f030d431b..ec419cc1fc 100644 --- a/openttd.vcproj +++ b/openttd.vcproj @@ -724,6 +724,9 @@ + + diff --git a/rail_cmd.c b/rail_cmd.c index ab297039ee..06b4e4badb 100644 --- a/rail_cmd.c +++ b/rail_cmd.c @@ -130,7 +130,7 @@ static bool CheckTrackCombination(TileIndex tile, TrackBits to_build, uint flags } -const TrackBits _valid_tileh_slopes[4][15] = { +static const TrackBits _valid_tileh_slopes[][15] = { // set of normal ones { diff --git a/slope.h b/slope.h index 79678780ba..8ed509e2bd 100644 --- a/slope.h +++ b/slope.h @@ -27,4 +27,15 @@ typedef enum Slope { SLOPE_STEEP_N = SLOPE_STEEP | SLOPE_ENW } Slope; +static inline bool IsSteepSlope(Slope s) +{ + return (s & SLOPE_STEEP) != 0; +} + +static inline Slope ComplementSlope(Slope s) +{ + assert(!IsSteepSlope(s)); + return (Slope)(0xF ^ s); +} + #endif diff --git a/tile.c b/tile.c index 5d5056a95d..448ff78a0b 100644 --- a/tile.c +++ b/tile.c @@ -15,37 +15,14 @@ uint GetMapExtraBits(TileIndex tile) return GB(_m[tile].extra, 0, 2); } -/** Converts the heights of 4 corners into a tileh, and returns the minimum height of the tile - * @param n,w,e,s the four corners - * @param h uint pointer to write the height to - * @return the tileh -*/ -uint GetTileh(uint n, uint w, uint e, uint s, uint *h) -{ - uint min = n; - uint r; - - if (min >= w) min = w; - if (min >= e) min = e; - if (min >= s) min = s; - - r = 0; - if ((n -= min) != 0) r += (--n << 4) + 8; - if ((e -= min) != 0) r += (--e << 4) + 4; - if ((s -= min) != 0) r += (--s << 4) + 2; - if ((w -= min) != 0) r += (--w << 4) + 1; - - if (h != NULL) *h = min * 8; - - return r; -} - uint GetTileSlope(TileIndex tile, uint *h) { uint a; uint b; uint c; uint d; + uint min; + uint r; assert(tile < MapSize()); @@ -54,12 +31,23 @@ uint GetTileSlope(TileIndex tile, uint *h) return 0; } - a = TileHeight(tile); + min = a = TileHeight(tile); b = TileHeight(tile + TileDiffXY(1, 0)); + if (min >= b) min = b; c = TileHeight(tile + TileDiffXY(0, 1)); + if (min >= c) min = c; d = TileHeight(tile + TileDiffXY(1, 1)); + if (min >= d) min = d; - return GetTileh(a, b, c, d, h); + r = SLOPE_FLAT; + if ((a -= min) != 0) r += (--a << 4) + SLOPE_N; + if ((c -= min) != 0) r += (--c << 4) + SLOPE_E; + if ((d -= min) != 0) r += (--d << 4) + SLOPE_S; + if ((b -= min) != 0) r += (--b << 4) + SLOPE_W; + + if (h != NULL) *h = min * TILE_HEIGHT; + + return r; } uint GetTileZ(TileIndex tile) diff --git a/tile.h b/tile.h index dd96390720..267861635d 100644 --- a/tile.h +++ b/tile.h @@ -48,7 +48,6 @@ typedef enum DiagonalDirections { void SetMapExtraBits(TileIndex tile, byte flags); uint GetMapExtraBits(TileIndex tile); -uint GetTileh(uint n, uint w, uint e, uint s, uint *h); uint GetTileSlope(TileIndex tile, uint *h); uint GetTileZ(TileIndex tile); uint GetTileMaxZ(TileIndex tile); diff --git a/tunnel_map.c b/tunnel_map.c new file mode 100644 index 0000000000..b3975f37f1 --- /dev/null +++ b/tunnel_map.c @@ -0,0 +1,31 @@ +/* $Id$ */ + +#include "stdafx.h" +#include "openttd.h" +#include "tile.h" +#include "tunnel_map.h" + +static bool IsTunnelInWayDir(TileIndex tile, uint z, DiagDirection dir) +{ + TileIndexDiff delta = TileOffsByDir(dir); + uint height; + + do { + tile -= delta; + height = GetTileZ(tile); + } while (z < height); + + return + z == height && + IsTunnelTile(tile) && + GetTunnelDirection(tile) == dir; +} + +bool IsTunnelInWay(TileIndex tile, uint z) +{ + return + IsTunnelInWayDir(tile, z, DIAGDIR_NE) || + IsTunnelInWayDir(tile, z, DIAGDIR_SE) || + IsTunnelInWayDir(tile, z, DIAGDIR_SW) || + IsTunnelInWayDir(tile, z, DIAGDIR_NW); +} diff --git a/tunnel_map.h b/tunnel_map.h index 2a7c6e4310..4afd8ef919 100644 --- a/tunnel_map.h +++ b/tunnel_map.h @@ -20,4 +20,7 @@ static inline uint GetTunnelDirection(TileIndex t) return (uint)GB(_m[t].m5, 0, 2); } + +bool IsTunnelInWay(TileIndex, uint z); + #endif