mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge pull request #2237 from vcmi/fictive_connections
Wide, fictive, repulsive connections
This commit is contained in:
commit
a560eaea51
@ -171,7 +171,7 @@
|
||||
},
|
||||
"connections" :
|
||||
[
|
||||
{ "a" : "1", "b" : "2", "guard" : 0 },
|
||||
{ "a" : "1", "b" : "2", "type": "wide" },
|
||||
{ "a" : "1", "b" : "3", "guard" : 0 },
|
||||
{ "a" : "1", "b" : "4", "guard" : 0 },
|
||||
{ "a" : "1", "b" : "5", "guard" : 2000 },
|
||||
@ -183,19 +183,19 @@
|
||||
{ "a" : "2", "b" : "3", "guard" : 0 },
|
||||
{ "a" : "2", "b" : "4", "guard" : 0 },
|
||||
{ "a" : "2", "b" : "5", "guard" : 2000 },
|
||||
{ "a" : "5", "b" : "6", "guard" : 0 },
|
||||
{ "a" : "5", "b" : "6", "type": "wide" },
|
||||
{ "a" : "5", "b" : "7", "guard" : 0 },
|
||||
{ "a" : "5", "b" : "8", "guard" : 0 },
|
||||
{ "a" : "6", "b" : "7", "guard" : 0 },
|
||||
{ "a" : "6", "b" : "8", "guard" : 0 },
|
||||
{ "a" : "6", "b" : "9", "guard" : 2000 },
|
||||
{ "a" : "9", "b" : "10", "guard" : 0 },
|
||||
{ "a" : "9", "b" : "10", "type": "wide" },
|
||||
{ "a" : "9", "b" : "11", "guard" : 0 },
|
||||
{ "a" : "9", "b" : "12", "guard" : 0 },
|
||||
{ "a" : "10", "b" : "11", "guard" : 0 },
|
||||
{ "a" : "10", "b" : "12", "guard" : 0 },
|
||||
{ "a" : "10", "b" : "13", "guard" : 2000 },
|
||||
{ "a" : "13", "b" : "14", "guard" : 0 },
|
||||
{ "a" : "13", "b" : "14", "type": "wide" },
|
||||
{ "a" : "13", "b" : "15", "guard" : 0 },
|
||||
{ "a" : "13", "b" : "16", "guard" : 0 },
|
||||
{ "a" : "14", "b" : "15", "guard" : 0 },
|
||||
|
@ -93,11 +93,11 @@
|
||||
"connections" :
|
||||
[
|
||||
{ "a" : "1", "b" : "3", "guard" : 6000 },
|
||||
{ "a" : "1", "b" : "5", "guard" : 0 },
|
||||
{ "a" : "1", "b" : "7", "guard" : 0 },
|
||||
{ "a" : "1", "b" : "5", "type": "wide" },
|
||||
{ "a" : "1", "b" : "7", "type": "wide" },
|
||||
{ "a" : "2", "b" : "4", "guard" : 6000 },
|
||||
{ "a" : "2", "b" : "6", "guard" : 0 },
|
||||
{ "a" : "2", "b" : "8", "guard" : 0 },
|
||||
{ "a" : "2", "b" : "6", "type": "wide" },
|
||||
{ "a" : "2", "b" : "8", "type": "wide" },
|
||||
{ "a" : "5", "b" : "6", "guard" : 6000 },
|
||||
{ "a" : "7", "b" : "8", "guard" : 6000 }
|
||||
]
|
||||
|
@ -299,8 +299,8 @@
|
||||
{ "a" : "18", "b" : "22", "guard" : 9000 },
|
||||
{ "a" : "19", "b" : "24", "guard" : 9000 },
|
||||
{ "a" : "20", "b" : "25", "guard" : 9000 },
|
||||
{ "a" : "21", "b" : "22", "guard" : 0 },
|
||||
{ "a" : "24", "b" : "25", "guard" : 0 }
|
||||
{ "a" : "21", "b" : "22", "type": "wide" },
|
||||
{ "a" : "24", "b" : "25", "type": "wide" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -310,8 +310,8 @@
|
||||
{ "a" : "19", "b" : "24", "guard" : 9000 },
|
||||
{ "a" : "20", "b" : "25", "guard" : 9000 },
|
||||
|
||||
{ "a" : "21", "b" : "22", "guard" : 0 },
|
||||
{ "a" : "24", "b" : "25", "guard" : 0 }
|
||||
{ "a" : "21", "b" : "22", "type": "wide" },
|
||||
{ "a" : "24", "b" : "25", "type": "wide" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -159,10 +159,10 @@
|
||||
{ "a" : "8", "b" : "9", "guard" : 3000 },
|
||||
{ "a" : "8", "b" : "11", "guard" : 6000 },
|
||||
{ "a" : "8", "b" : "12", "guard" : 3000 },
|
||||
{ "a" : "9", "b" : "10", "guard" : 0 },
|
||||
{ "a" : "9", "b" : "12", "guard" : 0 },
|
||||
{ "a" : "10", "b" : "13", "guard" : 0 },
|
||||
{ "a" : "12", "b" : "13", "guard" : 0 }
|
||||
{ "a" : "9", "b" : "10", "type": "wide" },
|
||||
{ "a" : "9", "b" : "12", "type": "wide" },
|
||||
{ "a" : "10", "b" : "13", "type": "wide" },
|
||||
{ "a" : "12", "b" : "13", "type": "wide" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <vstd/ContainerUtils.h>
|
||||
#include <boost/bimap.hpp>
|
||||
#include "CRmgTemplate.h"
|
||||
#include "Functions.h"
|
||||
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CTownHandler.h"
|
||||
@ -285,14 +286,20 @@ TRmgTemplateZoneId ZoneOptions::getTreasureLikeZone() const
|
||||
return treasureLikeZone;
|
||||
}
|
||||
|
||||
void ZoneOptions::addConnection(TRmgTemplateZoneId otherZone)
|
||||
void ZoneOptions::addConnection(const ZoneConnection & connection)
|
||||
{
|
||||
connections.push_back (otherZone);
|
||||
connectedZoneIds.push_back(connection.getOtherZoneId(getId()));
|
||||
connectionDetails.push_back(connection);
|
||||
}
|
||||
|
||||
std::vector<TRmgTemplateZoneId> ZoneOptions::getConnections() const
|
||||
std::vector<ZoneConnection> ZoneOptions::getConnections() const
|
||||
{
|
||||
return connections;
|
||||
return connectionDetails;
|
||||
}
|
||||
|
||||
std::vector<TRmgTemplateZoneId> ZoneOptions::getConnectedZoneIds() const
|
||||
{
|
||||
return connectedZoneIds;
|
||||
}
|
||||
|
||||
bool ZoneOptions::areTownsSameType() const
|
||||
@ -429,7 +436,8 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
|
||||
ZoneConnection::ZoneConnection()
|
||||
: zoneA(-1),
|
||||
zoneB(-1),
|
||||
guardStrength(0)
|
||||
guardStrength(0),
|
||||
connectionType(EConnectionType::EConnectionType::GUARDED)
|
||||
{
|
||||
|
||||
}
|
||||
@ -444,10 +452,31 @@ TRmgTemplateZoneId ZoneConnection::getZoneB() const
|
||||
return zoneB;
|
||||
}
|
||||
|
||||
TRmgTemplateZoneId ZoneConnection::getOtherZoneId(TRmgTemplateZoneId id) const
|
||||
{
|
||||
if (id == zoneA)
|
||||
{
|
||||
return zoneB;
|
||||
}
|
||||
else if (id == zoneB)
|
||||
{
|
||||
return zoneA;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw rmgException("Zone does not belong to this connection");
|
||||
}
|
||||
}
|
||||
|
||||
int ZoneConnection::getGuardStrength() const
|
||||
{
|
||||
return guardStrength;
|
||||
}
|
||||
|
||||
EConnectionType::EConnectionType ZoneConnection::getConnectionType() const
|
||||
{
|
||||
return connectionType;
|
||||
}
|
||||
|
||||
bool operator==(const ZoneConnection & l, const ZoneConnection & r)
|
||||
{
|
||||
@ -456,9 +485,18 @@ bool operator==(const ZoneConnection & l, const ZoneConnection & r)
|
||||
|
||||
void ZoneConnection::serializeJson(JsonSerializeFormat & handler)
|
||||
{
|
||||
static const std::vector<std::string> connectionTypes =
|
||||
{
|
||||
"guarded",
|
||||
"fictive",
|
||||
"repulsive",
|
||||
"wide"
|
||||
};
|
||||
|
||||
handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("a", zoneA, -1);
|
||||
handler.serializeId<TRmgTemplateZoneId, TRmgTemplateZoneId, ZoneEncoder>("b", zoneB, -1);
|
||||
handler.serializeInt("guard", guardStrength, 0);
|
||||
handler.serializeEnum("type", connectionType, connectionTypes);
|
||||
}
|
||||
|
||||
}
|
||||
@ -526,9 +564,9 @@ const CRmgTemplate::Zones & CRmgTemplate::getZones() const
|
||||
return zones;
|
||||
}
|
||||
|
||||
const std::vector<ZoneConnection> & CRmgTemplate::getConnections() const
|
||||
const std::vector<ZoneConnection> & CRmgTemplate::getConnectedZoneIds() const
|
||||
{
|
||||
return connections;
|
||||
return connectedZoneIds;
|
||||
}
|
||||
|
||||
void CRmgTemplate::validate() const
|
||||
@ -644,7 +682,7 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
|
||||
|
||||
{
|
||||
auto connectionsData = handler.enterArray("connections");
|
||||
connectionsData.serializeStruct(connections);
|
||||
connectionsData.serializeStruct(connectedZoneIds);
|
||||
}
|
||||
|
||||
{
|
||||
@ -759,16 +797,18 @@ void CRmgTemplate::afterLoad()
|
||||
inheritTreasureInfo(zone);
|
||||
}
|
||||
|
||||
for(const auto & connection : connections)
|
||||
for(const auto & connection : connectedZoneIds)
|
||||
{
|
||||
//TODO: Remember connection details and allow to access them from anywhere
|
||||
|
||||
auto id1 = connection.getZoneA();
|
||||
auto id2 = connection.getZoneB();
|
||||
|
||||
auto zone1 = zones.at(id1);
|
||||
auto zone2 = zones.at(id2);
|
||||
|
||||
zone1->addConnection(id2);
|
||||
zone2->addConnection(id1);
|
||||
zone1->addConnection(connection);
|
||||
zone2->addConnection(connection);
|
||||
}
|
||||
|
||||
if(allowedWaterContent.empty() || allowedWaterContent.count(EWaterContent::RANDOM))
|
||||
|
@ -67,17 +67,31 @@ public:
|
||||
void serializeJson(JsonSerializeFormat & handler);
|
||||
};
|
||||
|
||||
namespace EConnectionType
|
||||
{
|
||||
enum class EConnectionType
|
||||
{
|
||||
GUARDED = 0, //default
|
||||
FICTIVE,
|
||||
REPULSIVE,
|
||||
WIDE
|
||||
};
|
||||
}
|
||||
|
||||
namespace rmg
|
||||
{
|
||||
|
||||
class DLL_LINKAGE ZoneConnection
|
||||
{
|
||||
public:
|
||||
|
||||
ZoneConnection();
|
||||
|
||||
TRmgTemplateZoneId getZoneA() const;
|
||||
TRmgTemplateZoneId getZoneB() const;
|
||||
TRmgTemplateZoneId getOtherZoneId(TRmgTemplateZoneId id) const;
|
||||
int getGuardStrength() const;
|
||||
EConnectionType::EConnectionType getConnectionType() const;
|
||||
|
||||
void serializeJson(JsonSerializeFormat & handler);
|
||||
|
||||
@ -86,6 +100,7 @@ private:
|
||||
TRmgTemplateZoneId zoneA;
|
||||
TRmgTemplateZoneId zoneB;
|
||||
int guardStrength;
|
||||
EConnectionType::EConnectionType connectionType;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE ZoneOptions
|
||||
@ -149,8 +164,9 @@ public:
|
||||
TRmgTemplateZoneId getTerrainTypeLikeZone() const;
|
||||
TRmgTemplateZoneId getTreasureLikeZone() const;
|
||||
|
||||
void addConnection(TRmgTemplateZoneId otherZone);
|
||||
std::vector<TRmgTemplateZoneId> getConnections() const;
|
||||
void addConnection(const ZoneConnection & connection);
|
||||
std::vector<ZoneConnection> getConnections() const;
|
||||
std::vector<TRmgTemplateZoneId> getConnectedZoneIds() const;
|
||||
|
||||
void serializeJson(JsonSerializeFormat & handler);
|
||||
|
||||
@ -178,7 +194,8 @@ protected:
|
||||
|
||||
std::vector<CTreasureInfo> treasureInfo;
|
||||
|
||||
std::vector<TRmgTemplateZoneId> connections; //list of adjacent zones
|
||||
std::vector<TRmgTemplateZoneId> connectedZoneIds; //list of adjacent zone ids
|
||||
std::vector<ZoneConnection> connectionDetails; //list of connections linked to that zone
|
||||
|
||||
TRmgTemplateZoneId minesLikeZone;
|
||||
TRmgTemplateZoneId terrainTypeLikeZone;
|
||||
@ -223,7 +240,7 @@ public:
|
||||
const CPlayerCountRange & getCpuPlayers() const;
|
||||
std::pair<int3, int3> getMapSizes() const;
|
||||
const Zones & getZones() const;
|
||||
const std::vector<rmg::ZoneConnection> & getConnections() const;
|
||||
const std::vector<rmg::ZoneConnection> & getConnectedZoneIds() const;
|
||||
|
||||
void validate() const; /// Tests template on validity and throws exception on failure
|
||||
|
||||
@ -235,7 +252,7 @@ private:
|
||||
int3 minSize, maxSize;
|
||||
CPlayerCountRange players, cpuPlayers;
|
||||
Zones zones;
|
||||
std::vector<rmg::ZoneConnection> connections;
|
||||
std::vector<rmg::ZoneConnection> connectedZoneIds;
|
||||
std::set<EWaterContent::EWaterContent> allowedWaterContent;
|
||||
|
||||
void afterLoad();
|
||||
|
@ -71,10 +71,16 @@ void CZonePlacer::findPathsBetweenZones()
|
||||
q.pop();
|
||||
|
||||
const auto& currentZone = zones.at(current);
|
||||
const auto& connections = currentZone->getConnections();
|
||||
const auto& connectedZoneIds = currentZone->getConnections();
|
||||
|
||||
for (uint32_t neighbor : connections)
|
||||
for (auto & connection : connectedZoneIds)
|
||||
{
|
||||
if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE)
|
||||
{
|
||||
//Do not consider virtual connections for graph distance
|
||||
continue;
|
||||
}
|
||||
auto neighbor = connection.getOtherZoneId(current);
|
||||
if (!visited[neighbor])
|
||||
{
|
||||
visited[neighbor] = true;
|
||||
@ -98,7 +104,6 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
|
||||
GridType grid(boost::extents[gridSize][gridSize]);
|
||||
|
||||
TZoneVector zonesVector(zones.begin(), zones.end());
|
||||
RandomGeneratorUtil::randomShuffle(zonesVector, *rand);
|
||||
|
||||
//Place first zone
|
||||
|
||||
@ -132,7 +137,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
|
||||
{
|
||||
case ETemplateZoneType::PLAYER_START:
|
||||
case ETemplateZoneType::CPU_START:
|
||||
if (firstZone->getConnections().size() > 2)
|
||||
if (firstZone->getConnectedZoneIds().size() > 2)
|
||||
{
|
||||
getRandomEdge(x, y);
|
||||
}
|
||||
@ -180,7 +185,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
|
||||
for (size_t i = 1; i < zones.size(); i++)
|
||||
{
|
||||
auto zone = zonesVector[i].second;
|
||||
auto connections = zone->getConnections();
|
||||
auto connectedZoneIds = zone->getConnectedZoneIds();
|
||||
|
||||
float maxDistance = -1000.0;
|
||||
int3 mostDistantPlace;
|
||||
@ -220,16 +225,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
|
||||
localDistance = -potentialPos.dist2d(int3(existingX, existingY, 0));
|
||||
}
|
||||
|
||||
//Spread apart player starting zones
|
||||
if (zone->getOwner() && existingZone->getOwner()) //Players participate in game
|
||||
{
|
||||
int firstPlayer = zone->getOwner().value();
|
||||
int secondPlayer = existingZone->getOwner().value();
|
||||
|
||||
//Players with lower indexes (especially 1 and 2) will be placed further apart
|
||||
|
||||
localDistance *= (1.0f + (2.0f / (firstPlayer * secondPlayer)));
|
||||
}
|
||||
localDistance *= scaleForceBetweenZones(zone, existingZone);
|
||||
|
||||
distance += localDistance;
|
||||
}
|
||||
@ -287,6 +283,23 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
|
||||
}
|
||||
}
|
||||
|
||||
float CZonePlacer::scaleForceBetweenZones(const std::shared_ptr<Zone> zoneA, const std::shared_ptr<Zone> zoneB) const
|
||||
{
|
||||
if (zoneA->getOwner() && zoneB->getOwner()) //Players participate in game
|
||||
{
|
||||
int firstPlayer = zoneA->getOwner().value();
|
||||
int secondPlayer = zoneB->getOwner().value();
|
||||
|
||||
//Players with lower indexes (especially 1 and 2) will be placed further apart
|
||||
|
||||
return (1.0f + (2.0f / (firstPlayer * secondPlayer)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CZonePlacer::placeZones(CRandomGenerator * rand)
|
||||
{
|
||||
logGlobal->info("Starting zone placement");
|
||||
@ -521,13 +534,18 @@ void CZonePlacer::attractConnectedZones(TZoneMap & zones, TForceVector & forces,
|
||||
float3 pos = zone.second->getCenter();
|
||||
float totalDistance = 0;
|
||||
|
||||
for (auto con : zone.second->getConnections())
|
||||
for (const auto & connection : zone.second->getConnections())
|
||||
{
|
||||
auto otherZone = zones[con];
|
||||
if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto otherZone = zones[connection.getOtherZoneId(zone.second->getId())];
|
||||
float3 otherZoneCenter = otherZone->getCenter();
|
||||
auto distance = static_cast<float>(pos.dist2d(otherZoneCenter));
|
||||
|
||||
forceVector += (otherZoneCenter - pos) * distance * gravityConstant; //positive value
|
||||
forceVector += (otherZoneCenter - pos) * distance * gravityConstant * scaleForceBetweenZones(zone.second, otherZone); //positive value
|
||||
|
||||
//Attract zone centers always
|
||||
|
||||
@ -569,6 +587,7 @@ void CZonePlacer::separateOverlappingZones(TZoneMap &zones, TForceVector &forces
|
||||
{
|
||||
float3 localForce = (((otherZoneCenter - pos)*(minDistance / (distance ? distance : 1e-3f))) / getDistance(distance)) * stifness;
|
||||
//negative value
|
||||
localForce *= scaleForceBetweenZones(zone.second, otherZone.second);
|
||||
forceVector -= localForce * (distancesBetweenZones[zone.second->getId()][otherZone.second->getId()] / 2.0f);
|
||||
overlap += (minDistance - distance); //overlapping of small zones hurts us more
|
||||
}
|
||||
@ -601,6 +620,25 @@ void CZonePlacer::separateOverlappingZones(TZoneMap &zones, TForceVector &forces
|
||||
{
|
||||
pushAwayFromBoundary(pos.x, 1);
|
||||
}
|
||||
|
||||
//Always move repulsive zones away, no matter their distance
|
||||
//TODO: Consider z plane?
|
||||
for (auto& connection : zone.second->getConnections())
|
||||
{
|
||||
if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE)
|
||||
{
|
||||
auto & otherZone = zones[connection.getOtherZoneId(zone.second->getId())];
|
||||
float3 otherZoneCenter = otherZone->getCenter();
|
||||
|
||||
//TODO: Roll into lambda?
|
||||
auto distance = static_cast<float>(pos.dist2d(otherZoneCenter));
|
||||
float minDistance = (zone.second->getSize() + otherZone->getSize()) / mapSize;
|
||||
float3 localForce = (((otherZoneCenter - pos)*(minDistance / (distance ? distance : 1e-3f))) / getDistance(distance)) * stifness;
|
||||
localForce *= (distancesBetweenZones[zone.second->getId()][otherZone->getId()]);
|
||||
forceVector -= localForce * scaleForceBetweenZones(zone.second, otherZone);
|
||||
}
|
||||
}
|
||||
|
||||
overlaps[zone.second] = overlap;
|
||||
forceVector.z = 0; //operator - doesn't preserve z coordinate :/
|
||||
forces[zone.second] = forceVector;
|
||||
@ -609,7 +647,9 @@ void CZonePlacer::separateOverlappingZones(TZoneMap &zones, TForceVector &forces
|
||||
|
||||
void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDistanceVector& distances, TDistanceVector& overlaps)
|
||||
{
|
||||
const int maxDistanceMovementRatio = zones.size() * zones.size(); //The more zones, the greater total distance expected
|
||||
//The more zones, the greater total distance expected
|
||||
//Also, higher stiffness make expected movement lower
|
||||
const int maxDistanceMovementRatio = zones.size() * zones.size() * (stiffnessConstant / stifness);
|
||||
|
||||
typedef std::pair<float, std::shared_ptr<Zone>> Misplacement;
|
||||
std::vector<Misplacement> misplacedZones;
|
||||
@ -638,7 +678,7 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista
|
||||
|
||||
boost::sort(misplacedZones, [](const Misplacement& lhs, Misplacement& rhs)
|
||||
{
|
||||
return lhs.first > rhs.first; //Biggest first
|
||||
return lhs.first > rhs.first; //Largest dispalcement first
|
||||
});
|
||||
|
||||
logGlobal->trace("Worst misplacement/movement ratio: %3.2f", misplacedZones.front().first);
|
||||
@ -649,14 +689,24 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista
|
||||
|
||||
auto firstZone = misplacedZones.front().second;
|
||||
std::shared_ptr<Zone> secondZone;
|
||||
std::set<TRmgTemplateZoneId> connectedZones;
|
||||
for (const auto& connection : firstZone->getConnections())
|
||||
{
|
||||
//FIXME: Should we also exclude fictive connections?
|
||||
if (connection.getConnectionType() != EConnectionType::EConnectionType::REPULSIVE)
|
||||
{
|
||||
connectedZones.insert(connection.getOtherZoneId(firstZone->getId()));
|
||||
}
|
||||
}
|
||||
|
||||
auto level = firstZone->getCenter().z;
|
||||
for (size_t i = 1; i < misplacedZones.size(); i++)
|
||||
{
|
||||
//Only swap zones on the same level
|
||||
//Don't swap zones that should be connected (Jebus)
|
||||
|
||||
if (misplacedZones[i].second->getCenter().z == level &&
|
||||
!vstd::contains(firstZone->getConnections(), misplacedZones[i].second->getId()))
|
||||
!vstd::contains(connectedZones, misplacedZones[i].second->getId()))
|
||||
{
|
||||
secondZone = misplacedZones[i].second;
|
||||
break;
|
||||
@ -690,7 +740,12 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista
|
||||
float maxDistance = 0;
|
||||
for (auto con : misplacedZone->getConnections())
|
||||
{
|
||||
auto otherZone = zones[con];
|
||||
if (con.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto otherZone = zones[con.getOtherZoneId(misplacedZone->getId())];
|
||||
float distance = static_cast<float>(otherZone->getCenter().dist2dSQ(ourCenter));
|
||||
if (distance > maxDistance)
|
||||
{
|
||||
|
@ -40,6 +40,7 @@ public:
|
||||
void placeZones(CRandomGenerator * rand);
|
||||
void findPathsBetweenZones();
|
||||
void placeOnGrid(CRandomGenerator* rand);
|
||||
float scaleForceBetweenZones(const std::shared_ptr<Zone> zoneA, const std::shared_ptr<Zone> zoneB) const;
|
||||
void assignZones(CRandomGenerator * rand);
|
||||
|
||||
const TDistanceMap & getDistanceMap();
|
||||
|
@ -79,7 +79,7 @@ void ConnectionsPlacer::init()
|
||||
POSTFUNCTION(RoadPlacer);
|
||||
POSTFUNCTION(ObjectManager);
|
||||
|
||||
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnections())
|
||||
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
|
||||
addConnection(c);
|
||||
}
|
||||
|
||||
@ -108,8 +108,67 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
||||
|| vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType());
|
||||
auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
|
||||
|
||||
if (directConnectionIterator != dNeighbourZones.end())
|
||||
{
|
||||
if (connection.getConnectionType() == EConnectionType::EConnectionType::WIDE)
|
||||
{
|
||||
for (auto borderPos : directConnectionIterator->second)
|
||||
{
|
||||
//TODO: Refactor common code with direct connection
|
||||
int3 potentialPos = zone.areaPossible().nearest(borderPos);
|
||||
assert(borderPos != potentialPos);
|
||||
|
||||
auto safetyGap = rmg::Area({ potentialPos });
|
||||
safetyGap.unite(safetyGap.getBorderOutside());
|
||||
safetyGap.intersect(zone.areaPossible());
|
||||
if (!safetyGap.empty())
|
||||
{
|
||||
safetyGap.intersect(otherZone->areaPossible());
|
||||
if (safetyGap.empty())
|
||||
{
|
||||
rmg::Area border(zone.getArea().getBorder());
|
||||
border.unite(otherZone->getArea().getBorder());
|
||||
|
||||
auto costFunction = [&border](const int3& s, const int3& d)
|
||||
{
|
||||
return 1.f / (1.f + border.distanceSqr(d));
|
||||
};
|
||||
|
||||
auto ourArea = zone.areaPossible() + zone.freePaths();
|
||||
auto theirArea = otherZone->areaPossible() + otherZone->freePaths();
|
||||
theirArea.add(potentialPos);
|
||||
rmg::Path ourPath(ourArea);
|
||||
rmg::Path theirPath(theirArea);
|
||||
ourPath.connect(zone.freePaths());
|
||||
ourPath = ourPath.search(potentialPos, true, costFunction);
|
||||
theirPath.connect(otherZone->freePaths());
|
||||
theirPath = theirPath.search(potentialPos, true, costFunction);
|
||||
|
||||
if (ourPath.valid() && theirPath.valid())
|
||||
{
|
||||
zone.connectPath(ourPath);
|
||||
otherZone->connectPath(theirPath);
|
||||
otherZone->getModificator<ObjectManager>()->updateDistances(potentialPos);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (connection.getConnectionType() == EConnectionType::EConnectionType::FICTIVE ||
|
||||
connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE)
|
||||
{
|
||||
//Fictive or repulsive connections are not real, take no action
|
||||
dCompleted.push_back(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
float maxDist = -10e6;
|
||||
if(!directProhibited && directConnectionIterator != dNeighbourZones.end())
|
||||
if(!success && !directProhibited && directConnectionIterator != dNeighbourZones.end())
|
||||
{
|
||||
int3 guardPos(-1, -1, -1);
|
||||
int3 roadNode;
|
||||
@ -187,9 +246,12 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update distances from empty passage, too
|
||||
zone.areaPossible().erase(guardPos);
|
||||
zone.freePaths().add(guardPos);
|
||||
map.setOccupied(guardPos, ETileType::FREE);
|
||||
manager.updateDistances(guardPos);
|
||||
otherZone->getModificator<ObjectManager>()->updateDistances(guardPos);
|
||||
}
|
||||
|
||||
assert(zone.getModificator<RoadPlacer>());
|
||||
@ -334,6 +396,23 @@ void ConnectionsPlacer::createBorder()
|
||||
return map.isOnMap(tile) && map.getZones()[map.getZoneID(tile)]->getType() != ETemplateZoneType::WATER;
|
||||
});
|
||||
|
||||
//No border for wide connections
|
||||
for (auto& connection : zone.getConnections()) // We actually placed that connection already
|
||||
{
|
||||
auto otherZone = connection.getOtherZoneId(zone.getId());
|
||||
|
||||
if (connection.getConnectionType() == EConnectionType::EConnectionType::WIDE)
|
||||
{
|
||||
auto sharedBorder = borderArea.getSubarea([this, otherZone, &borderOutsideArea](const int3 & t)
|
||||
{
|
||||
auto tile = borderOutsideArea.nearest(t);
|
||||
return map.isOnMap(tile) && map.getZones()[map.getZoneID(tile)]->getId() == otherZone;
|
||||
});
|
||||
|
||||
blockBorder.subtract(sharedBorder);
|
||||
}
|
||||
};
|
||||
|
||||
Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again
|
||||
for(const auto & tile : blockBorder.getTilesVector())
|
||||
{
|
||||
|
@ -76,12 +76,28 @@ void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * n
|
||||
}
|
||||
|
||||
void ObjectManager::updateDistances(const rmg::Object & obj)
|
||||
{
|
||||
updateDistances([obj](const int3& tile) -> ui32
|
||||
{
|
||||
return obj.getArea().distanceSqr(tile); //optimization, only relative distance is interesting
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectManager::updateDistances(const int3 & pos)
|
||||
{
|
||||
updateDistances([pos](const int3& tile) -> ui32
|
||||
{
|
||||
return pos.dist2dSQ(tile); //optimization, only relative distance is interesting
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> distanceFunction)
|
||||
{
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
tilesByDistance.clear();
|
||||
for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles
|
||||
{
|
||||
ui32 d = obj.getArea().distanceSqr(tile); //optimization, only relative distance is interesting
|
||||
ui32 d = distanceFunction(tile);
|
||||
map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile)));
|
||||
tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ public:
|
||||
void placeObject(rmg::Object & object, bool guarded, bool updateDistance);
|
||||
|
||||
void updateDistances(const rmg::Object & obj);
|
||||
void updateDistances(const int3& pos);
|
||||
void updateDistances(std::function<ui32(const int3 & tile)> distanceFunction);
|
||||
void createDistancesPriorityQueue();
|
||||
|
||||
const rmg::Area & getVisitableArea() const;
|
||||
|
@ -404,7 +404,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
|
||||
//Seer huts with creatures or generic rewards
|
||||
|
||||
if(zone.getConnections().size()) //Unlikely, but...
|
||||
if(zone.getConnectedZoneIds().size()) //Unlikely, but...
|
||||
{
|
||||
auto * qap = zone.getModificator<QuestArtifactPlacer>();
|
||||
if(!qap)
|
||||
|
Loading…
Reference in New Issue
Block a user