diff options
author | Tyler Trahan <tyler@tylertrahan.com> | 2021-01-08 12:32:44 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-08 18:32:44 +0100 |
commit | 9c0da686da260962c68a6a0ec58fe10cbf4dc421 (patch) | |
tree | 1346564a3959b803cc1a039a65138ceb87cb6137 | |
parent | fec5ce093f1f20b71f3b9a2d9008bd95065b0399 (diff) | |
download | openttd-9c0da686da260962c68a6a0ec58fe10cbf4dc421.tar.xz |
Add: Towns can build tunnels (#8473)
-rw-r--r-- | src/town_cmd.cpp | 104 |
1 files changed, 87 insertions, 17 deletions
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 6bd78ff0d..3ea1ce436 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1109,44 +1109,44 @@ static bool GrowTownWithRoad(const Town *t, TileIndex tile, RoadBits rcmd) } /** - * Checks if a town road can be continued on the other side of a bridge. + * Checks if a town road can be continued into the next tile. + * Road vehicle stations, bridges, and tunnels are fine, as long as they are facing the right direction. * - * @param end_tile The end tile of the bridge - * @param bridge_dir The direction of the bridge + * @param tile The tile where the road would be built + * @param road_dir The direction of the road * @return true if the road can be continued, else false */ -static bool CanRoadContinueAfterBridge(const Town* t, const TileIndex end_tile, const DiagDirection bridge_dir) +static bool CanRoadContinueIntoNextTile(const Town* t, const TileIndex tile, const DiagDirection road_dir) { - const int delta = TileOffsByDiagDir(bridge_dir); // +1 tile in the direction of the bridge - TileIndex next_tile = end_tile + delta; // The tile beyond the bridge - RoadBits rcmd = DiagDirToRoadBits(ReverseDiagDir(bridge_dir)); + const int delta = TileOffsByDiagDir(road_dir); // +1 tile in the direction of the road + TileIndex next_tile = tile + delta; // The tile beyond which must be connectable to the target tile + RoadBits rcmd = DiagDirToRoadBits(ReverseDiagDir(road_dir)); RoadType rt = GetTownRoadType(t); /* Before we try anything, make sure the tile is on the map and not the void. */ if (!IsValidTile(next_tile)) return false; - /* If the next tile is a bridge or tunnel, allow if it's a road bridge/tunnel continuing in the same direction. */ + /* If the next tile is a bridge or tunnel, allow if it's continuing in the same direction. */ if (IsTileType(next_tile, MP_TUNNELBRIDGE)) { - return GetTunnelBridgeTransportType(next_tile) == TRANSPORT_ROAD && GetTunnelBridgeDirection(next_tile) == bridge_dir; + return GetTunnelBridgeTransportType(next_tile) == TRANSPORT_ROAD && GetTunnelBridgeDirection(next_tile) == road_dir; } /* If the next tile is a station, allow if it's a road station facing the proper direction. Otherwise return false. */ if (IsTileType(next_tile, MP_STATION)) { /* If the next tile is a road station, allow if it's facing the same direction, otherwise disallow. */ - return IsRoadStop(next_tile) && GetRoadStopDir(next_tile) == ReverseDiagDir(bridge_dir); + return IsRoadStop(next_tile) && GetRoadStopDir(next_tile) == ReverseDiagDir(road_dir); } - /* If the next tile is a road depot, allow if it's facing the new bridge. */ + /* If the next tile is a road depot, allow if it's facing the right way. */ if (IsTileType(next_tile, MP_ROAD)) { - return IsRoadDepot(next_tile) && GetRoadDepotDirection(next_tile) == ReverseDiagDir(bridge_dir); + return IsRoadDepot(next_tile) && GetRoadDepotDirection(next_tile) == ReverseDiagDir(road_dir); } /* If the next tile is a railroad track, check if towns are allowed to build level crossings. - * If level crossing are not allowed, reject the bridge. Else allow DoCommand to determine if the rail track is buildable. */ + * If level crossing are not allowed, reject the construction. Else allow DoCommand to determine if the rail track is buildable. */ if (IsTileType(next_tile, MP_RAILWAY) && !_settings_game.economy.allow_town_level_crossings) return false; - /* If a road tile can be built, the bridge is allowed. - * If not, the bridge is rejected. */ + /* If a road tile can be built, the construction is allowed. */ return DoCommand(next_tile, rcmd | (rt << 4), t->index, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded(); } @@ -1208,7 +1208,7 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi if (bridge_length == 1) return false; /* Make sure the road can be continued past the bridge. At this point, bridge_tile holds the end tile of the bridge. */ - if (!CanRoadContinueAfterBridge(t, bridge_tile, bridge_dir)) return false; + if (!CanRoadContinueIntoNextTile(t, bridge_tile, bridge_dir)) return false; for (uint8 times = 0; times <= 22; times++) { byte bridge_type = RandomRange(MAX_BRIDGES - 1); @@ -1226,6 +1226,75 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi } /** + * Grows the town with a tunnel. + * First we check if a tunnel is reasonable. + * If so we check if we are able to build it. + * + * @param t The current town + * @param tile The current tile + * @param tunnel_dir The valid direction in which to grow a tunnel + * @return true if a tunnel has been built, else false + */ +static bool GrowTownWithTunnel(const Town* t, const TileIndex tile, const DiagDirection tunnel_dir) +{ + assert(tunnel_dir < DIAGDIR_END); + + Slope slope = GetTileSlope(tile); + + /* Only consider building a tunnel if the starting tile is sloped properly. */ + if (slope != InclinedSlope(tunnel_dir)) return false; + + /* Assure that the tunnel is connectable to the start side */ + if (!(GetTownRoadBits(TileAddByDiagDir(tile, ReverseDiagDir(tunnel_dir))) & DiagDirToRoadBits(tunnel_dir))) return false; + + const int delta = TileOffsByDiagDir(tunnel_dir); + int max_tunnel_length = 0; + + /* There are two conditions for building tunnels: Under a mountain and under an obstruction. */ + if (CanRoadContinueIntoNextTile(t, tile, tunnel_dir)) { + /* Only tunnel under a mountain if the slope is continuous for at least 4 tiles. We want tunneling to be a last resort for large hills. */ + TileIndex slope_tile = tile; + for (uint8 tiles = 0; tiles < 4; tiles++) { + slope = GetTileSlope(slope_tile); + if (slope != InclinedSlope(tunnel_dir) && !IsSteepSlope(slope) && !IsSlopeWithOneCornerRaised(slope)) return false; + slope_tile += delta; + } + + /* More population means longer tunnels, but make sure we can at least cover the smallest mountain which neccesitates tunneling. */ + max_tunnel_length = (t->cache.population / 1000) + 7; + } else { + /* When tunneling under an obstruction, the length limit is 5, enough to tunnel under a four-track railway. */ + max_tunnel_length = 5; + } + + uint8 tunnel_length = 0; + TileIndex tunnel_tile = tile; // Iteratator to store the other end tile of the tunnel. + + /* Find the end tile of the tunnel for length and continuation checks. */ + do { + if (tunnel_length++ >= max_tunnel_length) return false; + tunnel_tile += delta; + /* The tunnel ends when start and end tiles are the same height. */ + } while (IsValidTile(tunnel_tile) && GetTileZ(tile) != GetTileZ(tunnel_tile)); + + /* Don't allow a tunnel where the start and end tiles are adjacent. */ + if (tunnel_length == 1) return false; + + /* Make sure the road can be continued past the tunnel. At this point, tunnel_tile holds the end tile of the tunnel. */ + if (!CanRoadContinueIntoNextTile(t, tunnel_tile, tunnel_dir)) return false; + + /* Attempt to build the tunnel. Return false if it fails to let the town build a road instead. */ + RoadType rt = GetTownRoadType(t); + if (DoCommand(tile, rt | (TRANSPORT_ROAD << 8), 0, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_TUNNEL)), CMD_BUILD_TUNNEL).Succeeded()) { + DoCommand(tile, rt | (TRANSPORT_ROAD << 8), 0, DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_TUNNEL)), CMD_BUILD_TUNNEL); + _grow_town_result = GROWTH_SUCCEED; + return true; + } + + return false; +} + +/** * Checks whether at least one surrounding roads allows to build a house here * * @param t the tile where the house will be built @@ -1460,10 +1529,11 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t rcmd = CleanUpRoadBits(tile, rcmd); if (rcmd == ROAD_NONE) return; - /* Only use the target direction for bridges to ensure they're connected. + /* Only use the target direction for bridges and tunnels to ensure they're connected. * The target_dir is as computed previously according to town layout, so * it will match it perfectly. */ if (GrowTownWithBridge(t1, tile, target_dir)) return; + if (GrowTownWithTunnel(t1, tile, target_dir)) return; GrowTownWithRoad(t1, tile, rcmd); } |