1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

Fix connections not getting actually updated

This commit is contained in:
Tomasz Zieliński
2025-03-14 14:37:33 +01:00
parent f0971dfdd6
commit 11ecb05bca
5 changed files with 155 additions and 120 deletions

View File

@@ -290,7 +290,7 @@
{ "a" : "16", "b" : "25", "guard" : 6000, "road" : "random" },
{ "a" : "9", "b" : "10", "guard" : 6000, "road" : "true" },
{ "a" : "9", "b" : "21", "guard" : 6000, "road" : "random" },
{ "a" : "9", "b" : "21", "guard" : 6000, "road" : "false" },
{ "a" : "9", "b" : "23", "guard" : 6000, "road" : "false" },
{ "a" : "10", "b" : "22", "guard" : 6000, "road" : "random" },
{ "a" : "10", "b" : "23", "guard" : 6000, "road" : "random" },
@@ -298,7 +298,7 @@
{ "a" : "13", "b" : "24", "guard" : 6000, "road" : "random" },
{ "a" : "13", "b" : "14", "guard" : 6000, "road" : "true" },
{ "a" : "14", "b" : "23", "guard" : 6000, "road" : "false" },
{ "a" : "14", "b" : "25", "guard" : 6000, "road" : "random" },
{ "a" : "14", "b" : "25", "guard" : 6000, "road" : "false" },
{ "a" : "11", "b" : "21", "guard" : 6000, "road" : "true" },
{ "a" : "11", "b" : "24", "guard" : 6000, "road" : "random" },

View File

