diff --git a/src/road.cpp b/src/road.cpp index 7e525d9a72..ca24e8421c 100644 --- a/src/road.cpp +++ b/src/road.cpp @@ -246,8 +246,6 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u 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 _town_centers; -static std::vector _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. @@ -263,14 +261,15 @@ 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. +static const int32 _base_cost = 1; // Cost for utilizing an existing road, bridge, or tunnel. /** AyStar callback for getting the cost of the current node. */ static int32 PublicRoad_CalculateG(AyStar *, AyStarNode *current, OpenListNode *parent) { - int32 cost = BASE_COST; + 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. + + int32 cost = _base_cost; if (!IsTileType(current->tile, MP_ROAD)) { if (!AreTilesAdjacent(parent->path.node.tile, current->tile)) @@ -300,7 +299,7 @@ static int32 PublicRoad_CalculateG(AyStar *, AyStarNode *current, OpenListNode * /** AyStar callback for getting the estimated cost to the destination. */ static int32 PublicRoad_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent) { - return DistanceManhattan(*static_cast(aystar->user_target), current->tile) * BASE_COST; + return DistanceManhattan(*static_cast(aystar->user_target), current->tile) * _base_cost; } /** Helper function to check if a tile along a certain direction is going up an inclined slope. */ @@ -633,14 +632,6 @@ static void PublicRoad_GetNeighbours(AyStar *aystar, OpenListNode *current) /** 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(aystar->user_target) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE; } @@ -750,117 +741,87 @@ void GeneratePublicRoads() using namespace std; if (_settings_game.game_creation.build_public_roads == PRC_NONE) return; + + vector cities; + vector villages; + vector unconnected_villages; - _town_centers.clear(); - _towns_visited_along_the_way.clear(); + _public_road_type = GetTownRoadType(*Town::Iterate().begin()); - vector towns; - towns.clear(); - { - for (const Town *town : Town::Iterate()) { - towns.push_back(town->xy); - _town_centers.push_back(town->xy); + for (const Town *town : Town::Iterate()) { + if (town->larger_town) { + cities.push_back(town->xy); + } else { + villages.push_back(town->xy); } } - if (towns.empty()) { - return; + SetGeneratingWorldProgress(GWP_PUBLIC_ROADS, uint(cities.size() + villages.size())); + + // Try to connect every village to the nearest city. + for (auto village : villages) { + sort(cities.begin(), cities.end(), [&](auto a, auto b) { return DistanceManhattan(village, a) < DistanceManhattan(village, b); }); + + bool is_connected = false; + + for (auto city : cities) { + AyStar finder {}; + + auto found_path = FindPath(finder, village, city); + + finder.Free(); + + if (found_path){ + is_connected = true; + break; + } + } + + if (!is_connected) { + unconnected_villages.push_back(village); + } else { + IncreaseGeneratingWorldProgress(GWP_PUBLIC_ROADS); + } } - - 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>>> town_networks; - unordered_map>> towns_reachable_networks; + vector villages_copy(unconnected_villages); - TileIndex main_town = *towns.begin(); - towns.erase(towns.begin()); + // If a village could not be connected to a city, try to connect it to another unconnected village. + for (auto village : unconnected_villages) { + sort(villages_copy.begin(), villages_copy.end(), [&](auto a, auto b) { return DistanceManhattan(village, a) < DistanceManhattan(village, b); }); - _public_road_type = GetTownRoadType(Town::GetByTile(main_town)); + for (auto other_village : villages_copy) { + AyStar finder {}; - auto main_network = make_shared>(); - main_network->push_back(main_town); + auto found_path = FindPath(finder, village, other_village); - town_networks.emplace_back(0, main_network); - IncreaseGeneratingWorldProgress(GWP_PUBLIC_ROADS); + finder.Free(); - sort(towns.begin(), towns.end(), [&](auto a, auto b) { return DistanceManhattan(main_town, a) < DistanceManhattan(main_town, b); }); + if (found_path) { + break; + } + } - for (auto begin_town : towns) { - // Check if we can connect to any of the networks. - _towns_visited_along_the_way.clear(); + IncreaseGeneratingWorldProgress(GWP_PUBLIC_ROADS); + } - auto reachable_network_iter = towns_reachable_networks.find(begin_town); - bool found_easy_path = false; + vector cities_copy(cities); - if (reachable_network_iter != towns_reachable_networks.end()) { - auto reachable_network = reachable_network_iter->second; + // Connect the cities with each-other. + for (auto city : cities) { + sort(cities_copy.begin(), cities_copy.end(), [&](auto a, auto b) { return DistanceManhattan(city, a) < DistanceManhattan(city, b); }); - 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(); + for (auto other_city : cities_copy) { + if (other_city == city) continue; AyStar finder {}; - found_easy_path = FindPath(finder, begin_town, end_town); + auto found_path = FindPath(finder, city, other_city); 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>>)> 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>(); - 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(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)); - } + if (found_path) { + break; } }