@ -8,6 +8,11 @@
/** @file road.cpp Generic road related functions. */
# include "stdafx.h"
# include <algorithm>
# include <memory>
# include <numeric>
# include <unordered_map>
# include <vector>
# include "rail_map.h"
# include "road_map.h"
# include "water_map.h"
@ -18,13 +23,30 @@
# include "date_func.h"
# include "landscape.h"
# include "road.h"
# include "town.h"
# include "pathfinder/npf/aystar.h"
# include "tunnelbridge.h"
# include "road_func.h"
# include "roadveh.h"
# include "map_func.h"
# include "core/backup_type.hpp"
# include "core/random_func.hpp"
# include <numeric>
# include "cheat_func.h"
# include "command_func.h"
# include "safeguards.h"
uint32 _road_layout_change_counter = 0 ;
/** Whether to build public roads */
enum PublicRoadsConstruction {
PRC_NONE , ///< Generate no public roads
PRC_WITH_CURVES , ///< Generate roads with lots of curves
PRC_AVOID_CURVES , ///< Generate roads avoiding curves if possible
} ;
/**
* Return if the tile is a valid tile for a crossing .
*
@ -216,6 +238,640 @@ RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces)
return rts ;
}
/* ========================================================================= */
/* PUBLIC ROADS */
/* ========================================================================= */
CommandCost CmdBuildBridge ( TileIndex end_tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text = nullptr ) ;
CommandCost CmdBuildTunnel ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text = nullptr ) ;
CommandCost CmdBuildRoad ( TileIndex tile , DoCommandFlag flags , uint32 p1 , uint32 p2 , const char * text = nullptr ) ;
static std : : vector < TileIndex > _town_centers ;
static std : : vector < TileIndex > _towns_visited_along_the_way ;
static bool _has_tunnel_in_path ;
static RoadType _public_road_type ;
static const uint _public_road_hash_size = 8U ; ///< The number of bits the hash for river finding should have.
/**
* Simple hash function for public road tiles to be used by AyStar .
* @ param tile The tile to hash .
* @ param dir The unused direction .
* @ return The hash for the tile .
*/
static uint PublicRoad_Hash ( uint tile , uint dir )
{
return GB ( TileHash ( TileX ( tile ) , TileY ( tile ) ) , 0 , _public_road_hash_size ) ;
}
static const int32 BASE_COST = 1 ; // Cost for utilizing an existing road, bridge, or tunnel.
static const int32 COST_FOR_NEW_ROAD = 10 ; // Cost for building a new road.
static const int32 COST_FOR_SLOPE = 5 ; // Additional cost if the road heads up or down a slope.
/** AyStar callback for getting the cost of the current node. */
static int32 PublicRoad_CalculateG ( AyStar * , AyStarNode * current , OpenListNode * parent )
{
int32 cost = BASE_COST ;
if ( ! IsTileType ( current - > tile , MP_ROAD ) ) {
if ( ! AreTilesAdjacent ( parent - > path . node . tile , current - > tile ) )
{
// We're not adjacent, so we built a tunnel or bridge.
cost + = ( DistanceManhattan ( parent - > path . node . tile , current - > tile ) ) * COST_FOR_NEW_ROAD + 6 * COST_FOR_SLOPE ;
}
else if ( ! IsTileFlat ( current - > tile ) ) {
cost + = COST_FOR_NEW_ROAD ;
cost + = COST_FOR_SLOPE ;
}
else
{
cost + = COST_FOR_NEW_ROAD ;
}
}
if ( _settings_game . game_creation . build_public_roads = = PRC_AVOID_CURVES & &
parent - > path . parent ! = nullptr & &
DiagdirBetweenTiles ( parent - > path . parent - > node . tile , parent - > path . node . tile ) ! = DiagdirBetweenTiles ( parent - > path . node . tile , current - > tile ) ) {
cost + = 1 ;
}
return cost ;
}
/** AyStar callback for getting the estimated cost to the destination. */
static int32 PublicRoad_CalculateH ( AyStar * aystar , AyStarNode * current , OpenListNode * parent )
{
return DistanceManhattan ( * static_cast < TileIndex * > ( aystar - > user_target ) , current - > tile ) * BASE_COST ;
}
/** Helper function to check if a tile along a certain direction is going up an inclined slope. */
static bool IsUpwardsSlope ( TileIndex tile , DiagDirection road_direction )
{
const auto slope = GetTileSlope ( tile ) ;
if ( ! IsInclinedSlope ( slope ) ) return false ;
const auto slope_direction = GetInclinedSlopeDirection ( slope ) ;
return road_direction = = slope_direction ;
}
/** Helper function to check if a tile along a certain direction is going down an inclined slope. */
static bool IsDownwardsSlope ( const TileIndex tile , const DiagDirection road_direction )
{
const auto slope = GetTileSlope ( tile ) ;
if ( ! IsInclinedSlope ( slope ) ) return false ;
const auto slope_direction = GetInclinedSlopeDirection ( slope ) ;
return road_direction = = ReverseDiagDir ( slope_direction ) ;
}
static TileIndex BuildTunnel ( PathNode * current , TileIndex end_tile = INVALID_TILE , const bool build_tunnel = false )
{
const TileIndex start_tile = current - > node . tile ;
int start_z ;
GetTileSlope ( start_tile , & start_z ) ;
if ( start_z = = 0 ) return INVALID_TILE ;
const DiagDirection direction = GetInclinedSlopeDirection ( GetTileSlope ( start_tile ) ) ;
if ( ! build_tunnel ) {
// We are not building yet, so we still need to find the end_tile.
const TileIndexDiff delta = TileOffsByDiagDir ( direction ) ;
end_tile = start_tile ;
int end_z ;
for ( int tunnel_length = 1 ; ; tunnel_length + + ) {
end_tile + = delta ;
if ( ! IsValidTile ( end_tile ) ) return INVALID_TILE ;
if ( tunnel_length > _settings_game . construction . max_tunnel_length ) return INVALID_TILE ;
GetTileSlope ( end_tile , & end_z ) ;
if ( start_z = = end_z ) break ;
if ( ! _cheats . crossing_tunnels . value & & IsTunnelInWay ( end_tile , start_z ) ) return INVALID_TILE ;
}
// No too long or super-short tunnels and always ending up on a matching upwards slope.
if ( IsSteepSlope ( GetTileSlope ( end_tile ) ) | | IsHalftileSlope ( GetTileSlope ( end_tile ) ) ) return INVALID_TILE ;
if ( GetTileSlope ( start_tile ) ! = ComplementSlope ( GetTileSlope ( end_tile ) ) ) return INVALID_TILE ;
if ( AreTilesAdjacent ( start_tile , end_tile ) ) return INVALID_TILE ;
if ( ! IsValidTile ( end_tile ) ) return INVALID_TILE ;
if ( ! IsTileType ( end_tile , MP_CLEAR ) & & ! IsTileType ( end_tile , MP_TREES ) ) return INVALID_TILE ;
}
assert ( ! build_tunnel | | ( IsValidTile ( end_tile ) & & GetTileSlope ( start_tile ) = = ComplementSlope ( GetTileSlope ( end_tile ) ) ) ) ;
Backup cur_company ( _current_company , OWNER_DEITY , FILE_LINE ) ;
const auto build_tunnel_cmd = CmdBuildTunnel ( start_tile , build_tunnel ? DC_EXEC : DC_NONE , _public_road_type | ( TRANSPORT_ROAD < < 8 ) , 0 ) ;
cur_company . Restore ( ) ;
assert ( ! build_tunnel | | build_tunnel_cmd . Succeeded ( ) ) ;
assert ( ! build_tunnel | | ( IsTileType ( start_tile , MP_TUNNELBRIDGE ) & & IsTileType ( end_tile , MP_TUNNELBRIDGE ) ) ) ;
if ( ! build_tunnel_cmd . Succeeded ( ) ) return INVALID_TILE ;
return end_tile ;
}
static TileIndex BuildBridge ( PathNode * current , TileIndex end_tile = INVALID_TILE , const bool build_bridge = false )
{
const TileIndex start_tile = current - > node . tile ;
const DiagDirection direction = ReverseDiagDir ( GetInclinedSlopeDirection ( GetTileSlope ( start_tile ) ) ) ;
if ( ! build_bridge ) {
// We are not building yet, so we still need to find the end_tile.
for ( TileIndex tile = start_tile + TileOffsByDiagDir ( direction ) ;
IsValidTile ( tile ) & &
( GetTunnelBridgeLength ( start_tile , tile ) < = _settings_game . construction . max_bridge_length ) & &
( GetTileZ ( start_tile ) < ( GetTileZ ( tile ) + _settings_game . construction . max_bridge_height ) ) & &
( GetTileZ ( tile ) < = GetTileZ ( start_tile ) ) ;
tile + = TileOffsByDiagDir ( direction ) ) {
auto is_complementary_slope =
! IsSteepSlope ( GetTileSlope ( tile ) ) & &
! IsHalftileSlope ( GetTileSlope ( tile ) ) & &
GetTileSlope ( start_tile ) = = ComplementSlope ( GetTileSlope ( tile ) ) ;
// No super-short bridges and always ending up on a matching upwards slope.
if ( ! AreTilesAdjacent ( start_tile , tile ) & & is_complementary_slope ) {
end_tile = tile ;
break ;
}
}
if ( ! IsValidTile ( end_tile ) ) return INVALID_TILE ;
if ( GetTileSlope ( start_tile ) ! = ComplementSlope ( GetTileSlope ( end_tile ) ) ) return INVALID_TILE ;
if ( ! IsTileType ( end_tile , MP_CLEAR ) & & ! IsTileType ( end_tile , MP_TREES ) ) return INVALID_TILE ;
}
assert ( ! build_bridge | | ( IsValidTile ( end_tile ) & & GetTileSlope ( start_tile ) = = ComplementSlope ( GetTileSlope ( end_tile ) ) ) ) ;
std : : vector < BridgeType > available_bridge_types ;
for ( uint i = 0 ; i < MAX_BRIDGES ; + + i ) {
if ( CheckBridgeAvailability ( i , GetTunnelBridgeLength ( start_tile , end_tile ) ) . Succeeded ( ) ) {
available_bridge_types . push_back ( i ) ;
}
}
assert ( ! build_bridge | | ! available_bridge_types . empty ( ) ) ;
if ( available_bridge_types . empty ( ) ) return INVALID_TILE ;
const auto bridge_type = available_bridge_types [ build_bridge ? RandomRange ( uint32 ( available_bridge_types . size ( ) ) ) : 0 ] ;
Backup cur_company ( _current_company , OWNER_DEITY , FILE_LINE ) ;
const auto build_bridge_cmd = CmdBuildBridge ( end_tile , build_bridge ? DC_EXEC : DC_NONE , start_tile , bridge_type | ( ROADTYPE_ROAD < < 8 ) | ( TRANSPORT_ROAD < < 15 ) ) ;
cur_company . Restore ( ) ;
assert ( ! build_bridge | | build_bridge_cmd . Succeeded ( ) ) ;
assert ( ! build_bridge | | ( IsTileType ( start_tile , MP_TUNNELBRIDGE ) & & IsTileType ( end_tile , MP_TUNNELBRIDGE ) ) ) ;
if ( ! build_bridge_cmd . Succeeded ( ) ) return INVALID_TILE ;
return end_tile ;
}
static TileIndex BuildRiverBridge ( PathNode * current , const DiagDirection road_direction , TileIndex end_tile = INVALID_TILE , const bool build_bridge = false )
{
const TileIndex start_tile = current - > node . tile ;
if ( ! build_bridge ) {
// We are not building yet, so we still need to find the end_tile.
// We will only build a bridge if we need to cross a river, so first check for that.
TileIndex tile = start_tile + TileOffsByDiagDir ( road_direction ) ;
if ( ! IsWaterTile ( tile ) | | ! IsRiver ( tile ) ) return INVALID_TILE ;
// Now let's see if we can bridge it. But don't bridge anything more than 4 river tiles. Cities aren't allowed to, so public roads we are not either.
// Only bridges starting at slopes should be longer ones. The others look like crap when built this way. Players can build them but the map generator
// should not force that on them. This is just to bridge rivers, not to make long bridges.
for ( ;
IsValidTile ( tile ) & &
( GetTunnelBridgeLength ( start_tile , tile ) < = 5 ) & &
( GetTileZ ( start_tile ) < ( GetTileZ ( tile ) + _settings_game . construction . max_bridge_height ) ) & &
( GetTileZ ( tile ) < = GetTileZ ( start_tile ) ) ;
tile + = TileOffsByDiagDir ( road_direction ) ) {
if ( ( IsTileType ( tile , MP_CLEAR ) | | IsTileType ( tile , MP_TREES ) ) & &
GetTileZ ( tile ) < = GetTileZ ( start_tile ) & &
GetTileSlope ( tile ) = = SLOPE_FLAT ) {
end_tile = tile ;
break ;
}
}
if ( ! IsValidTile ( end_tile ) ) return INVALID_TILE ;
if ( ! IsTileType ( end_tile , MP_CLEAR ) & & ! IsTileType ( end_tile , MP_TREES ) ) return INVALID_TILE ;
}
assert ( ! build_bridge | | IsValidTile ( end_tile ) ) ;
std : : vector < BridgeType > available_bridge_types ;
for ( uint i = 0 ; i < MAX_BRIDGES ; + + i ) {
if ( CheckBridgeAvailability ( i , GetTunnelBridgeLength ( start_tile , end_tile ) ) . Succeeded ( ) ) {
available_bridge_types . push_back ( i ) ;
}
}
const auto bridge_type = available_bridge_types [ build_bridge ? RandomRange ( uint32 ( available_bridge_types . size ( ) ) ) : 0 ] ;
Backup cur_company ( _current_company , OWNER_DEITY , FILE_LINE ) ;
const auto build_bridge_cmd = CmdBuildBridge ( end_tile , build_bridge ? DC_EXEC : DC_NONE , start_tile , bridge_type | ( ROADTYPE_ROAD < < 8 ) | ( TRANSPORT_ROAD < < 15 ) ) ;
cur_company . Restore ( ) ;
assert ( ! build_bridge | | build_bridge_cmd . Succeeded ( ) ) ;
assert ( ! build_bridge | | ( IsTileType ( start_tile , MP_TUNNELBRIDGE ) & & IsTileType ( end_tile , MP_TUNNELBRIDGE ) ) ) ;
if ( ! build_bridge_cmd . Succeeded ( ) ) return INVALID_TILE ;
return end_tile ;
}
static bool IsValidNeighbourOfPreviousTile ( const TileIndex tile , const TileIndex previous_tile )
{
if ( ! IsValidTile ( tile ) | | ( tile = = previous_tile ) ) return false ;
if ( IsTileType ( tile , MP_TUNNELBRIDGE ) )
{
if ( GetOtherTunnelBridgeEnd ( tile ) = = previous_tile ) return true ;
const auto tunnel_direction = GetTunnelBridgeDirection ( tile ) ;
if ( previous_tile + TileOffsByDiagDir ( tunnel_direction ) ! = tile ) return false ;
} else {
if ( ! IsTileType ( tile , MP_CLEAR ) & & ! IsTileType ( tile , MP_TREES ) & & ! IsTileType ( tile , MP_ROAD ) ) return false ;
const auto slope = GetTileSlope ( tile ) ;
// Do not allow foundations. We'll mess things up later.
const bool has_foundation = GetFoundationSlope ( tile ) ! = slope ;
if ( has_foundation ) return false ;
if ( IsInclinedSlope ( slope ) ) {
const auto slope_direction = GetInclinedSlopeDirection ( slope ) ;
const auto road_direction = DiagdirBetweenTiles ( previous_tile , tile ) ;
if ( slope_direction ! = road_direction & & ReverseDiagDir ( slope_direction ) ! = road_direction ) {
return false ;
}
} else if ( slope ! = SLOPE_FLAT ) {
return false ;
}
}
return true ;
}
/** AyStar callback for getting the neighbouring nodes of the given node. */
static void PublicRoad_GetNeighbours ( AyStar * aystar , OpenListNode * current )
{
const TileIndex tile = current - > path . node . tile ;
aystar - > num_neighbours = 0 ;
// Check if we just went through a tunnel or a bridge.
if ( current - > path . parent ! = nullptr & & ! AreTilesAdjacent ( tile , current - > path . parent - > node . tile ) ) {
const auto previous_tile = current - > path . parent - > node . tile ;
// We went through a tunnel or bridge, this limits our options to proceed to only forward.
const auto tunnel_bridge_direction = DiagdirBetweenTiles ( previous_tile , tile ) ;
const TileIndex tunnel_bridge_end = tile + TileOffsByDiagDir ( tunnel_bridge_direction ) ;
if ( IsValidNeighbourOfPreviousTile ( tunnel_bridge_end , tile ) ) {
aystar - > neighbours [ aystar - > num_neighbours ] . tile = tunnel_bridge_end ;
aystar - > neighbours [ aystar - > num_neighbours ] . direction = INVALID_TRACKDIR ;
aystar - > num_neighbours + + ;
}
} else {
// Handle all the regular neighbours and existing tunnels/bridges.
std : : vector < TileIndex > potential_neighbours ;
if ( IsTileType ( tile , MP_TUNNELBRIDGE ) ) {
auto neighbour = GetOtherTunnelBridgeEnd ( tile ) ;
aystar - > neighbours [ aystar - > num_neighbours ] . tile = neighbour ;
aystar - > neighbours [ aystar - > num_neighbours ] . direction = INVALID_TRACKDIR ;
aystar - > num_neighbours + + ;
neighbour = tile + TileOffsByDiagDir ( ReverseDiagDir ( DiagdirBetweenTiles ( tile , neighbour ) ) ) ;
if ( IsValidNeighbourOfPreviousTile ( neighbour , tile ) ) {
aystar - > neighbours [ aystar - > num_neighbours ] . tile = neighbour ;
aystar - > neighbours [ aystar - > num_neighbours ] . direction = INVALID_TRACKDIR ;
aystar - > num_neighbours + + ;
}
} else {
for ( DiagDirection d = DIAGDIR_BEGIN ; d < DIAGDIR_END ; d + + ) {
const auto neighbour = tile + TileOffsByDiagDir ( d ) ;
if ( IsValidNeighbourOfPreviousTile ( neighbour , tile ) ) {
aystar - > neighbours [ aystar - > num_neighbours ] . tile = neighbour ;
aystar - > neighbours [ aystar - > num_neighbours ] . direction = INVALID_TRACKDIR ;
aystar - > num_neighbours + + ;
}
}
// Check if we can turn this into a tunnel or a bridge.
if ( current - > path . parent ! = nullptr ) {
const auto road_direction = DiagdirBetweenTiles ( current - > path . parent - > node . tile , tile ) ;
if ( IsUpwardsSlope ( tile , road_direction ) & & ! _has_tunnel_in_path ) {
const auto tunnel_end = BuildTunnel ( & current - > path ) ;
if ( tunnel_end ! = INVALID_TILE & &
! IsSteepSlope ( GetTileSlope ( tunnel_end ) ) & &
! IsHalftileSlope ( GetTileSlope ( tunnel_end ) ) & &
( GetTileSlope ( tunnel_end ) = = ComplementSlope ( GetTileSlope ( current - > path . node . tile ) ) ) ) {
assert ( IsValidDiagDirection ( DiagdirBetweenTiles ( tile , tunnel_end ) ) ) ;
aystar - > neighbours [ aystar - > num_neighbours ] . tile = tunnel_end ;
aystar - > neighbours [ aystar - > num_neighbours ] . direction = INVALID_TRACKDIR ;
aystar - > num_neighbours + + ;
_has_tunnel_in_path = true ;
}
}
else if ( IsDownwardsSlope ( tile , road_direction ) ) {
const auto bridge_end = BuildBridge ( & current - > path ) ;
if ( bridge_end ! = INVALID_TILE & &
! IsSteepSlope ( GetTileSlope ( bridge_end ) ) & &
! IsHalftileSlope ( GetTileSlope ( bridge_end ) ) & &
( GetTileSlope ( bridge_end ) = = ComplementSlope ( GetTileSlope ( current - > path . node . tile ) ) ) ) {
assert ( IsValidDiagDirection ( DiagdirBetweenTiles ( tile , bridge_end ) ) ) ;
aystar - > neighbours [ aystar - > num_neighbours ] . tile = bridge_end ;
aystar - > neighbours [ aystar - > num_neighbours ] . direction = INVALID_TRACKDIR ;
aystar - > num_neighbours + + ;
}
}
else if ( GetTileSlope ( tile ) = = SLOPE_FLAT )
{
// Check if we could bridge a river from a flat tile. Not looking pretty on the map but you gotta do what you gotta do.
const auto bridge_end = BuildRiverBridge ( & current - > path , DiagdirBetweenTiles ( current - > path . parent - > node . tile , tile ) ) ;
assert ( bridge_end = = INVALID_TILE | | GetTileSlope ( bridge_end ) = = SLOPE_FLAT ) ;
if ( bridge_end ! = INVALID_TILE ) {
assert ( IsValidDiagDirection ( DiagdirBetweenTiles ( tile , bridge_end ) ) ) ;
aystar - > neighbours [ aystar - > num_neighbours ] . tile = bridge_end ;
aystar - > neighbours [ aystar - > num_neighbours ] . direction = INVALID_TRACKDIR ;
aystar - > num_neighbours + + ;
}
}
}
}
}
}
/** AyStar callback for checking whether we reached our destination. */
static int32 PublicRoad_EndNodeCheck ( const AyStar * aystar , const OpenListNode * current )
{
// Mark towns visited along the way.
const auto search_result =
std : : find ( _town_centers . begin ( ) , _town_centers . end ( ) , current - > path . node . tile ) ;
if ( search_result ! = _town_centers . end ( ) ) {
_towns_visited_along_the_way . push_back ( current - > path . node . tile ) ;
}
return current - > path . node . tile = = * static_cast < TileIndex * > ( aystar - > user_target ) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE ;
}
/** AyStar callback when an route has been found. */
static void PublicRoad_FoundEndNode ( AyStar * aystar , OpenListNode * current )
{
PathNode * child = nullptr ;
for ( PathNode * path = & current - > path ; path ! = nullptr ; path = path - > parent ) {
const TileIndex tile = path - > node . tile ;
if ( IsTileType ( tile , MP_TUNNELBRIDGE ) ) {
// Just follow the path; infrastructure is already in place.
continue ;
}
if ( path - > parent = = nullptr | | AreTilesAdjacent ( tile , path - > parent - > node . tile ) ) {
RoadBits road_bits = ROAD_NONE ;
if ( child ! = nullptr ) {
const TileIndex tile2 = child - > node . tile ;
road_bits | = DiagDirToRoadBits ( DiagdirBetweenTiles ( tile , tile2 ) ) ;
}
if ( path - > parent ! = nullptr ) {
const TileIndex tile2 = path - > parent - > node . tile ;
road_bits | = DiagDirToRoadBits ( DiagdirBetweenTiles ( tile , tile2 ) ) ;
}
if ( child ! = nullptr | | path - > parent ! = nullptr ) {
// Check if we need to build anything.
bool need_to_build_road = true ;
if ( IsTileType ( tile , MP_ROAD ) ) {
const RoadBits existing_bits = GetRoadBits ( tile , RTT_ROAD ) ;
CLRBITS ( road_bits , existing_bits ) ;
if ( road_bits = = ROAD_NONE ) need_to_build_road = false ;
}
// If it is already a road and has the right bits, we are good. Otherwise build the needed ones.
if ( need_to_build_road )
{
Backup cur_company ( _current_company , OWNER_DEITY , FILE_LINE ) ;
CmdBuildRoad ( tile , DC_EXEC , _public_road_type < < 4 | road_bits , 0 ) ;
cur_company . Restore ( ) ;
}
}
} else {
// We only get here if we have a parent and we're not adjacent to it. River/Tunnel time!
const DiagDirection road_direction = DiagdirBetweenTiles ( tile , path - > parent - > node . tile ) ;
auto end_tile = INVALID_TILE ;
if ( IsUpwardsSlope ( tile , road_direction ) ) {
end_tile = BuildTunnel ( path , path - > parent - > node . tile , true ) ;
assert ( IsValidTile ( end_tile ) & & IsDownwardsSlope ( end_tile , road_direction ) ) ;
} else if ( IsDownwardsSlope ( tile , road_direction ) ) {
// Provide the function with the end tile, since we already know it, but still check the result.
end_tile = BuildBridge ( path , path - > parent - > node . tile , true ) ;
assert ( IsValidTile ( end_tile ) & & IsUpwardsSlope ( end_tile , road_direction ) ) ;
} else {
// River bridge is the last possibility.
assert ( GetTileSlope ( tile ) = = SLOPE_FLAT ) ;
end_tile = BuildRiverBridge ( path , road_direction , path - > parent - > node . tile , true ) ;
assert ( IsValidTile ( end_tile ) & & GetTileSlope ( end_tile ) = = SLOPE_FLAT ) ;
}
}
child = path ;
}
}
bool FindPath ( AyStar & finder , const TileIndex from , TileIndex to )
{
finder . CalculateG = PublicRoad_CalculateG ;
finder . CalculateH = PublicRoad_CalculateH ;
finder . GetNeighbours = PublicRoad_GetNeighbours ;
finder . EndNodeCheck = PublicRoad_EndNodeCheck ;
finder . FoundEndNode = PublicRoad_FoundEndNode ;
finder . user_target = & ( to ) ;
finder . max_search_nodes = 1 < < 20 ; // 1,048,576
finder . Init ( PublicRoad_Hash , 1 < < _public_road_hash_size ) ;
_has_tunnel_in_path = false ;
AyStarNode start { } ;
start . tile = from ;
start . direction = INVALID_TRACKDIR ;
finder . AddStartNode ( & start , 0 ) ;
int result = AYSTAR_STILL_BUSY ;
while ( result = = AYSTAR_STILL_BUSY ) {
result = finder . Main ( ) ;
}
const bool found_path = ( result = = AYSTAR_FOUND_END_NODE ) ;
return found_path ;
}
/**
* Build the public road network connecting towns using AyStar .
*/
void GeneratePublicRoads ( )
{
using namespace std ;
if ( _settings_game . game_creation . build_public_roads = = PRC_NONE ) return ;
_town_centers . clear ( ) ;
_towns_visited_along_the_way . clear ( ) ;
vector < TileIndex > towns ;
towns . clear ( ) ;
{
for ( const Town * town : Town : : Iterate ( ) ) {
towns . push_back ( town - > xy ) ;
_town_centers . push_back ( town - > xy ) ;
}
}
if ( towns . empty ( ) ) {
return ;
}
SetGeneratingWorldProgress ( GWP_PUBLIC_ROADS , uint ( towns . size ( ) ) ) ;
// Create a list of networks which also contain a value indicating how many times we failed to connect to them.
vector < pair < uint , shared_ptr < vector < TileIndex > > > > town_networks ;
unordered_map < TileIndex , shared_ptr < vector < TileIndex > > > towns_reachable_networks ;
TileIndex main_town = * towns . begin ( ) ;
towns . erase ( towns . begin ( ) ) ;
_public_road_type = GetTownRoadType ( Town : : GetByTile ( main_town ) ) ;
auto main_network = make_shared < vector < TileIndex > > ( ) ;
main_network - > push_back ( main_town ) ;
town_networks . emplace_back ( 0 , main_network ) ;
IncreaseGeneratingWorldProgress ( GWP_PUBLIC_ROADS ) ;
sort ( towns . begin ( ) , towns . end ( ) , [ & ] ( auto a , auto b ) { return DistanceManhattan ( main_town , a ) < DistanceManhattan ( main_town , b ) ; } ) ;
for ( auto begin_town : towns ) {
// Check if we can connect to any of the networks.
_towns_visited_along_the_way . clear ( ) ;
auto reachable_network_iter = towns_reachable_networks . find ( begin_town ) ;
bool found_easy_path = false ;
if ( reachable_network_iter ! = towns_reachable_networks . end ( ) ) {
auto reachable_network = reachable_network_iter - > second ;
sort ( reachable_network - > begin ( ) , reachable_network - > end ( ) , [ & ] ( auto a , auto b ) { return DistanceManhattan ( begin_town , a ) < DistanceManhattan ( begin_town , b ) ; } ) ;
const TileIndex end_town = * reachable_network - > begin ( ) ;
AyStar finder { } ;
found_easy_path = FindPath ( finder , begin_town , end_town ) ;
finder . Free ( ) ;
}
if ( found_easy_path ) {
reachable_network_iter - > second - > push_back ( begin_town ) ;
for ( const TileIndex visited_town : _towns_visited_along_the_way ) {
if ( visited_town ! = begin_town ) towns_reachable_networks [ visited_town ] = reachable_network_iter - > second ;
}
} else {
// Sort networks by failed connection attempts, so we try the most likely one first.
sort ( town_networks . begin ( ) , town_networks . end ( ) , [ & ] ( auto a , auto b ) { return a . first < b . first ; } ) ;
std : : function < bool ( pair < uint , shared_ptr < vector < TileIndex > > > ) > can_reach_network = [ & ] ( auto network_pair ) {
AyStar finder { } ;
auto network = network_pair . second ;
// Try to connect to the town in the network that is closest to us.
// If we can't connect to that one, we can't connect to any of them since they are all interconnected.
sort ( network - > begin ( ) , network - > end ( ) , [ & ] ( auto a , auto b ) { return DistanceManhattan ( begin_town , a ) < DistanceManhattan ( begin_town , b ) ; } ) ;
const TileIndex end_town = * network - > begin ( ) ;
const bool found_path = FindPath ( finder , begin_town , end_town ) ;
if ( found_path ) {
network - > push_back ( begin_town ) ;
for ( auto visited_town : _towns_visited_along_the_way ) {
if ( visited_town ! = begin_town ) towns_reachable_networks [ visited_town ] = network ;
}
}
// Increase number of failed attempts if necessary.
network_pair . first + = ( found_path ? ( network_pair . first > 0 ? - 1 : 0 ) : 1 ) ;
finder . Free ( ) ;
return found_path ;
} ;
if ( ! any_of ( town_networks . begin ( ) , town_networks . end ( ) , can_reach_network ) ) {
// We failed to connect to any network, so we are a separate network. Let future towns try to connect to us.
auto new_network = make_shared < vector < TileIndex > > ( ) ;
new_network - > push_back ( begin_town ) ;
// We basically failed to connect to this many towns.
int towns_already_in_networks = std : : accumulate ( town_networks . begin ( ) , town_networks . end ( ) , 0 , [ & ] ( int accumulator , auto network_pair ) {
return accumulator + static_cast < int > ( network_pair . second - > size ( ) ) ;
} ) ;
town_networks . emplace_back ( towns_already_in_networks , new_network ) ;
for ( const TileIndex visited_town : _towns_visited_along_the_way ) {
if ( visited_town ! = begin_town ) towns_reachable_networks . insert ( make_pair ( visited_town , new_network ) ) ;
}
}
}
IncreaseGeneratingWorldProgress ( GWP_PUBLIC_ROADS ) ;
}
}
/* ========================================================================= */
/* END PUBLIC ROADS */
/* ========================================================================= */
/**
* Get list of road types , regardless of company availability .
* @ param introduces If true , include road types introduced by other road types