@@ -302,6 +302,28 @@ std::vector<ZoneConnection> ZoneOptions::getConnections() const
return connectionDetails;
}
std::vector<ZoneConnection>& ZoneOptions::getConnectionsRef()
{
return connectionDetails;
}
void ZoneOptions::setRoadOption(int connectionId, rmg::ERoadOption roadOption)
{
for(auto & connection : connectionDetails)
{
if(connection.getId() == connectionId)
{
connection.setRoadOption(roadOption);
logGlobal->info("Set road option for connection %d between zones %d and %d to %s",
connectionId, connection.getZoneA(), connection.getZoneB(),
roadOption == rmg::ERoadOption::ROAD_TRUE ? "true" :
roadOption == rmg::ERoadOption::ROAD_FALSE ? "false" : "random");
return;
}
}
logGlobal->warn("Failed to find connection with ID %d in zone %d", connectionId, id);
}
std::vector<TRmgTemplateZoneId> ZoneOptions::getConnectedZoneIds() const
{
return connectedZoneIds;
@@ -482,11 +504,21 @@ rmg::ERoadOption ZoneConnection::getRoadOption() const
return hasRoad;
}
void ZoneConnection::setRoadOption(rmg::ERoadOption roadOption)
{
hasRoad = roadOption;
}
bool operator==(const ZoneConnection & l, const ZoneConnection & r)
{
return l.id == r.id;
}
bool operator<(const ZoneConnection & l, const ZoneConnection & r)
{
return l.id < r.id;
}
void ZoneConnection::serializeJson(JsonSerializeFormat & handler)
{
static const std::vector<std::string> connectionTypes =

View File

@@ -105,10 +105,12 @@ public:
int getGuardStrength() const;
rmg::EConnectionType getConnectionType() const;
rmg::ERoadOption getRoadOption() const;
void setRoadOption(rmg::ERoadOption roadOption);
void serializeJson(JsonSerializeFormat & handler);
friend bool operator==(const ZoneConnection &, const ZoneConnection &);
friend bool operator<(const ZoneConnection &, const ZoneConnection &);
private:
int id;
TRmgTemplateZoneId zoneA;
@@ -185,8 +187,12 @@ public:
void addConnection(const ZoneConnection & connection);
std::vector<ZoneConnection> getConnections() const;
std::vector<ZoneConnection>& getConnectionsRef();
std::vector<TRmgTemplateZoneId> getConnectedZoneIds() const;
// Set road option for a specific connection by ID
void setRoadOption(int connectionId, rmg::ERoadOption roadOption);
void serializeJson(JsonSerializeFormat & handler);
EMonsterStrength::EMonsterStrength monsterStrength;

View File

@@ -1008,13 +1008,15 @@ void CZonePlacer::assignZones(vstd::RNG * rand)
void CZonePlacer::dropRandomRoads(vstd::RNG * rand)
{
auto zones = map.getZones();
bool anyDropped;
//First, build a graph of road connections
do
{
anyDropped = false;
std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> roadGraph;
std::vector<rmg::ZoneConnection> randomConnections;
std::vector<rmg::ZoneConnection> fixedConnections;
std::set<rmg::ZoneConnection> randomConnections;
//Collect all road connections and build initial graph
//Build graph and collect random connections
for(const auto & zone : zones)
{
for(const auto & connection : zone.second->getConnections())
@@ -1023,84 +1025,61 @@ void CZonePlacer::dropRandomRoads(vstd::RNG * rand)
{
roadGraph[connection.getZoneA()].insert(connection.getZoneB());
roadGraph[connection.getZoneB()].insert(connection.getZoneA());
fixedConnections.push_back(connection);
}
else if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
{
roadGraph[connection.getZoneA()].insert(connection.getZoneB());
roadGraph[connection.getZoneB()].insert(connection.getZoneA());
randomConnections.push_back(connection);
}
randomConnections.insert(connection);
}
// ROAD_FALSE connections are ignored
}
}
logGlobal->info("Remaining random connections: %d", randomConnections.size());
//Find all connected components in the initial graph
std::map<TRmgTemplateZoneId, int> zoneToComponent;
int numComponents = 0;
if(randomConnections.empty())
break;
auto dfsComponent = [&](TRmgTemplateZoneId start, int component)
//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)
{
std::stack<TRmgTemplateZoneId> stack;
stack.push(start);
auto zoneA = conn.getZoneA();
auto zoneB = conn.getZoneB();
while(!stack.empty())
//Check if either zone would become isolated by removing this connection
if(roadGraph[zoneA].size() <= 1 || roadGraph[zoneB].size() <= 1)
{
auto current = stack.top();
stack.pop();
if(zoneToComponent.find(current) != zoneToComponent.end())
//Can't remove this connection as it would isolate a zone
continue;
zoneToComponent[current] = component;
for(auto neighbor : roadGraph[current])
{
if(zoneToComponent.find(neighbor) == zoneToComponent.end())
{
stack.push(neighbor);
}
}
}
};
//Find all components
for(const auto & zone : zones)
{
if(zoneToComponent.find(zone.first) == zoneToComponent.end() && !roadGraph[zone.first].empty())
{
dfsComponent(zone.first, numComponents++);
}
}
//Process each component separately
for(int component = 0; component < numComponents; component++)
{
//Get random connections for this component
std::vector<rmg::ZoneConnection> componentRandomConnections;
for(const auto & conn : randomConnections)
{
if(zoneToComponent[conn.getZoneA()] == component)
{
componentRandomConnections.push_back(conn);
}
}
//Shuffle random connections
RandomGeneratorUtil::randomShuffle(componentRandomConnections, *rand);
//Try removing each random connection
for(const auto & conn : componentRandomConnections)
{
//Temporarily remove this connection
roadGraph[conn.getZoneA()].erase(conn.getZoneB());
roadGraph[conn.getZoneB()].erase(conn.getZoneA());
roadGraph[zoneA].erase(zoneB);
roadGraph[zoneB].erase(zoneA);
//Check if graph remains connected
//Check if graph remains connected as a whole
bool canRemove = true;
std::set<TRmgTemplateZoneId> visited;
//Start DFS from any zone in this component
auto startZone = conn.getZoneA();
// Get all zones that have road connections
std::set<TRmgTemplateZoneId> zonesWithRoads;
for(const auto & entry : roadGraph)
{
if(!entry.second.empty())
{
zonesWithRoads.insert(entry.first);
}
}
if(!zonesWithRoads.empty())
{
//Start DFS from any zone that has roads
TRmgTemplateZoneId startZone = *zonesWithRoads.begin();
std::stack<TRmgTemplateZoneId> stack;
stack.push(startZone);
@@ -1123,12 +1102,10 @@ void CZonePlacer::dropRandomRoads(vstd::RNG * rand)
}
}
//Check if all zones in this component are still reachable
for(const auto & zone : zones)
//Check if all zones with roads are still reachable
for(auto zoneId : zonesWithRoads)
{
if(zoneToComponent[zone.first] == component && !roadGraph[zone.first].empty())
{
if(visited.find(zone.first) == visited.end())
if(visited.find(zoneId) == visited.end())
{
canRemove = false;
break;
@@ -1138,24 +1115,44 @@ void CZonePlacer::dropRandomRoads(vstd::RNG * rand)
if(!canRemove)
{
//Restore connection if removing it would break connectivity
roadGraph[conn.getZoneA()].insert(conn.getZoneB());
roadGraph[conn.getZoneB()].insert(conn.getZoneA());
//Restore connection and try next one
roadGraph[zoneA].insert(zoneB);
roadGraph[zoneB].insert(zoneA);
continue;
}
else
{
//Mark this connection as having no road
//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
}
} while(anyDropped);
// Use a set to track processed connection IDs to avoid duplicates
std::set<int> processedConnectionIds;
// Process each zone's connections
for(auto & zonePtr : zones)
{
for(auto & connection : zonePtr.second->getConnections())
{
if(connection.getId() == conn.getId())
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
{
const_cast<rmg::ZoneConnection&>(connection) = conn; //FIXME: avoid const_cast
break;
}
}
}
auto id = connection.getId();
// Only process each connection once
if(vstd::contains(processedConnectionIds, id))
continue;
processedConnectionIds.insert(id);
// Use the new setRoadOption method
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
}
}
}

View File

@@ -110,16 +110,12 @@ void ConnectionsPlacer::init()
POSTFUNCTION(RoadPlacer);
POSTFUNCTION(ObjectManager);
auto id = zone.getId();
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
{
// Only consider connected zones
if (c.getZoneA() == id || c.getZoneB() == id)
// FIXME: Use zones modified by CZonePlacer
for (auto c : zone.getConnections())
{
addConnection(c);
}
}
}
void ConnectionsPlacer::addConnection(const rmg::ZoneConnection& connection)
{
@@ -477,6 +473,10 @@ void ConnectionsPlacer::collectNeighbourZones()
bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection) const
{
if (connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
logGlobal->error("Random road between zones %d and %d", connection.getZoneA(), connection.getZoneB());
else
logGlobal->info("Should generate road between zones %d and %d: %d", connection.getZoneA(), connection.getZoneB(), connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE);
return connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE;
}