Skip to content

Commit

Permalink
Add: Towns can build tunnels
Browse files Browse the repository at this point in the history
  • Loading branch information
2TallTyler committed Jan 8, 2021
1 parent 188bf0f commit 7f333df
Showing 1 changed file with 87 additions and 17 deletions.
104 changes: 87 additions & 17 deletions src/town_cmd.cpp
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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);
Expand All @@ -1225,6 +1225,75 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi
return false;
}

/**
* 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
*
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 7f333df

Please sign in to comment.