mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
+ Handle Wide Connections
+ Possibly hide fictive and repulsive connections, needs testing
This commit is contained in:
parent
02ea798c97
commit
65d10cf9f2
@ -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)
|
||||
{
|
||||
//TODO: Access information about connection type
|
||||
if (connection.getConnectionType() == EConnectionType::EConnectionType::REPULSIVE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto neighbor = connection.getOtherZoneId(current);
|
||||
if (!visited[neighbor])
|
||||
{
|
||||
visited[neighbor] = true;
|
||||
@ -132,7 +138,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 +186,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;
|
||||
@ -521,9 +527,14 @@ 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));
|
||||
|
||||
@ -601,6 +612,24 @@ 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;
|
||||
forceVector -= localForce * (distancesBetweenZones[zone.second->getId()][otherZone->getId()]);
|
||||
}
|
||||
}
|
||||
|
||||
overlaps[zone.second] = overlap;
|
||||
forceVector.z = 0; //operator - doesn't preserve z coordinate :/
|
||||
forces[zone.second] = forceVector;
|
||||
@ -656,7 +685,7 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista
|
||||
//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(firstZone->getConnectedZoneIds(), misplacedZones[i].second->getId()))
|
||||
{
|
||||
secondZone = misplacedZones[i].second;
|
||||
break;
|
||||
@ -688,7 +717,7 @@ void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDista
|
||||
//Move one zone towards most distant zone to reduce distance
|
||||
|
||||
float maxDistance = 0;
|
||||
for (auto con : misplacedZone->getConnections())
|
||||
for (auto con : misplacedZone->getConnectedZoneIds())
|
||||
{
|
||||
auto otherZone = zones[con];
|
||||
float distance = static_cast<float>(otherZone->getCenter().dist2dSQ(ourCenter));
|
||||
|
@ -78,7 +78,7 @@ void ConnectionsPlacer::init()
|
||||
POSTFUNCTION(RoadPlacer);
|
||||
POSTFUNCTION(ObjectManager);
|
||||
|
||||
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnections())
|
||||
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
|
||||
addConnection(c);
|
||||
}
|
||||
|
||||
@ -107,8 +107,58 @@ 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);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float maxDist = -10e6;
|
||||
if(!directProhibited && directConnectionIterator != dNeighbourZones.end())
|
||||
if(!success && !directProhibited && directConnectionIterator != dNeighbourZones.end())
|
||||
{
|
||||
int3 guardPos(-1, -1, -1);
|
||||
int3 roadNode;
|
||||
@ -333,6 +383,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, &connection, &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())
|
||||
{
|
||||
|
@ -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