|
|
@@ -1005,167 +1005,572 @@ void CZonePlacer::assignZones(vstd::RNG * rand)
|
|
|
logGlobal->info("Finished zone colouring");
|
|
|
}
|
|
|
|
|
|
+TRmgTemplateZoneId findSet(std::map<TRmgTemplateZoneId, TRmgTemplateZoneId> & parent, TRmgTemplateZoneId x)
|
|
|
+{
|
|
|
+ if(parent[x] != x)
|
|
|
+ parent[x] = findSet(parent, parent[x]);
|
|
|
+ return parent[x];
|
|
|
+}
|
|
|
+
|
|
|
+void unionSets(std::map<TRmgTemplateZoneId, TRmgTemplateZoneId> & parent, TRmgTemplateZoneId x, TRmgTemplateZoneId y)
|
|
|
+{
|
|
|
+ TRmgTemplateZoneId rx = findSet(parent, x);
|
|
|
+ TRmgTemplateZoneId ry = findSet(parent, y);
|
|
|
+ if(rx != ry)
|
|
|
+ parent[rx] = ry;
|
|
|
+}
|
|
|
+
|
|
|
void CZonePlacer::dropRandomRoads(vstd::RNG * rand)
|
|
|
{
|
|
|
auto zones = map.getZones();
|
|
|
- bool anyDropped;
|
|
|
-
|
|
|
- do
|
|
|
+
|
|
|
+ // First, identify zones with towns
|
|
|
+ std::set<TRmgTemplateZoneId> zonesWithTowns;
|
|
|
+
|
|
|
+ for(const auto & zone : zones)
|
|
|
{
|
|
|
- anyDropped = false;
|
|
|
-
|
|
|
- // Instead of a simple set, use a multiset to track multiple connections between zones
|
|
|
- std::map<TRmgTemplateZoneId, std::map<TRmgTemplateZoneId, int>> roadGraph;
|
|
|
- std::set<rmg::ZoneConnection> randomConnections;
|
|
|
-
|
|
|
- //Build graph and collect random connections
|
|
|
- for(const auto & zone : zones)
|
|
|
+ // A zone has towns if it has player towns or neutral towns
|
|
|
+ if(zone.second->getPlayerTowns().getTownCount() ||
|
|
|
+ zone.second->getPlayerTowns().getCastleCount() ||
|
|
|
+ zone.second->getNeutralTowns().getTownCount() ||
|
|
|
+ zone.second->getNeutralTowns().getCastleCount())
|
|
|
+ {
|
|
|
+ zonesWithTowns.insert(zone.first);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ logGlobal->info("Zones with towns: %d", zonesWithTowns.size());
|
|
|
+
|
|
|
+ if(zonesWithTowns.empty())
|
|
|
+ {
|
|
|
+ // No towns, no roads needed
|
|
|
+ // Mark all roads as FALSE
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
{
|
|
|
- for(const auto & connection : zone.second->getConnections())
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
{
|
|
|
- if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
|
|
+ if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
|
|
{
|
|
|
- roadGraph[connection.getZoneA()][connection.getZoneB()]++;
|
|
|
- roadGraph[connection.getZoneB()][connection.getZoneA()]++;
|
|
|
+ auto id = connection.getId();
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
|
|
}
|
|
|
- else if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Map to track which connections we've processed (to avoid duplicates)
|
|
|
+ std::set<int> processedConnectionIds;
|
|
|
+
|
|
|
+ // Map of all connections by their ID for easier access
|
|
|
+ std::map<int, std::pair<TRmgTemplateZoneId, TRmgTemplateZoneId>> connectionMap;
|
|
|
+
|
|
|
+ // Map true roads between zones
|
|
|
+ std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> trueRoads;
|
|
|
+
|
|
|
+ // Track existing defined connections in template (both TRUE and RANDOM)
|
|
|
+ std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> existingConnections;
|
|
|
+
|
|
|
+ // First pass: collect all connections defined in the template
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ auto id = connection.getId();
|
|
|
+ auto zoneA = connection.getZoneA();
|
|
|
+ auto zoneB = connection.getZoneB();
|
|
|
+
|
|
|
+ connectionMap[id] = std::make_pair(zoneA, zoneB);
|
|
|
+
|
|
|
+ // Track all valid connections defined in template
|
|
|
+ existingConnections[zoneA].insert(zoneB);
|
|
|
+ existingConnections[zoneB].insert(zoneA);
|
|
|
+
|
|
|
+ if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
|
|
+ {
|
|
|
+ // Record this as a true road
|
|
|
+ trueRoads[zoneA].insert(zoneB);
|
|
|
+ trueRoads[zoneB].insert(zoneA);
|
|
|
+
|
|
|
+ logGlobal->info("Keeping pre-configured TRUE road for connection %d between zones %d and %d",
|
|
|
+ id, zoneA, zoneB);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // For each town zone, ensure it has at least one connection if possible
|
|
|
+ for(auto townZone : zonesWithTowns)
|
|
|
+ {
|
|
|
+ // Skip if the town zone already has a TRUE road
|
|
|
+ if(vstd::contains(trueRoads, townZone) && !trueRoads[townZone].empty())
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if the town has any valid connections
|
|
|
+ if(!vstd::contains(existingConnections, townZone) || existingConnections[townZone].empty())
|
|
|
+ {
|
|
|
+ logGlobal->warn("Town zone %d has no valid connections in template", townZone);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Find the best random connection to mark as TRUE
|
|
|
+ bool foundConnection = false;
|
|
|
+
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ if(foundConnection) break;
|
|
|
+
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ if(connection.getRoadOption() != rmg::ERoadOption::ROAD_RANDOM)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ auto zoneA = connection.getZoneA();
|
|
|
+ auto zoneB = connection.getZoneB();
|
|
|
+
|
|
|
+ // Check if this connection involves our town zone
|
|
|
+ if(zoneA == townZone || zoneB == townZone)
|
|
|
{
|
|
|
- roadGraph[connection.getZoneA()][connection.getZoneB()]++;
|
|
|
- roadGraph[connection.getZoneB()][connection.getZoneA()]++;
|
|
|
- randomConnections.insert(connection);
|
|
|
+ // Found a connection for this town zone, mark it as TRUE
|
|
|
+ zonePtr.second->setRoadOption(connection.getId(), rmg::ERoadOption::ROAD_TRUE);
|
|
|
+
|
|
|
+ auto otherZone = (zoneA == townZone) ? zoneB : zoneA;
|
|
|
+
|
|
|
+ logGlobal->info("Setting RANDOM road to TRUE for connection %d to ensure town zone %d has at least one connection (to zone %d)",
|
|
|
+ connection.getId(), townZone, otherZone);
|
|
|
+
|
|
|
+ // Update trueRoads map
|
|
|
+ trueRoads[zoneA].insert(zoneB);
|
|
|
+ trueRoads[zoneB].insert(zoneA);
|
|
|
+
|
|
|
+ foundConnection = true;
|
|
|
+ break;
|
|
|
}
|
|
|
- // ROAD_FALSE connections are ignored
|
|
|
}
|
|
|
}
|
|
|
- logGlobal->info("Remaining random connections: %d", randomConnections.size());
|
|
|
-
|
|
|
- if(randomConnections.empty())
|
|
|
- break;
|
|
|
-
|
|
|
- //Convert to vector for shuffling
|
|
|
- std::vector<rmg::ZoneConnection> shuffledConnections(randomConnections.begin(), randomConnections.end());
|
|
|
- RandomGeneratorUtil::randomShuffle(shuffledConnections, *rand);
|
|
|
-
|
|
|
- //Try each random connection in shuffled order
|
|
|
- for(const auto & conn : shuffledConnections)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Union-find data structure for MST algorithm
|
|
|
+ std::map<TRmgTemplateZoneId, TRmgTemplateZoneId> parent;
|
|
|
+
|
|
|
+ // Initialize all town zones as separate sets
|
|
|
+ for(auto townZone : zonesWithTowns)
|
|
|
+ {
|
|
|
+ parent[townZone] = townZone;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Process TRUE roads first to build initial MST
|
|
|
+ // Add all TRUE roads that directly connect towns to the MST
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
{
|
|
|
- auto zoneA = conn.getZoneA();
|
|
|
- auto zoneB = conn.getZoneB();
|
|
|
-
|
|
|
- //Check if either zone would become isolated by removing this connection
|
|
|
- if(roadGraph[zoneA].size() <= 1 || roadGraph[zoneB].size() <= 1)
|
|
|
+ if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
|
|
{
|
|
|
- //Can't remove this connection as it would isolate a zone
|
|
|
- continue;
|
|
|
+ auto id = connection.getId();
|
|
|
+ auto zoneA = connection.getZoneA();
|
|
|
+ auto zoneB = connection.getZoneB();
|
|
|
+
|
|
|
+ // Skip if we've already processed this connection
|
|
|
+ if(processedConnectionIds.find(id) != processedConnectionIds.end())
|
|
|
+ continue;
|
|
|
+
|
|
|
+ processedConnectionIds.insert(id);
|
|
|
+
|
|
|
+ // If both ends have towns, add to MST
|
|
|
+ if(vstd::contains(zonesWithTowns, zoneA) && vstd::contains(zonesWithTowns, zoneB))
|
|
|
+ {
|
|
|
+ unionSets(parent, zoneA, zoneB);
|
|
|
+
|
|
|
+ logGlobal->info("Adding TRUE road between town zones %d and %d to MST", zoneA, zoneB);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- bool multipleConnections = roadGraph[zoneA][zoneB] > 1;
|
|
|
-
|
|
|
- //Check if this is the last road between these zones
|
|
|
- if(!multipleConnections)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Find all paths through TRUE roads
|
|
|
+ std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> reachableFromTown;
|
|
|
+
|
|
|
+ for(auto townZone : zonesWithTowns)
|
|
|
+ {
|
|
|
+ std::queue<TRmgTemplateZoneId> q;
|
|
|
+ std::set<TRmgTemplateZoneId> visited;
|
|
|
+
|
|
|
+ q.push(townZone);
|
|
|
+ visited.insert(townZone);
|
|
|
+
|
|
|
+ while(!q.empty())
|
|
|
+ {
|
|
|
+ auto current = q.front();
|
|
|
+ q.pop();
|
|
|
+
|
|
|
+ // Add this zone to reachable list for the town
|
|
|
+ reachableFromTown[townZone].insert(current);
|
|
|
+
|
|
|
+ // Check all TRUE road neighbors
|
|
|
+ if(vstd::contains(trueRoads, current))
|
|
|
{
|
|
|
- //Temporarily remove this connection
|
|
|
- roadGraph[zoneA].erase(zoneB);
|
|
|
- roadGraph[zoneB].erase(zoneA);
|
|
|
+ for(auto neighbor : trueRoads[current])
|
|
|
+ {
|
|
|
+ if(visited.find(neighbor) == visited.end())
|
|
|
+ {
|
|
|
+ visited.insert(neighbor);
|
|
|
+ q.push(neighbor);
|
|
|
+
|
|
|
+ // If this is another town, update MST
|
|
|
+ if(vstd::contains(zonesWithTowns, neighbor))
|
|
|
+ {
|
|
|
+ unionSets(parent, townZone, neighbor);
|
|
|
+
|
|
|
+ logGlobal->info("Adding multi-zone TRUE path between town zones %d and %d to MST",
|
|
|
+ townZone, neighbor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now process RANDOM roads to complete the MST
|
|
|
+ // Collect all RANDOM roads
|
|
|
+ std::vector<std::pair<int, std::pair<TRmgTemplateZoneId, TRmgTemplateZoneId>>> randomRoads;
|
|
|
+
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
|
|
+ {
|
|
|
+ auto id = connection.getId();
|
|
|
+ auto zoneA = connection.getZoneA();
|
|
|
+ auto zoneB = connection.getZoneB();
|
|
|
+
|
|
|
+ // Skip if we've already processed this connection
|
|
|
+ if(processedConnectionIds.find(id) != processedConnectionIds.end())
|
|
|
+ continue;
|
|
|
+
|
|
|
+ processedConnectionIds.insert(id);
|
|
|
+
|
|
|
+ // Add to random roads vector
|
|
|
+ randomRoads.push_back(std::make_pair(id, std::make_pair(zoneA, zoneB)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Process random roads - prioritize direct connections between towns
|
|
|
+ for(auto& road : randomRoads)
|
|
|
+ {
|
|
|
+ auto id = road.first;
|
|
|
+ auto zoneA = road.second.first;
|
|
|
+ auto zoneB = road.second.second;
|
|
|
+
|
|
|
+ // If both zones have towns, check if they're already connected in the MST
|
|
|
+ if(vstd::contains(zonesWithTowns, zoneA) && vstd::contains(zonesWithTowns, zoneB))
|
|
|
+ {
|
|
|
+ if(findSet(parent, zoneA) != findSet(parent, zoneB))
|
|
|
+ {
|
|
|
+ // Not connected, add this road to MST
|
|
|
+ unionSets(parent, zoneA, zoneB);
|
|
|
+
|
|
|
+ // Set road to TRUE
|
|
|
+ bool found = false;
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ if(connection.getId() == id)
|
|
|
+ {
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(found) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update trueRoads map
|
|
|
+ trueRoads[zoneA].insert(zoneB);
|
|
|
+ trueRoads[zoneB].insert(zoneA);
|
|
|
+
|
|
|
+ logGlobal->info("Setting RANDOM road to TRUE for direct connection %d between town zones %d and %d",
|
|
|
+ id, zoneA, zoneB);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- //Decrement the connection count
|
|
|
- roadGraph[zoneA][zoneB]--;
|
|
|
- roadGraph[zoneB][zoneA]--;
|
|
|
+ // Already connected, set to FALSE
|
|
|
+ bool found = false;
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ if(connection.getId() == id)
|
|
|
+ {
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(found) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ logGlobal->info("Setting RANDOM road to FALSE for connection %d between town zones %d and %d (already connected)",
|
|
|
+ id, zoneA, zoneB);
|
|
|
}
|
|
|
-
|
|
|
- //Check if graph remains connected as a whole
|
|
|
- bool canRemove = true;
|
|
|
-
|
|
|
- // Multiple connections imply this one can be removed without checking
|
|
|
- if (!multipleConnections)
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Special case: either zone is a town that has no other connections yet
|
|
|
+ if((vstd::contains(zonesWithTowns, zoneA) && (!vstd::contains(trueRoads, zoneA) || trueRoads[zoneA].empty())) ||
|
|
|
+ (vstd::contains(zonesWithTowns, zoneB) && (!vstd::contains(trueRoads, zoneB) || trueRoads[zoneB].empty())))
|
|
|
+ {
|
|
|
+ // Always mark this as TRUE - it's the only connection for an isolated town
|
|
|
+ bool found = false;
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
{
|
|
|
- std::set<TRmgTemplateZoneId> visited;
|
|
|
-
|
|
|
- // Get all zones that have road connections
|
|
|
- std::set<TRmgTemplateZoneId> zonesWithRoads;
|
|
|
- for(const auto & entry : roadGraph)
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
{
|
|
|
- if(!entry.second.empty())
|
|
|
+ if(connection.getId() == id)
|
|
|
{
|
|
|
- zonesWithRoads.insert(entry.first);
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- if(!zonesWithRoads.empty())
|
|
|
+ if(found) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update trueRoads map
|
|
|
+ trueRoads[zoneA].insert(zoneB);
|
|
|
+ trueRoads[zoneB].insert(zoneA);
|
|
|
+
|
|
|
+ // If either is a town zone, note it specially in the log
|
|
|
+ auto townZoneId = vstd::contains(zonesWithTowns, zoneA) ? zoneA : zoneB;
|
|
|
+ auto otherZoneId = vstd::contains(zonesWithTowns, zoneA) ? zoneB : zoneA;
|
|
|
+
|
|
|
+ logGlobal->info("Setting RANDOM road to TRUE for connection %d - only connection for isolated town zone %d",
|
|
|
+ id, townZoneId);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If one zone has a town and one doesn't
|
|
|
+ TRmgTemplateZoneId townZone = vstd::contains(zonesWithTowns, zoneA) ? zoneA :
|
|
|
+ vstd::contains(zonesWithTowns, zoneB) ? zoneB : 0;
|
|
|
+ TRmgTemplateZoneId nonTownZone = vstd::contains(zonesWithTowns, zoneA) ? zoneB :
|
|
|
+ vstd::contains(zonesWithTowns, zoneB) ? zoneA : 0;
|
|
|
+
|
|
|
+ if(townZone && nonTownZone)
|
|
|
+ {
|
|
|
+ // See if this non-town zone can connect to any other town
|
|
|
+ std::set<TRmgTemplateZoneId> possibleConnections;
|
|
|
+
|
|
|
+ for(auto & otherZonePtr : zones)
|
|
|
+ {
|
|
|
+ if(otherZonePtr.first == nonTownZone) continue;
|
|
|
+
|
|
|
+ // Check if there's a possible connection to another zone
|
|
|
+ if(vstd::contains(existingConnections, nonTownZone) &&
|
|
|
+ vstd::contains(existingConnections[nonTownZone], otherZonePtr.first))
|
|
|
{
|
|
|
- //Start DFS from any zone that has roads
|
|
|
- TRmgTemplateZoneId startZone = *zonesWithRoads.begin();
|
|
|
-
|
|
|
- std::stack<TRmgTemplateZoneId> stack;
|
|
|
- stack.push(startZone);
|
|
|
-
|
|
|
- while(!stack.empty())
|
|
|
+ // If this other zone is a town or can reach other towns, note it
|
|
|
+ if(vstd::contains(zonesWithTowns, otherZonePtr.first) && otherZonePtr.first != townZone)
|
|
|
{
|
|
|
- auto current = stack.top();
|
|
|
- stack.pop();
|
|
|
-
|
|
|
- if(vstd::contains(visited, current))
|
|
|
- continue;
|
|
|
-
|
|
|
- visited.insert(current);
|
|
|
-
|
|
|
- for(auto & neighbor : roadGraph[current])
|
|
|
+ possibleConnections.insert(otherZonePtr.first);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!possibleConnections.empty())
|
|
|
+ {
|
|
|
+ // This non-town zone can potentially connect to other towns
|
|
|
+ // Check if any of these towns aren't already connected to our town
|
|
|
+ bool canCreateNewConnection = false;
|
|
|
+
|
|
|
+ for(auto otherTown : possibleConnections)
|
|
|
+ {
|
|
|
+ if(findSet(parent, townZone) != findSet(parent, otherTown))
|
|
|
+ {
|
|
|
+ canCreateNewConnection = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set road to TRUE if it could create a new connection
|
|
|
+ // This ensures isolated town zones get connected through non-town zones
|
|
|
+ bool found = false;
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ if(connection.getId() == id)
|
|
|
{
|
|
|
- if(!vstd::contains(visited, neighbor.first))
|
|
|
+ if(canCreateNewConnection)
|
|
|
+ {
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
|
|
+ logGlobal->info("Setting RANDOM road to TRUE for connection %d - potential path between town zones", id);
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- stack.push(neighbor.first);
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
|
|
+ logGlobal->info("Setting RANDOM road to FALSE for connection %d - won't create new town connections", id);
|
|
|
}
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- //Check if all zones with roads are still reachable
|
|
|
- for(auto zoneId : zonesWithRoads)
|
|
|
+ if(found) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(canCreateNewConnection)
|
|
|
+ {
|
|
|
+ // Update trueRoads map
|
|
|
+ trueRoads[zoneA].insert(zoneB);
|
|
|
+ trueRoads[zoneB].insert(zoneA);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // This is a dead-end connection to a town zone - keep for connectivity
|
|
|
+ bool found = false;
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
{
|
|
|
- if(!vstd::contains(visited, zoneId))
|
|
|
+ if(connection.getId() == id)
|
|
|
{
|
|
|
- canRemove = false;
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
|
|
+ found = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+ if(found) break;
|
|
|
}
|
|
|
+
|
|
|
+ // Update trueRoads map
|
|
|
+ trueRoads[zoneA].insert(zoneB);
|
|
|
+ trueRoads[zoneB].insert(zoneA);
|
|
|
+
|
|
|
+ logGlobal->info("Setting RANDOM road to TRUE for connection %d - road to town zone %d",
|
|
|
+ id, townZone);
|
|
|
}
|
|
|
-
|
|
|
- if(!canRemove)
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // This is a connection between non-town zones
|
|
|
+ // Set it to FALSE by default
|
|
|
+ bool found = false;
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
{
|
|
|
- //Restore connection and try next one
|
|
|
- roadGraph[zoneA][zoneB]++;
|
|
|
- roadGraph[zoneB][zoneA]++;
|
|
|
- continue;
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ if(connection.getId() == id)
|
|
|
+ {
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(found) break;
|
|
|
}
|
|
|
-
|
|
|
- //Found a connection we can remove - update in both zones that contain it
|
|
|
- auto & zonePtr = zones[zoneA];
|
|
|
- zonePtr->setRoadOption(conn.getId(), rmg::ERoadOption::ROAD_FALSE);
|
|
|
-
|
|
|
- auto & otherZonePtr = zones[zoneB];
|
|
|
- otherZonePtr->setRoadOption(conn.getId(), rmg::ERoadOption::ROAD_FALSE);
|
|
|
-
|
|
|
- logGlobal->info("Dropped random road between %d and %d", zoneA, zoneB);
|
|
|
- anyDropped = true;
|
|
|
- break; //Exit loop and rebuild graph
|
|
|
+
|
|
|
+ logGlobal->info("Setting RANDOM road to FALSE for connection %d between non-town zones %d and %d",
|
|
|
+ id, zoneA, zoneB);
|
|
|
}
|
|
|
- } while(anyDropped);
|
|
|
-
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update town connectivity after adding all roads
|
|
|
+ // Re-run BFS from each town zone through TRUE roads to see if we need more connections
|
|
|
+ std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> connectedTownGroups;
|
|
|
+
|
|
|
+ for(auto townZone : zonesWithTowns)
|
|
|
+ {
|
|
|
+ std::queue<TRmgTemplateZoneId> q;
|
|
|
+ std::set<TRmgTemplateZoneId> visited;
|
|
|
+
|
|
|
+ q.push(townZone);
|
|
|
+ visited.insert(townZone);
|
|
|
+
|
|
|
+ while(!q.empty())
|
|
|
+ {
|
|
|
+ auto current = q.front();
|
|
|
+ q.pop();
|
|
|
+
|
|
|
+ // Check all TRUE road neighbors
|
|
|
+ if(vstd::contains(trueRoads, current))
|
|
|
+ {
|
|
|
+ for(auto neighbor : trueRoads[current])
|
|
|
+ {
|
|
|
+ if(visited.find(neighbor) == visited.end())
|
|
|
+ {
|
|
|
+ visited.insert(neighbor);
|
|
|
+ q.push(neighbor);
|
|
|
+
|
|
|
+ // If this is another town, add to connected group
|
|
|
+ if(vstd::contains(zonesWithTowns, neighbor))
|
|
|
+ {
|
|
|
+ connectedTownGroups[townZone].insert(neighbor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // Mark all remaining random connections as TRUE
|
|
|
+ // Set any remaining RANDOM roads to FALSE
|
|
|
for(auto & zonePtr : zones)
|
|
|
{
|
|
|
for(auto & connection : zonePtr.second->getConnections())
|
|
|
- {
|
|
|
+ {
|
|
|
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
|
|
{
|
|
|
auto id = connection.getId();
|
|
|
- zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
|
|
+ zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
|
|
+ logGlobal->info("Setting remaining RANDOM road to FALSE for connection %d", id);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // Log the final town connectivity information
|
|
|
+ logGlobal->info("======== Town Connectivity Report ========");
|
|
|
+ for(auto townZone : zonesWithTowns)
|
|
|
+ {
|
|
|
+ std::string connections;
|
|
|
+
|
|
|
+ // Find all adjacent zones connected by roads
|
|
|
+ std::set<TRmgTemplateZoneId> adjacentConnections;
|
|
|
+
|
|
|
+ for(auto & zonePtr : zones)
|
|
|
+ {
|
|
|
+ if(zonePtr.first == townZone)
|
|
|
+ {
|
|
|
+ for(auto & connection : zonePtr.second->getConnections())
|
|
|
+ {
|
|
|
+ if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
|
|
+ {
|
|
|
+ auto otherZoneId = connection.getOtherZoneId(townZone);
|
|
|
+ adjacentConnections.insert(otherZoneId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!adjacentConnections.empty())
|
|
|
+ {
|
|
|
+ for(auto connectedZone : adjacentConnections)
|
|
|
+ {
|
|
|
+ if(!connections.empty())
|
|
|
+ connections += ", ";
|
|
|
+ connections += std::to_string(connectedZone);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(connections.empty())
|
|
|
+ connections = "None (no road connections)";
|
|
|
+
|
|
|
+ logGlobal->info("Zone %d with town connected to adjacent zones: %s", townZone, connections);
|
|
|
+ }
|
|
|
+
|
|
|
+ logGlobal->info("Finished road generation - created minimal spanning tree connecting all towns");
|
|
|
}
|
|
|
|
|
|
const TDistanceMap& CZonePlacer::getDistanceMap()
|