mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-14 02:33:51 +02:00
Protect every access to zone tiles with a mutex
This commit is contained in:
parent
cfc4a26f55
commit
d8c93cb222
@ -418,7 +418,7 @@ void CMapGenerator::fillZones()
|
|||||||
}
|
}
|
||||||
auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand);
|
auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand);
|
||||||
|
|
||||||
map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand);
|
map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths()->getTiles(), rand);
|
||||||
|
|
||||||
logGlobal->info("Zones filled successfully");
|
logGlobal->info("Zones filled successfully");
|
||||||
|
|
||||||
|
@ -858,7 +858,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
auto moveZoneToCenterOfMass = [width, height](const std::shared_ptr<Zone> & zone) -> void
|
auto moveZoneToCenterOfMass = [width, height](const std::shared_ptr<Zone> & zone) -> void
|
||||||
{
|
{
|
||||||
int3 total(0, 0, 0);
|
int3 total(0, 0, 0);
|
||||||
auto tiles = zone->area().getTiles();
|
auto tiles = zone->area()->getTiles();
|
||||||
for(const auto & tile : tiles)
|
for(const auto & tile : tiles)
|
||||||
{
|
{
|
||||||
total += tile;
|
total += tile;
|
||||||
@ -892,14 +892,14 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
{
|
{
|
||||||
distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos())));
|
distances.emplace_back(zone.second, static_cast<float>(pos.dist2dSQ(zone.second->getPos())));
|
||||||
}
|
}
|
||||||
boost::min_element(distances, compareByDistance)->first->area().add(pos); //closest tile belongs to zone
|
boost::min_element(distances, compareByDistance)->first->area()->add(pos); //closest tile belongs to zone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto & zone : zones)
|
for(const auto & zone : zones)
|
||||||
{
|
{
|
||||||
if(zone.second->area().empty())
|
if(zone.second->area()->empty())
|
||||||
throw rmgException("Empty zone is generated, probably RMG template is inappropriate for map size");
|
throw rmgException("Empty zone is generated, probably RMG template is inappropriate for map size");
|
||||||
|
|
||||||
moveZoneToCenterOfMass(zone.second);
|
moveZoneToCenterOfMass(zone.second);
|
||||||
@ -948,14 +948,14 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
|
|
||||||
//Tile closest to vertex belongs to zone
|
//Tile closest to vertex belongs to zone
|
||||||
auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first;
|
auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first;
|
||||||
closestZone->area().add(pos);
|
closestZone->area()->add(pos);
|
||||||
map.setZoneID(pos, closestZone->getId());
|
map.setZoneID(pos, closestZone->getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto & zone : zonesOnLevel[level])
|
for(const auto & zone : zonesOnLevel[level])
|
||||||
{
|
{
|
||||||
if(zone.second->area().empty())
|
if(zone.second->area()->empty())
|
||||||
{
|
{
|
||||||
// FIXME: Some vertices are duplicated, but it's not a source of problem
|
// FIXME: Some vertices are duplicated, but it's not a source of problem
|
||||||
logGlobal->error("Zone %d at %s is empty, dumping Penrose tiling", zone.second->getId(), zone.second->getCenter().toString());
|
logGlobal->error("Zone %d at %s is empty, dumping Penrose tiling", zone.second->getId(), zone.second->getCenter().toString());
|
||||||
@ -981,12 +981,12 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
|||||||
{
|
{
|
||||||
auto discardTiles = collectDistantTiles(*zone.second, zone.second->getSize() + 1.f);
|
auto discardTiles = collectDistantTiles(*zone.second, zone.second->getSize() + 1.f);
|
||||||
for(const auto & t : discardTiles)
|
for(const auto & t : discardTiles)
|
||||||
zone.second->area().erase(t);
|
zone.second->area()->erase(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
//make sure that terrain inside zone is not a rock
|
//make sure that terrain inside zone is not a rock
|
||||||
|
|
||||||
auto v = zone.second->getArea().getTilesVector();
|
auto v = zone.second->area()->getTilesVector();
|
||||||
map.getMapProxy()->drawTerrain(*rand, v, ETerrainId::SUBTERRANEAN);
|
map.getMapProxy()->drawTerrain(*rand, v, ETerrainId::SUBTERRANEAN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
rmg::Tileset collectDistantTiles(const Zone& zone, int distance)
|
rmg::Tileset collectDistantTiles(const Zone& zone, int distance)
|
||||||
{
|
{
|
||||||
uint32_t distanceSq = distance * distance;
|
uint32_t distanceSq = distance * distance;
|
||||||
auto subarea = zone.getArea().getSubarea([&zone, distanceSq](const int3 & t)
|
|
||||||
|
auto subarea = zone.area()->getSubarea([&zone, distanceSq](const int3 & t)
|
||||||
{
|
{
|
||||||
return t.dist2dSQ(zone.getPos()) > distanceSq;
|
return t.dist2dSQ(zone.getPos()) > distanceSq;
|
||||||
});
|
});
|
||||||
|
@ -75,25 +75,44 @@ void Zone::setPos(const int3 &Pos)
|
|||||||
pos = Pos;
|
pos = Pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rmg::Area & Zone::getArea() const
|
ThreadSafeProxy<rmg::Area> Zone::area()
|
||||||
{
|
{
|
||||||
return dArea;
|
return ThreadSafeProxy<rmg::Area>(dArea, areaMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
rmg::Area & Zone::area()
|
ThreadSafeProxy<const rmg::Area> Zone::area() const
|
||||||
{
|
{
|
||||||
return dArea;
|
return ThreadSafeProxy<const rmg::Area>(dArea, areaMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
rmg::Area & Zone::areaPossible()
|
ThreadSafeProxy<rmg::Area> Zone::areaPossible()
|
||||||
{
|
{
|
||||||
//FIXME: make const, only modify via mutex-protected interface
|
return ThreadSafeProxy<rmg::Area>(dAreaPossible, areaMutex);
|
||||||
return dAreaPossible;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rmg::Area & Zone::areaUsed()
|
ThreadSafeProxy<const rmg::Area> Zone::areaPossible() const
|
||||||
{
|
{
|
||||||
return dAreaUsed;
|
return ThreadSafeProxy<const rmg::Area>(dAreaPossible, areaMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadSafeProxy<rmg::Area> Zone::freePaths()
|
||||||
|
{
|
||||||
|
return ThreadSafeProxy<rmg::Area>(dAreaFree, areaMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadSafeProxy<const rmg::Area> Zone::freePaths() const
|
||||||
|
{
|
||||||
|
return ThreadSafeProxy<const rmg::Area>(dAreaFree, areaMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadSafeProxy<rmg::Area> Zone::areaUsed()
|
||||||
|
{
|
||||||
|
return ThreadSafeProxy<rmg::Area>(dAreaUsed, areaMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadSafeProxy<const rmg::Area> Zone::areaUsed() const
|
||||||
|
{
|
||||||
|
return ThreadSafeProxy<const rmg::Area>(dAreaUsed, areaMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Zone::clearTiles()
|
void Zone::clearTiles()
|
||||||
@ -122,11 +141,6 @@ void Zone::initFreeTiles()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rmg::Area & Zone::freePaths()
|
|
||||||
{
|
|
||||||
return dAreaFree;
|
|
||||||
}
|
|
||||||
|
|
||||||
FactionID Zone::getTownType() const
|
FactionID Zone::getTownType() const
|
||||||
{
|
{
|
||||||
return townType;
|
return townType;
|
||||||
@ -225,18 +239,16 @@ TModificators Zone::getModificators()
|
|||||||
void Zone::connectPath(const rmg::Path & path)
|
void Zone::connectPath(const rmg::Path & path)
|
||||||
///connect current tile to any other free tile within zone
|
///connect current tile to any other free tile within zone
|
||||||
{
|
{
|
||||||
dAreaPossible.subtract(path.getPathArea());
|
//Lock lock(areaMutex);
|
||||||
dAreaFree.unite(path.getPathArea());
|
|
||||||
|
areaPossible()->subtract(path.getPathArea());
|
||||||
|
freePaths()->unite(path.getPathArea());
|
||||||
for(const auto & t : path.getPathArea().getTilesVector())
|
for(const auto & t : path.getPathArea().getTilesVector())
|
||||||
map.setOccupied(t, ETileType::FREE);
|
map.setOccupied(t, ETileType::FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Zone::fractalize()
|
void Zone::fractalize()
|
||||||
{
|
{
|
||||||
rmg::Area clearedTiles(dAreaFree);
|
|
||||||
rmg::Area possibleTiles(dAreaPossible);
|
|
||||||
rmg::Area tilesToIgnore; //will be erased in this iteration
|
|
||||||
|
|
||||||
//Squared
|
//Squared
|
||||||
float minDistance = 9 * 9;
|
float minDistance = 9 * 9;
|
||||||
float freeDistance = pos.z ? (10 * 10) : (9 * 9);
|
float freeDistance = pos.z ? (10 * 10) : (9 * 9);
|
||||||
@ -283,6 +295,13 @@ void Zone::fractalize()
|
|||||||
vstd::amax(freeDistance, 4 * 4);
|
vstd::amax(freeDistance, 4 * 4);
|
||||||
logGlobal->trace("Zone %d: treasureValue %d blockDistance: %2.f, freeDistance: %2.f", getId(), treasureValue, blockDistance, freeDistance);
|
logGlobal->trace("Zone %d: treasureValue %d blockDistance: %2.f, freeDistance: %2.f", getId(), treasureValue, blockDistance, freeDistance);
|
||||||
|
|
||||||
|
Lock lock(areaMutex);
|
||||||
|
// FIXME: Do not access Area directly
|
||||||
|
|
||||||
|
rmg::Area clearedTiles(dAreaFree);
|
||||||
|
rmg::Area possibleTiles(dAreaPossible);
|
||||||
|
rmg::Area tilesToIgnore; //will be erased in this iteration
|
||||||
|
|
||||||
if(type != ETemplateZoneType::JUNCTION)
|
if(type != ETemplateZoneType::JUNCTION)
|
||||||
{
|
{
|
||||||
//junction is not fractalized, has only one straight path
|
//junction is not fractalized, has only one straight path
|
||||||
@ -336,7 +355,7 @@ void Zone::fractalize()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock lock(areaMutex);
|
//Lock lock(areaMutex);
|
||||||
//Connect with free areas
|
//Connect with free areas
|
||||||
auto areas = connectedAreas(clearedTiles, true);
|
auto areas = connectedAreas(clearedTiles, true);
|
||||||
for(auto & area : areas)
|
for(auto & area : areas)
|
||||||
|
@ -34,6 +34,36 @@ extern const std::function<bool(const int3 &)> AREA_NO_FILTER;
|
|||||||
|
|
||||||
typedef std::list<std::shared_ptr<Modificator>> TModificators;
|
typedef std::list<std::shared_ptr<Modificator>> TModificators;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class ThreadSafeProxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ThreadSafeProxy(T& resource, boost::recursive_mutex& mutex)
|
||||||
|
: resourceRef(resource), lock(mutex) {}
|
||||||
|
|
||||||
|
T* operator->() { return &resourceRef; }
|
||||||
|
const T* operator->() const { return &resourceRef; }
|
||||||
|
T& operator*() { return resourceRef; }
|
||||||
|
const T& operator*() const { return resourceRef; }
|
||||||
|
T& get() {return resourceRef;}
|
||||||
|
const T& get() const {return resourceRef;}
|
||||||
|
|
||||||
|
|
||||||
|
T operator+(const T & other)
|
||||||
|
{
|
||||||
|
return resourceRef + other;
|
||||||
|
}
|
||||||
|
|
||||||
|
T operator+(ThreadSafeProxy<T> & other)
|
||||||
|
{
|
||||||
|
return get() + other.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T& resourceRef;
|
||||||
|
std::lock_guard<boost::recursive_mutex> lock;
|
||||||
|
};
|
||||||
|
|
||||||
class Zone : public rmg::ZoneOptions
|
class Zone : public rmg::ZoneOptions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -48,11 +78,14 @@ public:
|
|||||||
int3 getPos() const;
|
int3 getPos() const;
|
||||||
void setPos(const int3 &pos);
|
void setPos(const int3 &pos);
|
||||||
|
|
||||||
const rmg::Area & getArea() const;
|
ThreadSafeProxy<rmg::Area> area();
|
||||||
rmg::Area & area();
|
ThreadSafeProxy<const rmg::Area> area() const;
|
||||||
rmg::Area & areaPossible();
|
ThreadSafeProxy<rmg::Area> areaPossible();
|
||||||
rmg::Area & freePaths();
|
ThreadSafeProxy<const rmg::Area> areaPossible() const;
|
||||||
rmg::Area & areaUsed();
|
ThreadSafeProxy<rmg::Area> freePaths();
|
||||||
|
ThreadSafeProxy<const rmg::Area> freePaths() const;
|
||||||
|
ThreadSafeProxy<rmg::Area> areaUsed();
|
||||||
|
ThreadSafeProxy<const rmg::Area> areaUsed() const;
|
||||||
|
|
||||||
void initFreeTiles();
|
void initFreeTiles();
|
||||||
void clearTiles();
|
void clearTiles();
|
||||||
@ -89,7 +122,7 @@ public:
|
|||||||
|
|
||||||
CRandomGenerator & getRand();
|
CRandomGenerator & getRand();
|
||||||
public:
|
public:
|
||||||
boost::recursive_mutex areaMutex;
|
mutable boost::recursive_mutex areaMutex;
|
||||||
using Lock = boost::unique_lock<boost::recursive_mutex>;
|
using Lock = boost::unique_lock<boost::recursive_mutex>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -28,6 +28,24 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
std::pair<Zone::Lock, Zone::Lock> ConnectionsPlacer::lockZones(std::shared_ptr<Zone> otherZone)
|
||||||
|
{
|
||||||
|
if (zone.getId() == otherZone->getId())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// FIXME: What if every zone owns its own lock?
|
||||||
|
auto lock1 = Zone::Lock(zone.areaMutex, boost::try_to_lock);
|
||||||
|
auto lock2 = Zone::Lock(otherZone->areaMutex, boost::try_to_lock);
|
||||||
|
|
||||||
|
if (lock1.owns_lock() && lock2.owns_lock())
|
||||||
|
{
|
||||||
|
return { std::move(lock1), std::move(lock2) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionsPlacer::process()
|
void ConnectionsPlacer::process()
|
||||||
{
|
{
|
||||||
collectNeighbourZones();
|
collectNeighbourZones();
|
||||||
@ -36,16 +54,14 @@ void ConnectionsPlacer::process()
|
|||||||
{
|
{
|
||||||
for (auto& c : dConnections)
|
for (auto& c : dConnections)
|
||||||
{
|
{
|
||||||
if (c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto otherZone = map.getZones().at(c.getZoneB());
|
auto otherZone = map.getZones().at(c.getZoneB());
|
||||||
auto* cp = otherZone->getModificator<ConnectionsPlacer>();
|
auto* cp = otherZone->getModificator<ConnectionsPlacer>();
|
||||||
|
|
||||||
while (cp)
|
while (cp)
|
||||||
{
|
{
|
||||||
RecursiveLock lock1(externalAccessMutex, boost::try_to_lock_t{});
|
// FIXME: It does lock ConnectionPlacer, but not areaMutex
|
||||||
RecursiveLock lock2(cp->externalAccessMutex, boost::try_to_lock_t{});
|
RecursiveLock lock1(externalAccessMutex, boost::try_to_lock);
|
||||||
|
RecursiveLock lock2(cp->externalAccessMutex, boost::try_to_lock);
|
||||||
if (lock1.owns_lock() && lock2.owns_lock())
|
if (lock1.owns_lock() && lock2.owns_lock())
|
||||||
{
|
{
|
||||||
if (!vstd::contains(dCompleted, c))
|
if (!vstd::contains(dCompleted, c))
|
||||||
@ -78,9 +94,16 @@ void ConnectionsPlacer::init()
|
|||||||
POSTFUNCTION(RoadPlacer);
|
POSTFUNCTION(RoadPlacer);
|
||||||
POSTFUNCTION(ObjectManager);
|
POSTFUNCTION(ObjectManager);
|
||||||
|
|
||||||
|
auto id = zone.getId();
|
||||||
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
|
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
|
||||||
|
{
|
||||||
|
// Only consider connected zones
|
||||||
|
if (c.getZoneA() == id || c.getZoneB() == id)
|
||||||
|
{
|
||||||
addConnection(c);
|
addConnection(c);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionsPlacer::addConnection(const rmg::ZoneConnection& connection)
|
void ConnectionsPlacer::addConnection(const rmg::ZoneConnection& connection)
|
||||||
{
|
{
|
||||||
@ -106,6 +129,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
|
|
||||||
bool directProhibited = vstd::contains(ourTerrain->prohibitTransitions, otherZone->getTerrainType())
|
bool directProhibited = vstd::contains(ourTerrain->prohibitTransitions, otherZone->getTerrainType())
|
||||||
|| vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType());
|
|| vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType());
|
||||||
|
|
||||||
|
auto lock = lockZones(otherZone);
|
||||||
|
|
||||||
auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
|
auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
|
||||||
|
|
||||||
if (directConnectionIterator != dNeighbourZones.end())
|
if (directConnectionIterator != dNeighbourZones.end())
|
||||||
@ -115,19 +141,19 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
for (auto borderPos : directConnectionIterator->second)
|
for (auto borderPos : directConnectionIterator->second)
|
||||||
{
|
{
|
||||||
//TODO: Refactor common code with direct connection
|
//TODO: Refactor common code with direct connection
|
||||||
int3 potentialPos = zone.areaPossible().nearest(borderPos);
|
int3 potentialPos = zone.areaPossible()->nearest(borderPos);
|
||||||
assert(borderPos != potentialPos);
|
assert(borderPos != potentialPos);
|
||||||
|
|
||||||
auto safetyGap = rmg::Area({ potentialPos });
|
auto safetyGap = rmg::Area({ potentialPos });
|
||||||
safetyGap.unite(safetyGap.getBorderOutside());
|
safetyGap.unite(safetyGap.getBorderOutside());
|
||||||
safetyGap.intersect(zone.areaPossible());
|
safetyGap.intersect(zone.areaPossible().get());
|
||||||
if (!safetyGap.empty())
|
if (!safetyGap.empty())
|
||||||
{
|
{
|
||||||
safetyGap.intersect(otherZone->areaPossible());
|
safetyGap.intersect(otherZone->areaPossible().get());
|
||||||
if (safetyGap.empty())
|
if (safetyGap.empty())
|
||||||
{
|
{
|
||||||
rmg::Area border(zone.getArea().getBorder());
|
rmg::Area border(zone.area()->getBorder());
|
||||||
border.unite(otherZone->getArea().getBorder());
|
border.unite(otherZone->area()->getBorder());
|
||||||
|
|
||||||
auto costFunction = [&border](const int3& s, const int3& d)
|
auto costFunction = [&border](const int3& s, const int3& d)
|
||||||
{
|
{
|
||||||
@ -139,9 +165,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
theirArea.add(potentialPos);
|
theirArea.add(potentialPos);
|
||||||
rmg::Path ourPath(ourArea);
|
rmg::Path ourPath(ourArea);
|
||||||
rmg::Path theirPath(theirArea);
|
rmg::Path theirPath(theirArea);
|
||||||
ourPath.connect(zone.freePaths());
|
ourPath.connect(zone.freePaths().get());
|
||||||
ourPath = ourPath.search(potentialPos, true, costFunction);
|
ourPath = ourPath.search(potentialPos, true, costFunction);
|
||||||
theirPath.connect(otherZone->freePaths());
|
theirPath.connect(otherZone->freePaths().get());
|
||||||
theirPath = theirPath.search(potentialPos, true, costFunction);
|
theirPath = theirPath.search(potentialPos, true, costFunction);
|
||||||
|
|
||||||
if (ourPath.valid() && theirPath.valid())
|
if (ourPath.valid() && theirPath.valid())
|
||||||
@ -174,7 +200,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
int3 roadNode;
|
int3 roadNode;
|
||||||
for (auto borderPos : directConnectionIterator->second)
|
for (auto borderPos : directConnectionIterator->second)
|
||||||
{
|
{
|
||||||
int3 potentialPos = zone.areaPossible().nearest(borderPos);
|
int3 potentialPos = zone.areaPossible()->nearest(borderPos);
|
||||||
assert(borderPos != potentialPos);
|
assert(borderPos != potentialPos);
|
||||||
|
|
||||||
//Check if guard pos doesn't touch any 3rd zone. This would create unwanted passage to 3rd zone
|
//Check if guard pos doesn't touch any 3rd zone. This would create unwanted passage to 3rd zone
|
||||||
@ -200,10 +226,10 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
|
|
||||||
auto safetyGap = rmg::Area({ potentialPos });
|
auto safetyGap = rmg::Area({ potentialPos });
|
||||||
safetyGap.unite(safetyGap.getBorderOutside());
|
safetyGap.unite(safetyGap.getBorderOutside());
|
||||||
safetyGap.intersect(zone.areaPossible());
|
safetyGap.intersect(zone.areaPossible().get());
|
||||||
if (!safetyGap.empty())
|
if (!safetyGap.empty())
|
||||||
{
|
{
|
||||||
safetyGap.intersect(otherZone->areaPossible());
|
safetyGap.intersect(otherZone->areaPossible().get());
|
||||||
if (safetyGap.empty())
|
if (safetyGap.empty())
|
||||||
{
|
{
|
||||||
float distanceToCenter = zone.getPos().dist2d(potentialPos) * otherZone->getPos().dist2d(potentialPos);
|
float distanceToCenter = zone.getPos().dist2d(potentialPos) * otherZone->getPos().dist2d(potentialPos);
|
||||||
@ -228,8 +254,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
auto & manager = *zone.getModificator<ObjectManager>();
|
auto & manager = *zone.getModificator<ObjectManager>();
|
||||||
auto * monsterType = manager.chooseGuard(connection.getGuardStrength(), true);
|
auto * monsterType = manager.chooseGuard(connection.getGuardStrength(), true);
|
||||||
|
|
||||||
rmg::Area border(zone.getArea().getBorder());
|
rmg::Area border(zone.area()->getBorder());
|
||||||
border.unite(otherZone->getArea().getBorder());
|
border.unite(otherZone->area()->getBorder());
|
||||||
|
|
||||||
auto costFunction = [&border](const int3 & s, const int3 & d)
|
auto costFunction = [&border](const int3 & s, const int3 & d)
|
||||||
{
|
{
|
||||||
@ -241,9 +267,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
theirArea.add(guardPos);
|
theirArea.add(guardPos);
|
||||||
rmg::Path ourPath(ourArea);
|
rmg::Path ourPath(ourArea);
|
||||||
rmg::Path theirPath(theirArea);
|
rmg::Path theirPath(theirArea);
|
||||||
ourPath.connect(zone.freePaths());
|
ourPath.connect(zone.freePaths().get());
|
||||||
ourPath = ourPath.search(guardPos, true, costFunction);
|
ourPath = ourPath.search(guardPos, true, costFunction);
|
||||||
theirPath.connect(otherZone->freePaths());
|
theirPath.connect(otherZone->freePaths().get());
|
||||||
theirPath = theirPath.search(guardPos, true, costFunction);
|
theirPath = theirPath.search(guardPos, true, costFunction);
|
||||||
|
|
||||||
if(ourPath.valid() && theirPath.valid())
|
if(ourPath.valid() && theirPath.valid())
|
||||||
@ -262,10 +288,11 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Update distances from empty passage, too
|
//Update distances from empty passage, too
|
||||||
zone.areaPossible().erase(guardPos);
|
zone.areaPossible()->erase(guardPos);
|
||||||
zone.freePaths().add(guardPos);
|
zone.freePaths()->add(guardPos);
|
||||||
map.setOccupied(guardPos, ETileType::FREE);
|
map.setOccupied(guardPos, ETileType::FREE);
|
||||||
manager.updateDistances(guardPos);
|
manager.updateDistances(guardPos);
|
||||||
|
// FIXME: Accessing other manager might lead to deadlock
|
||||||
otherZone->getModificator<ObjectManager>()->updateDistances(guardPos);
|
otherZone->getModificator<ObjectManager>()->updateDistances(guardPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,8 +345,10 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
|||||||
{
|
{
|
||||||
int3 zShift(0, 0, zone.getPos().z - otherZone->getPos().z);
|
int3 zShift(0, 0, zone.getPos().z - otherZone->getPos().z);
|
||||||
|
|
||||||
|
auto lock = lockZones(otherZone);
|
||||||
|
|
||||||
std::scoped_lock doubleLock(zone.areaMutex, otherZone->areaMutex);
|
std::scoped_lock doubleLock(zone.areaMutex, otherZone->areaMutex);
|
||||||
auto commonArea = zone.areaPossible() * (otherZone->areaPossible() + zShift);
|
auto commonArea = zone.areaPossible().get() * (otherZone->areaPossible().get() + zShift);
|
||||||
if(!commonArea.empty())
|
if(!commonArea.empty())
|
||||||
{
|
{
|
||||||
assert(zone.getModificator<ObjectManager>());
|
assert(zone.getModificator<ObjectManager>());
|
||||||
@ -339,7 +368,7 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
|||||||
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
|
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
|
||||||
int minDist = 3;
|
int minDist = 3;
|
||||||
|
|
||||||
rmg::Path path2(otherZone->area());
|
rmg::Path path2(otherZone->area().get());
|
||||||
rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile)
|
rmg::Path path1 = manager.placeAndConnectObject(commonArea, rmgGate1, [this, minDist, &path2, &rmgGate1, &zShift, guarded2, &managerOther, &rmgGate2 ](const int3 & tile)
|
||||||
{
|
{
|
||||||
auto ti = map.getTileInfo(tile);
|
auto ti = map.getTileInfo(tile);
|
||||||
@ -403,7 +432,7 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
|||||||
|
|
||||||
void ConnectionsPlacer::collectNeighbourZones()
|
void ConnectionsPlacer::collectNeighbourZones()
|
||||||
{
|
{
|
||||||
auto border = zone.area().getBorderOutside();
|
auto border = zone.area()->getBorderOutside();
|
||||||
for(const auto & i : border)
|
for(const auto & i : border)
|
||||||
{
|
{
|
||||||
if(!map.isOnMap(i))
|
if(!map.isOnMap(i))
|
||||||
@ -423,8 +452,8 @@ bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection
|
|||||||
|
|
||||||
void ConnectionsPlacer::createBorder()
|
void ConnectionsPlacer::createBorder()
|
||||||
{
|
{
|
||||||
rmg::Area borderArea(zone.getArea().getBorder());
|
rmg::Area borderArea(zone.area()->getBorder());
|
||||||
rmg::Area borderOutsideArea(zone.getArea().getBorderOutside());
|
rmg::Area borderOutsideArea(zone.area()->getBorderOutside());
|
||||||
auto blockBorder = borderArea.getSubarea([this, &borderOutsideArea](const int3 & t)
|
auto blockBorder = borderArea.getSubarea([this, &borderOutsideArea](const int3 & t)
|
||||||
{
|
{
|
||||||
auto tile = borderOutsideArea.nearest(t);
|
auto tile = borderOutsideArea.nearest(t);
|
||||||
@ -448,21 +477,22 @@ void ConnectionsPlacer::createBorder()
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again
|
//Zone::Lock lock(zone.areaMutex); //Protect from erasing same tiles again
|
||||||
|
auto areaPossible = zone.areaPossible();
|
||||||
for(const auto & tile : blockBorder.getTilesVector())
|
for(const auto & tile : blockBorder.getTilesVector())
|
||||||
{
|
{
|
||||||
if(map.isPossible(tile))
|
if(map.isPossible(tile))
|
||||||
{
|
{
|
||||||
map.setOccupied(tile, ETileType::BLOCKED);
|
map.setOccupied(tile, ETileType::BLOCKED);
|
||||||
zone.areaPossible().erase(tile);
|
areaPossible->erase(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
map.foreachDirectNeighbour(tile, [this](int3 &nearbyPos)
|
map.foreachDirectNeighbour(tile, [this, &areaPossible](int3 &nearbyPos)
|
||||||
{
|
{
|
||||||
if(map.isPossible(nearbyPos) && map.getZoneID(nearbyPos) == zone.getId())
|
if(map.isPossible(nearbyPos) && map.getZoneID(nearbyPos) == zone.getId())
|
||||||
{
|
{
|
||||||
map.setOccupied(nearbyPos, ETileType::BLOCKED);
|
map.setOccupied(nearbyPos, ETileType::BLOCKED);
|
||||||
zone.areaPossible().erase(nearbyPos);
|
areaPossible->erase(nearbyPos);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void collectNeighbourZones();
|
void collectNeighbourZones();
|
||||||
|
std::pair<Zone::Lock, Zone::Lock> lockZones(std::shared_ptr<Zone> otherZone);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<rmg::ZoneConnection> dConnections;
|
std::vector<rmg::ZoneConnection> dConnections;
|
||||||
|
@ -35,7 +35,7 @@ const std::string & Modificator::getName() const
|
|||||||
|
|
||||||
bool Modificator::isReady()
|
bool Modificator::isReady()
|
||||||
{
|
{
|
||||||
Lock lock(mx, boost::try_to_lock_t{});
|
Lock lock(mx, boost::try_to_lock);
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -63,7 +63,7 @@ bool Modificator::isReady()
|
|||||||
|
|
||||||
bool Modificator::isFinished()
|
bool Modificator::isFinished()
|
||||||
{
|
{
|
||||||
Lock lock(mx, boost::try_to_lock_t{});
|
Lock lock(mx, boost::try_to_lock);
|
||||||
if (!lock.owns_lock())
|
if (!lock.owns_lock())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -119,6 +119,8 @@ void Modificator::postfunction(Modificator * modificator)
|
|||||||
|
|
||||||
void Modificator::dump()
|
void Modificator::dump()
|
||||||
{
|
{
|
||||||
|
// TODO: Refactor to lock zone area only once
|
||||||
|
|
||||||
std::ofstream out(boost::str(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
|
std::ofstream out(boost::str(boost::format("seed_%d_modzone_%d_%s.txt") % generator.getRandomSeed() % zone.getId() % getName()));
|
||||||
int levels = map.levels();
|
int levels = map.levels();
|
||||||
int width = map.width();
|
int width = map.width();
|
||||||
@ -140,13 +142,13 @@ void Modificator::dump()
|
|||||||
|
|
||||||
char Modificator::dump(const int3 & t)
|
char Modificator::dump(const int3 & t)
|
||||||
{
|
{
|
||||||
if(zone.freePaths().contains(t))
|
if(zone.freePaths()->contains(t))
|
||||||
return '.'; //free path
|
return '.'; //free path
|
||||||
if(zone.areaPossible().contains(t))
|
if(zone.areaPossible()->contains(t))
|
||||||
return ' '; //possible
|
return ' '; //possible
|
||||||
if(zone.areaUsed().contains(t))
|
if(zone.areaUsed()->contains(t))
|
||||||
return 'U'; //used
|
return 'U'; //used
|
||||||
if(zone.area().contains(t))
|
if(zone.area()->contains(t))
|
||||||
{
|
{
|
||||||
if(map.shouldBeBlocked(t))
|
if(map.shouldBeBlocked(t))
|
||||||
return '#'; //obstacle
|
return '#'; //obstacle
|
||||||
|
@ -40,7 +40,30 @@ void ObjectManager::process()
|
|||||||
void ObjectManager::init()
|
void ObjectManager::init()
|
||||||
{
|
{
|
||||||
DEPENDENCY(WaterAdopter);
|
DEPENDENCY(WaterAdopter);
|
||||||
DEPENDENCY_ALL(ConnectionsPlacer); //Monoliths can be placed by other zone, too
|
|
||||||
|
//Monoliths can be placed by other zone, too
|
||||||
|
// Consider only connected zones
|
||||||
|
auto id = zone.getId();
|
||||||
|
std::set<TRmgTemplateZoneId> connectedZones;
|
||||||
|
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
|
||||||
|
{
|
||||||
|
// Only consider connected zones
|
||||||
|
if (c.getZoneA() == id || c.getZoneB() == id)
|
||||||
|
{
|
||||||
|
connectedZones.insert(c.getZoneA());
|
||||||
|
connectedZones.insert(c.getZoneB());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto zones = map.getZones();
|
||||||
|
for (auto zoneId : connectedZones)
|
||||||
|
{
|
||||||
|
auto * cp = zones.at(zoneId)->getModificator<ConnectionsPlacer>();
|
||||||
|
if (cp)
|
||||||
|
{
|
||||||
|
dependency(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DEPENDENCY(TownPlacer); //Only secondary towns
|
DEPENDENCY(TownPlacer); //Only secondary towns
|
||||||
DEPENDENCY(MinePlacer);
|
DEPENDENCY(MinePlacer);
|
||||||
POSTFUNCTION(RoadPlacer);
|
POSTFUNCTION(RoadPlacer);
|
||||||
@ -49,9 +72,11 @@ void ObjectManager::init()
|
|||||||
|
|
||||||
void ObjectManager::createDistancesPriorityQueue()
|
void ObjectManager::createDistancesPriorityQueue()
|
||||||
{
|
{
|
||||||
|
const auto tiles = zone.areaPossible()->getTilesVector();
|
||||||
|
|
||||||
RecursiveLock lock(externalAccessMutex);
|
RecursiveLock lock(externalAccessMutex);
|
||||||
tilesByDistance.clear();
|
tilesByDistance.clear();
|
||||||
for(const auto & tile : zone.areaPossible().getTilesVector())
|
for(const auto & tile : tiles)
|
||||||
{
|
{
|
||||||
tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
|
tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
|
||||||
}
|
}
|
||||||
@ -93,9 +118,24 @@ void ObjectManager::updateDistances(const int3 & pos)
|
|||||||
|
|
||||||
void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> distanceFunction)
|
void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> distanceFunction)
|
||||||
{
|
{
|
||||||
RecursiveLock lock(externalAccessMutex);
|
// Workaround to avoid dealock when accessed from other zone
|
||||||
|
RecursiveLock lock(zone.areaMutex, boost::try_to_lock);
|
||||||
|
if (!lock.owns_lock())
|
||||||
|
{
|
||||||
|
// Sorry, unsolvable problem of mutual impact¯\_(ツ)_/¯
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Update map distances - Requires lock on zone area only
|
||||||
|
2. Update tilesByDistance priority queue - Requires lock area AND externalAccessMutex
|
||||||
|
*/
|
||||||
|
|
||||||
|
const auto tiles = zone.areaPossible()->getTilesVector();
|
||||||
|
//RecursiveLock lock(externalAccessMutex);
|
||||||
tilesByDistance.clear();
|
tilesByDistance.clear();
|
||||||
for (const auto & tile : zone.areaPossible().getTilesVector()) //don't need to mark distance for not possible tiles
|
|
||||||
|
for (const auto & tile : tiles) //don't need to mark distance for not possible tiles
|
||||||
{
|
{
|
||||||
ui32 d = distanceFunction(tile);
|
ui32 d = distanceFunction(tile);
|
||||||
map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile)));
|
map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile)));
|
||||||
@ -145,7 +185,10 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
|
|||||||
|
|
||||||
if(optimizer & OptimizeType::DISTANCE)
|
if(optimizer & OptimizeType::DISTANCE)
|
||||||
{
|
{
|
||||||
|
// Do not add or remove tiles while we iterate on them
|
||||||
|
//RecursiveLock lock(externalAccessMutex);
|
||||||
auto open = tilesByDistance;
|
auto open = tilesByDistance;
|
||||||
|
|
||||||
while(!open.empty())
|
while(!open.empty())
|
||||||
{
|
{
|
||||||
auto node = open.top();
|
auto node = open.top();
|
||||||
@ -235,7 +278,7 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
|
|||||||
|
|
||||||
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
|
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
|
||||||
{
|
{
|
||||||
RecursiveLock lock(externalAccessMutex);
|
//RecursiveLock lock(externalAccessMutex);
|
||||||
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
|
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
|
||||||
{
|
{
|
||||||
float bestDistance = 10e9;
|
float bestDistance = 10e9;
|
||||||
@ -372,7 +415,7 @@ bool ObjectManager::createMonoliths()
|
|||||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, true);
|
bool guarded = addGuard(rmgObject, objInfo.guardStrength, true);
|
||||||
|
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
||||||
|
|
||||||
if(!path.valid())
|
if(!path.valid())
|
||||||
{
|
{
|
||||||
@ -395,7 +438,7 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
{
|
{
|
||||||
logGlobal->trace("Creating required objects");
|
logGlobal->trace("Creating required objects");
|
||||||
|
|
||||||
//RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop?
|
RecursiveLock lock(externalAccessMutex); //In case someone adds more objects
|
||||||
for(const auto & objInfo : requiredObjects)
|
for(const auto & objInfo : requiredObjects)
|
||||||
{
|
{
|
||||||
rmg::Object rmgObject(*objInfo.obj);
|
rmg::Object rmgObject(*objInfo.obj);
|
||||||
@ -403,7 +446,7 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
||||||
|
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
||||||
|
|
||||||
if(!path.valid())
|
if(!path.valid())
|
||||||
{
|
{
|
||||||
@ -421,7 +464,7 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
|
|
||||||
rmg::Object rmgNearObject(*nearby.obj);
|
rmg::Object rmgNearObject(*nearby.obj);
|
||||||
rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside());
|
rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside());
|
||||||
possibleArea.intersect(zone.areaPossible());
|
possibleArea.intersect(zone.areaPossible().get());
|
||||||
if(possibleArea.empty())
|
if(possibleArea.empty())
|
||||||
{
|
{
|
||||||
rmgNearObject.clear();
|
rmgNearObject.clear();
|
||||||
@ -436,12 +479,11 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
for(const auto & objInfo : closeObjects)
|
for(const auto & objInfo : closeObjects)
|
||||||
{
|
{
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
auto possibleArea = zone.areaPossible();
|
|
||||||
|
|
||||||
rmg::Object rmgObject(*objInfo.obj);
|
rmg::Object rmgObject(*objInfo.obj);
|
||||||
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
||||||
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject,
|
auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject,
|
||||||
[this, &rmgObject](const int3 & tile)
|
[this, &rmgObject](const int3 & tile)
|
||||||
{
|
{
|
||||||
float dist = rmgObject.getArea().distanceSqr(zone.getPos());
|
float dist = rmgObject.getArea().distanceSqr(zone.getPos());
|
||||||
@ -470,15 +512,15 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
|
|
||||||
rmg::Object rmgNearObject(*nearby.obj);
|
rmg::Object rmgNearObject(*nearby.obj);
|
||||||
std::set<int3> blockedArea = targetObject->getBlockedPos();
|
std::set<int3> blockedArea = targetObject->getBlockedPos();
|
||||||
rmg::Area possibleArea(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
|
rmg::Area areaForObject(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
|
||||||
possibleArea.intersect(zone.areaPossible());
|
areaForObject.intersect(zone.areaPossible().get());
|
||||||
if(possibleArea.empty())
|
if(areaForObject.empty())
|
||||||
{
|
{
|
||||||
rmgNearObject.clear();
|
rmgNearObject.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand()));
|
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(areaForObject.getTiles(), zone.getRand()));
|
||||||
placeObject(rmgNearObject, false, false);
|
placeObject(rmgNearObject, false, false);
|
||||||
auto path = zone.searchPath(rmgNearObject.getVisitablePosition(), false);
|
auto path = zone.searchPath(rmgNearObject.getVisitablePosition(), false);
|
||||||
if (path.valid())
|
if (path.valid())
|
||||||
@ -515,8 +557,6 @@ bool ObjectManager::createRequiredObjects()
|
|||||||
|
|
||||||
void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/)
|
void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/)
|
||||||
{
|
{
|
||||||
//object.finalize(map);
|
|
||||||
|
|
||||||
if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER)
|
if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER)
|
||||||
{
|
{
|
||||||
//Fix for HoTA offset - lonely guards
|
//Fix for HoTA offset - lonely guards
|
||||||
@ -539,14 +579,16 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
|||||||
}
|
}
|
||||||
object.finalize(map, zone.getRand());
|
object.finalize(map, zone.getRand());
|
||||||
|
|
||||||
|
{
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
zone.areaPossible().subtract(object.getArea());
|
|
||||||
bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition());
|
zone.areaPossible()->subtract(object.getArea());
|
||||||
zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping
|
bool keepVisitable = zone.freePaths()->contains(object.getVisitablePosition());
|
||||||
|
zone.freePaths()->subtract(object.getArea()); //just to avoid areas overlapping
|
||||||
if(keepVisitable)
|
if(keepVisitable)
|
||||||
zone.freePaths().add(object.getVisitablePosition());
|
zone.freePaths()->add(object.getVisitablePosition());
|
||||||
zone.areaUsed().unite(object.getArea());
|
zone.areaUsed()->unite(object.getArea());
|
||||||
zone.areaUsed().erase(object.getVisitablePosition());
|
zone.areaUsed()->erase(object.getVisitablePosition());
|
||||||
|
|
||||||
if(guarded) //We assume the monster won't be guarded
|
if(guarded) //We assume the monster won't be guarded
|
||||||
{
|
{
|
||||||
@ -554,16 +596,16 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
|||||||
guardedArea.add(object.instances().back()->getVisitablePosition());
|
guardedArea.add(object.instances().back()->getVisitablePosition());
|
||||||
auto areaToBlock = object.getAccessibleArea(true);
|
auto areaToBlock = object.getAccessibleArea(true);
|
||||||
areaToBlock.subtract(guardedArea);
|
areaToBlock.subtract(guardedArea);
|
||||||
zone.areaPossible().subtract(areaToBlock);
|
zone.areaPossible()->subtract(areaToBlock);
|
||||||
for(const auto & i : areaToBlock.getTilesVector())
|
for(const auto & i : areaToBlock.getTilesVector())
|
||||||
if(map.isOnMap(i) && map.isPossible(i))
|
if(map.isOnMap(i) && map.isPossible(i))
|
||||||
map.setOccupied(i, ETileType::BLOCKED);
|
map.setOccupied(i, ETileType::BLOCKED);
|
||||||
}
|
}
|
||||||
lock.unlock();
|
}
|
||||||
|
|
||||||
if (updateDistance)
|
if (updateDistance)
|
||||||
{
|
{
|
||||||
//Update distances in every adjacent zone in case of wide connection
|
//Update distances in every adjacent zone (including this one) in case of wide connection
|
||||||
|
|
||||||
std::set<TRmgTemplateZoneId> adjacentZones;
|
std::set<TRmgTemplateZoneId> adjacentZones;
|
||||||
auto objectArea = object.getArea();
|
auto objectArea = object.getArea();
|
||||||
@ -577,8 +619,15 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto id : adjacentZones)
|
// FIXME: Possible deadlock by two managers updating each other
|
||||||
|
// At this point areaMutex is locked
|
||||||
|
// TODO: Generic function for multiple spinlocks
|
||||||
|
//std::vector sorted(adjacentZones.begin(), adjacentZones.end());
|
||||||
|
//std::sort(sorted.begin(), sorted.end());
|
||||||
|
//for (auto id : adjacentZones)
|
||||||
|
..for (auto id : sorted)
|
||||||
{
|
{
|
||||||
|
//TODO: Test again with sorted order?
|
||||||
auto otherZone = map.getZones().at(id);
|
auto otherZone = map.getZones().at(id);
|
||||||
if ((otherZone->getType() == ETemplateZoneType::WATER) == (zone.getType() == ETemplateZoneType::WATER))
|
if ((otherZone->getType() == ETemplateZoneType::WATER) == (zone.getType() == ETemplateZoneType::WATER))
|
||||||
{
|
{
|
||||||
|
@ -37,22 +37,26 @@ void ObstaclePlacer::process()
|
|||||||
collectPossibleObstacles(zone.getTerrainType());
|
collectPossibleObstacles(zone.getTerrainType());
|
||||||
|
|
||||||
{
|
{
|
||||||
Zone::Lock lock(zone.areaMutex);
|
//Zone::Lock lock(zone.areaMutex);
|
||||||
blockedArea = zone.area().getSubarea([this](const int3& t)
|
auto area = zone.area();
|
||||||
|
auto areaPossible = zone.areaPossible();
|
||||||
|
auto areaUsed = zone.areaUsed();
|
||||||
|
|
||||||
|
blockedArea = area->getSubarea([this](const int3& t)
|
||||||
{
|
{
|
||||||
return map.shouldBeBlocked(t);
|
return map.shouldBeBlocked(t);
|
||||||
});
|
});
|
||||||
blockedArea.subtract(zone.areaUsed());
|
blockedArea.subtract(*areaUsed);
|
||||||
zone.areaPossible().subtract(blockedArea);
|
areaPossible->subtract(blockedArea);
|
||||||
|
|
||||||
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
|
prohibitedArea = zone.freePaths() + areaUsed + manager->getVisitableArea();
|
||||||
|
|
||||||
//Progressively block tiles, but make sure they don't seal any gap between blocks
|
//Progressively block tiles, but make sure they don't seal any gap between blocks
|
||||||
rmg::Area toBlock;
|
rmg::Area toBlock;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
toBlock.clear();
|
toBlock.clear();
|
||||||
for (const auto& tile : zone.areaPossible().getTilesVector())
|
for (const auto& tile : areaPossible->getTilesVector())
|
||||||
{
|
{
|
||||||
rmg::Area neighbors;
|
rmg::Area neighbors;
|
||||||
rmg::Area t;
|
rmg::Area t;
|
||||||
@ -76,7 +80,7 @@ void ObstaclePlacer::process()
|
|||||||
toBlock.add(tile);
|
toBlock.add(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zone.areaPossible().subtract(toBlock);
|
areaPossible->subtract(toBlock);
|
||||||
for (const auto& tile : toBlock.getTilesVector())
|
for (const auto& tile : toBlock.getTilesVector())
|
||||||
{
|
{
|
||||||
map.setOccupied(tile, ETileType::BLOCKED);
|
map.setOccupied(tile, ETileType::BLOCKED);
|
||||||
@ -84,7 +88,7 @@ void ObstaclePlacer::process()
|
|||||||
|
|
||||||
} while (!toBlock.empty());
|
} while (!toBlock.empty());
|
||||||
|
|
||||||
prohibitedArea.unite(zone.areaPossible());
|
prohibitedArea.unite(areaPossible.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto objs = createObstacles(zone.getRand(), map.mapInstance->cb);
|
auto objs = createObstacles(zone.getRand(), map.mapInstance->cb);
|
||||||
@ -119,7 +123,7 @@ void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance
|
|||||||
|
|
||||||
std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
|
std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
|
||||||
{
|
{
|
||||||
return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)};
|
return {map.shouldBeBlocked(t), zone.areaPossible()->contains(t)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObstaclePlacer::postProcess(const rmg::Object & object)
|
void ObstaclePlacer::postProcess(const rmg::Object & object)
|
||||||
@ -141,7 +145,7 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
|
|||||||
if(prohibitedArea.overlap(objArea))
|
if(prohibitedArea.overlap(objArea))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(!zone.area().contains(objArea))
|
if(!zone.area()->contains(objArea))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -106,14 +106,14 @@ char RiverPlacer::dump(const int3 & t)
|
|||||||
return '2';
|
return '2';
|
||||||
if(source.contains(t))
|
if(source.contains(t))
|
||||||
return '1';
|
return '1';
|
||||||
if(zone.area().contains(t))
|
if(zone.area()->contains(t))
|
||||||
return ' ';
|
return ' ';
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
|
|
||||||
void RiverPlacer::addRiverNode(const int3 & node)
|
void RiverPlacer::addRiverNode(const int3 & node)
|
||||||
{
|
{
|
||||||
assert(zone.area().contains(node));
|
assert(zone.area()->contains(node));
|
||||||
riverNodes.insert(node);
|
riverNodes.insert(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,14 +140,17 @@ void RiverPlacer::prepareHeightmap()
|
|||||||
roads.unite(m->getRoads());
|
roads.unite(m->getRoads());
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto & t : zone.area().getTilesVector())
|
auto area = zone.area();
|
||||||
|
auto areaUsed = zone.areaUsed();
|
||||||
|
|
||||||
|
for(const auto & t : area->getTilesVector())
|
||||||
{
|
{
|
||||||
heightMap[t] = zone.getRand().nextInt(5);
|
heightMap[t] = zone.getRand().nextInt(5);
|
||||||
|
|
||||||
if(roads.contains(t))
|
if(roads.contains(t))
|
||||||
heightMap[t] += 30.f;
|
heightMap[t] += 30.f;
|
||||||
|
|
||||||
if(zone.areaUsed().contains(t))
|
if(areaUsed->contains(t))
|
||||||
heightMap[t] += 1000.f;
|
heightMap[t] += 1000.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +160,7 @@ void RiverPlacer::prepareHeightmap()
|
|||||||
for(int i = 0; i < map.width(); i += 2)
|
for(int i = 0; i < map.width(); i += 2)
|
||||||
{
|
{
|
||||||
int3 t{i, j, zone.getPos().z};
|
int3 t{i, j, zone.getPos().z};
|
||||||
if(zone.area().contains(t))
|
if(area->contains(t))
|
||||||
heightMap[t] += 10.f;
|
heightMap[t] += 10.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,9 +170,10 @@ void RiverPlacer::preprocess()
|
|||||||
{
|
{
|
||||||
rmg::Area outOfMapTiles;
|
rmg::Area outOfMapTiles;
|
||||||
std::map<TRmgTemplateZoneId, rmg::Area> neighbourZonesTiles;
|
std::map<TRmgTemplateZoneId, rmg::Area> neighbourZonesTiles;
|
||||||
rmg::Area borderArea(zone.getArea().getBorder());
|
|
||||||
|
rmg::Area borderArea(zone.area()->getBorder());
|
||||||
TRmgTemplateZoneId connectedToWaterZoneId = -1;
|
TRmgTemplateZoneId connectedToWaterZoneId = -1;
|
||||||
for(const auto & t : zone.getArea().getBorderOutside())
|
for(const auto & t : zone.area()->getBorderOutside())
|
||||||
{
|
{
|
||||||
if(!map.isOnMap(t))
|
if(!map.isOnMap(t))
|
||||||
{
|
{
|
||||||
@ -182,6 +186,7 @@ void RiverPlacer::preprocess()
|
|||||||
neighbourZonesTiles[map.getZoneID(t)].add(t);
|
neighbourZonesTiles[map.getZoneID(t)].add(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rmg::Area outOfMapInternal(outOfMapTiles.getBorderOutside());
|
rmg::Area outOfMapInternal(outOfMapTiles.getBorderOutside());
|
||||||
outOfMapInternal.intersect(borderArea);
|
outOfMapInternal.intersect(borderArea);
|
||||||
|
|
||||||
@ -297,7 +302,7 @@ void RiverPlacer::preprocess()
|
|||||||
prepareHeightmap();
|
prepareHeightmap();
|
||||||
|
|
||||||
//decorative river
|
//decorative river
|
||||||
if(!sink.empty() && !source.empty() && riverNodes.empty() && !zone.areaPossible().empty())
|
if(!sink.empty() && !source.empty() && riverNodes.empty() && !zone.areaPossible()->empty())
|
||||||
{
|
{
|
||||||
addRiverNode(*RandomGeneratorUtil::nextItem(source.getTilesVector(), zone.getRand()));
|
addRiverNode(*RandomGeneratorUtil::nextItem(source.getTilesVector(), zone.getRand()));
|
||||||
}
|
}
|
||||||
@ -347,7 +352,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
|
|||||||
return cost;
|
return cost;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto availableArea = zone.area() - prohibit;
|
auto availableArea = zone.area().get() - prohibit;
|
||||||
|
|
||||||
rmg::Path pathToSource(availableArea);
|
rmg::Path pathToSource(availableArea);
|
||||||
pathToSource.connect(source);
|
pathToSource.connect(source);
|
||||||
|
@ -144,8 +144,8 @@ void RoadPlacer::drawRoads(bool secondary)
|
|||||||
return !terrain->isPassable() || !terrain->isLand();
|
return !terrain->isPassable() || !terrain->isLand();
|
||||||
});
|
});
|
||||||
|
|
||||||
zone.areaPossible().subtract(roads);
|
zone.areaPossible()->subtract(roads);
|
||||||
zone.freePaths().unite(roads);
|
zone.freePaths()->unite(roads);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!generator.getMapGenOptions().isRoadEnabled())
|
if(!generator.getMapGenOptions().isRoadEnabled())
|
||||||
|
@ -68,7 +68,7 @@ char RockFiller::dump(const int3 & t)
|
|||||||
{
|
{
|
||||||
if(!map.getTile(t).terType->isPassable())
|
if(!map.getTile(t).terType->isPassable())
|
||||||
{
|
{
|
||||||
return zone.area().contains(t) ? 'R' : 'E';
|
return zone.area()->contains(t) ? 'R' : 'E';
|
||||||
}
|
}
|
||||||
return Modificator::dump(t);
|
return Modificator::dump(t);
|
||||||
}
|
}
|
||||||
|
@ -40,25 +40,28 @@ void RockPlacer::blockRock()
|
|||||||
accessibleArea.unite(m->getVisitableArea());
|
accessibleArea.unite(m->getVisitableArea());
|
||||||
|
|
||||||
//negative approach - create rock tiles first, then make sure all accessible tiles have no rock
|
//negative approach - create rock tiles first, then make sure all accessible tiles have no rock
|
||||||
rockArea = zone.area().getSubarea([this](const int3 & t)
|
rockArea = zone.area()->getSubarea([this](const int3 & t)
|
||||||
{
|
{
|
||||||
return map.shouldBeBlocked(t);
|
return map.shouldBeBlocked(t);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RockPlacer::postProcess()
|
void RockPlacer::postProcess()
|
||||||
|
{
|
||||||
{
|
{
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
//Finally mark rock tiles as occupied, spawn no obstacles there
|
//Finally mark rock tiles as occupied, spawn no obstacles there
|
||||||
rockArea = zone.area().getSubarea([this](const int3 & t)
|
rockArea = zone.area()->getSubarea([this](const int3 & t)
|
||||||
{
|
{
|
||||||
return !map.getTile(t).terType->isPassable();
|
return !map.getTile(t).terType->isPassable();
|
||||||
});
|
});
|
||||||
|
|
||||||
zone.areaUsed().unite(rockArea);
|
zone.areaUsed()->unite(rockArea);
|
||||||
zone.areaPossible().subtract(rockArea);
|
zone.areaPossible()->subtract(rockArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
//RecursiveLock lock(externalAccessMutex);
|
||||||
|
|
||||||
//TODO: Might need mutex here as well
|
|
||||||
if(auto * m = zone.getModificator<RiverPlacer>())
|
if(auto * m = zone.getModificator<RiverPlacer>())
|
||||||
m->riverProhibit().unite(rockArea);
|
m->riverProhibit().unite(rockArea);
|
||||||
if(auto * m = zone.getModificator<RoadPlacer>())
|
if(auto * m = zone.getModificator<RoadPlacer>())
|
||||||
@ -84,7 +87,7 @@ char RockPlacer::dump(const int3 & t)
|
|||||||
{
|
{
|
||||||
if(!map.getTile(t).terType->isPassable())
|
if(!map.getTile(t).terType->isPassable())
|
||||||
{
|
{
|
||||||
return zone.area().contains(t) ? 'R' : 'E';
|
return zone.area()->contains(t) ? 'R' : 'E';
|
||||||
}
|
}
|
||||||
return Modificator::dump(t);
|
return Modificator::dump(t);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ void TerrainPainter::process()
|
|||||||
{
|
{
|
||||||
initTerrainType();
|
initTerrainType();
|
||||||
|
|
||||||
auto v = zone.getArea().getTilesVector();
|
auto v = zone.area()->getTilesVector();
|
||||||
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
|
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
|
|||||||
int3 position(-1, -1, -1);
|
int3 position(-1, -1, -1);
|
||||||
{
|
{
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
position = manager.findPlaceForObject(zone.areaPossible(), rmgObject, [this](const int3& t)
|
position = manager.findPlaceForObject(zone.areaPossible().get(), rmgObject, [this](const int3& t)
|
||||||
{
|
{
|
||||||
float distance = zone.getPos().dist2dSQ(t);
|
float distance = zone.getPos().dist2dSQ(t);
|
||||||
return 100000.f - distance; //some big number
|
return 100000.f - distance; //some big number
|
||||||
@ -169,8 +169,8 @@ void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
|
|||||||
if (map.isOnMap(t))
|
if (map.isOnMap(t))
|
||||||
{
|
{
|
||||||
map.setOccupied(t, ETileType::FREE);
|
map.setOccupied(t, ETileType::FREE);
|
||||||
zone.areaPossible().erase(t);
|
zone.areaPossible()->erase(t);
|
||||||
zone.freePaths().add(t);
|
zone.freePaths()->add(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -852,11 +852,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
|
|||||||
return oi1.value < oi2.value;
|
return oi1.value < oi2.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
size_t size = 0;
|
const size_t size = zone.area()->getTilesVector().size();
|
||||||
{
|
|
||||||
Zone::Lock lock(zone.areaMutex);
|
|
||||||
size = zone.getArea().getTilesVector().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalDensity = 0;
|
int totalDensity = 0;
|
||||||
|
|
||||||
@ -920,9 +916,11 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
|
|||||||
|
|
||||||
auto path = rmg::Path::invalid();
|
auto path = rmg::Path::invalid();
|
||||||
|
|
||||||
|
{
|
||||||
Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
|
Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
|
||||||
auto possibleArea = zone.areaPossible();
|
// FIXME: Possible area will be regenerated for every object
|
||||||
possibleArea.erase_if([this, &minDistance](const int3& tile) -> bool
|
auto searchArea = zone.areaPossible().get();
|
||||||
|
searchArea.erase_if([this, &minDistance](const int3& tile) -> bool
|
||||||
{
|
{
|
||||||
auto ti = map.getTileInfo(tile);
|
auto ti = map.getTileInfo(tile);
|
||||||
return (ti.getNearestObjectDistance() < minDistance);
|
return (ti.getNearestObjectDistance() < minDistance);
|
||||||
@ -930,7 +928,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
|
|||||||
|
|
||||||
if (guarded)
|
if (guarded)
|
||||||
{
|
{
|
||||||
path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile)
|
path = manager.placeAndConnectObject(searchArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile)
|
||||||
{
|
{
|
||||||
float bestDistance = 10e9;
|
float bestDistance = 10e9;
|
||||||
for (const auto& t : rmgObject.getArea().getTilesVector())
|
for (const auto& t : rmgObject.getArea().getTilesVector())
|
||||||
@ -945,7 +943,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
|
|||||||
const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea();
|
const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea();
|
||||||
const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
|
const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
|
||||||
|
|
||||||
if (zone.freePaths().overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock))
|
if (zone.freePaths()->overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock))
|
||||||
return -1.f;
|
return -1.f;
|
||||||
|
|
||||||
return bestDistance;
|
return bestDistance;
|
||||||
@ -953,9 +951,10 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
|
path = manager.placeAndConnectObject(searchArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
|
||||||
}
|
}
|
||||||
lock.unlock();
|
}
|
||||||
|
//lock.unlock();
|
||||||
|
|
||||||
if (path.valid())
|
if (path.valid())
|
||||||
{
|
{
|
||||||
|
@ -43,13 +43,13 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
|
|||||||
if(waterContent == EWaterContent::NONE || zone.isUnderground() || zone.getType() == ETemplateZoneType::WATER)
|
if(waterContent == EWaterContent::NONE || zone.isUnderground() || zone.getType() == ETemplateZoneType::WATER)
|
||||||
return; //do nothing
|
return; //do nothing
|
||||||
|
|
||||||
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap);
|
distanceMap = zone.area()->computeDistanceMap(reverseDistanceMap);
|
||||||
|
|
||||||
//add border tiles as water for ISLANDS
|
//add border tiles as water for ISLANDS
|
||||||
if(waterContent == EWaterContent::ISLANDS)
|
if(waterContent == EWaterContent::ISLANDS)
|
||||||
{
|
{
|
||||||
waterArea.unite(collectDistantTiles(zone, zone.getSize() + 1));
|
waterArea.unite(collectDistantTiles(zone, zone.getSize() + 1));
|
||||||
waterArea.unite(zone.area().getBorder());
|
waterArea.unite(zone.area()->getBorder());
|
||||||
}
|
}
|
||||||
|
|
||||||
//protect some parts from water for NORMAL
|
//protect some parts from water for NORMAL
|
||||||
@ -199,7 +199,7 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
|
|||||||
std::vector<int3> groundCoast;
|
std::vector<int3> groundCoast;
|
||||||
map.foreachDirectNeighbour(tile, [this, &groundCoast](const int3 & t)
|
map.foreachDirectNeighbour(tile, [this, &groundCoast](const int3 & t)
|
||||||
{
|
{
|
||||||
if(!waterArea.contains(t) && zone.area().contains(t)) //for ground tiles of same zone
|
if(!waterArea.contains(t) && zone.area()->contains(t)) //for ground tiles of same zone
|
||||||
{
|
{
|
||||||
groundCoast.push_back(t);
|
groundCoast.push_back(t);
|
||||||
}
|
}
|
||||||
@ -223,12 +223,12 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
|
|||||||
|
|
||||||
{
|
{
|
||||||
Zone::Lock waterLock(map.getZones()[waterZoneId]->areaMutex);
|
Zone::Lock waterLock(map.getZones()[waterZoneId]->areaMutex);
|
||||||
map.getZones()[waterZoneId]->area().unite(waterArea);
|
map.getZones()[waterZoneId]->area()->unite(waterArea);
|
||||||
}
|
}
|
||||||
Zone::Lock lock(zone.areaMutex);
|
Zone::Lock lock(zone.areaMutex);
|
||||||
zone.area().subtract(waterArea);
|
zone.area()->subtract(waterArea);
|
||||||
zone.areaPossible().subtract(waterArea);
|
zone.areaPossible()->subtract(waterArea);
|
||||||
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap);
|
distanceMap = zone.area()->computeDistanceMap(reverseDistanceMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterAdopter::setWaterZone(TRmgTemplateZoneId water)
|
void WaterAdopter::setWaterZone(TRmgTemplateZoneId water)
|
||||||
|
@ -34,45 +34,51 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
void WaterProxy::process()
|
void WaterProxy::process()
|
||||||
{
|
{
|
||||||
for(const auto & t : zone.area().getTilesVector())
|
auto area = zone.area();
|
||||||
|
|
||||||
|
for(const auto & t : area->getTilesVector())
|
||||||
{
|
{
|
||||||
map.setZoneID(t, zone.getId());
|
map.setZoneID(t, zone.getId());
|
||||||
map.setOccupied(t, ETileType::POSSIBLE);
|
map.setOccupied(t, ETileType::POSSIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto v = zone.getArea().getTilesVector();
|
auto v = area->getTilesVector();
|
||||||
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
|
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
|
||||||
|
|
||||||
//check terrain type
|
//check terrain type
|
||||||
for([[maybe_unused]] const auto & t : zone.area().getTilesVector())
|
for([[maybe_unused]] const auto & t : area->getTilesVector())
|
||||||
{
|
{
|
||||||
assert(map.isOnMap(t));
|
assert(map.isOnMap(t));
|
||||||
assert(map.getTile(t).terType->getId() == zone.getTerrainType());
|
assert(map.getTile(t).terType->getId() == zone.getTerrainType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Possible deadlock for 2 zones
|
||||||
|
|
||||||
|
auto areaPossible = zone.areaPossible();
|
||||||
for(const auto & z : map.getZones())
|
for(const auto & z : map.getZones())
|
||||||
{
|
{
|
||||||
if(z.second->getId() == zone.getId())
|
if(z.second->getId() == zone.getId())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Zone::Lock lock(z.second->areaMutex);
|
auto secondArea = z.second->area();
|
||||||
for(const auto & t : z.second->area().getTilesVector())
|
auto secondAreaPossible = z.second->areaPossible();
|
||||||
|
for(const auto & t : secondArea->getTilesVector())
|
||||||
{
|
{
|
||||||
if(map.getTile(t).terType->getId() == zone.getTerrainType())
|
if(map.getTile(t).terType->getId() == zone.getTerrainType())
|
||||||
{
|
{
|
||||||
z.second->areaPossible().erase(t);
|
secondArea->erase(t);
|
||||||
z.second->area().erase(t);
|
secondAreaPossible->erase(t);
|
||||||
zone.area().add(t);
|
area->add(t);
|
||||||
zone.areaPossible().add(t);
|
areaPossible->add(t);
|
||||||
map.setZoneID(t, zone.getId());
|
map.setZoneID(t, zone.getId());
|
||||||
map.setOccupied(t, ETileType::POSSIBLE);
|
map.setOccupied(t, ETileType::POSSIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!zone.area().contains(zone.getPos()))
|
if(!area->contains(zone.getPos()))
|
||||||
{
|
{
|
||||||
zone.setPos(zone.area().getTilesVector().front());
|
zone.setPos(area->getTilesVector().front());
|
||||||
}
|
}
|
||||||
|
|
||||||
zone.initFreeTiles();
|
zone.initFreeTiles();
|
||||||
@ -105,7 +111,7 @@ void WaterProxy::collectLakes()
|
|||||||
{
|
{
|
||||||
RecursiveLock lock(externalAccessMutex);
|
RecursiveLock lock(externalAccessMutex);
|
||||||
int lakeId = 0;
|
int lakeId = 0;
|
||||||
for(const auto & lake : connectedAreas(zone.getArea(), true))
|
for(const auto & lake : connectedAreas(zone.area().get(), true))
|
||||||
{
|
{
|
||||||
lakes.push_back(Lake{});
|
lakes.push_back(Lake{});
|
||||||
lakes.back().area = lake;
|
lakes.back().area = lake;
|
||||||
@ -117,8 +123,8 @@ void WaterProxy::collectLakes()
|
|||||||
lakeMap[t] = lakeId;
|
lakeMap[t] = lakeId;
|
||||||
|
|
||||||
//each lake must have at least one free tile
|
//each lake must have at least one free tile
|
||||||
if(!lake.overlap(zone.freePaths()))
|
if(!lake.overlap(zone.freePaths().get()))
|
||||||
zone.freePaths().add(*lakes.back().reverseDistanceMap[lakes.back().reverseDistanceMap.size() - 1].begin());
|
zone.freePaths()->add(*lakes.back().reverseDistanceMap[lakes.back().reverseDistanceMap.size() - 1].begin());
|
||||||
|
|
||||||
++lakeId;
|
++lakeId;
|
||||||
}
|
}
|
||||||
@ -151,7 +157,7 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Zone::Lock lock(dst.areaMutex);
|
Zone::Lock lock(dst.areaMutex);
|
||||||
dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]);
|
dst.areaPossible()->subtract(lake.neighbourZones[dst.getId()]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,7 +355,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
//try to place shipyard close to boarding position and appropriate water access
|
//try to place shipyard close to boarding position and appropriate water access
|
||||||
auto path = manager->placeAndConnectObject(land.areaPossible(), rmgObject, [&rmgObject, &shipPositions, &boardingPosition](const int3 & tile)
|
auto path = manager->placeAndConnectObject(land.areaPossible().get(), rmgObject, [&rmgObject, &shipPositions, &boardingPosition](const int3 & tile)
|
||||||
{
|
{
|
||||||
//Must only check the border of shipyard and not the added guard
|
//Must only check the border of shipyard and not the added guard
|
||||||
rmg::Area shipyardOut = rmgObject.instances().front()->getBlockedArea().getBorderOutside();
|
rmg::Area shipyardOut = rmgObject.instances().front()->getBlockedArea().getBorderOutside();
|
||||||
@ -361,9 +367,9 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
|
|||||||
}, guarded, true, ObjectManager::OptimizeType::NONE);
|
}, guarded, true, ObjectManager::OptimizeType::NONE);
|
||||||
|
|
||||||
//search path to boarding position
|
//search path to boarding position
|
||||||
auto searchArea = land.areaPossible() - rmgObject.getArea();
|
auto searchArea = land.areaPossible().get() - rmgObject.getArea();
|
||||||
rmg::Path pathToBoarding(searchArea);
|
rmg::Path pathToBoarding(searchArea);
|
||||||
pathToBoarding.connect(land.freePaths());
|
pathToBoarding.connect(land.freePaths().get());
|
||||||
pathToBoarding.connect(path);
|
pathToBoarding.connect(path);
|
||||||
pathToBoarding = pathToBoarding.search(boardingPosition, false);
|
pathToBoarding = pathToBoarding.search(boardingPosition, false);
|
||||||
|
|
||||||
@ -391,7 +397,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
|
|||||||
|
|
||||||
manager->placeObject(rmgObject, guarded, true, createRoad);
|
manager->placeObject(rmgObject, guarded, true, createRoad);
|
||||||
|
|
||||||
zone.areaPossible().subtract(shipyardOutToBlock);
|
zone.areaPossible()->subtract(shipyardOutToBlock);
|
||||||
for(const auto & i : shipyardOutToBlock.getTilesVector())
|
for(const auto & i : shipyardOutToBlock.getTilesVector())
|
||||||
if(map.isOnMap(i) && map.isPossible(i))
|
if(map.isOnMap(i) && map.isPossible(i))
|
||||||
map.setOccupied(i, ETileType::BLOCKED);
|
map.setOccupied(i, ETileType::BLOCKED);
|
||||||
|
@ -43,22 +43,27 @@ void WaterRoutes::process()
|
|||||||
result.push_back(wproxy->waterRoute(*z.second));
|
result.push_back(wproxy->waterRoute(*z.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
Zone::Lock lock(zone.areaMutex);
|
//Zone::Lock lock(zone.areaMutex);
|
||||||
|
|
||||||
|
auto area = zone.area();
|
||||||
|
auto freePaths = zone.freePaths();
|
||||||
|
auto areaPossible = zone.areaPossible();
|
||||||
|
auto areaUsed = zone.areaUsed();
|
||||||
|
|
||||||
//prohibit to place objects on sealed off lakes
|
//prohibit to place objects on sealed off lakes
|
||||||
for(const auto & lake : wproxy->getLakes())
|
for(const auto & lake : wproxy->getLakes())
|
||||||
{
|
{
|
||||||
if((lake.area * zone.freePaths()).getTilesVector().size() == 1)
|
if((lake.area * freePaths.get()).getTilesVector().size() == 1)
|
||||||
{
|
{
|
||||||
zone.freePaths().subtract(lake.area);
|
freePaths->subtract(lake.area);
|
||||||
zone.areaPossible().subtract(lake.area);
|
areaPossible->subtract(lake.area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//prohibit to place objects on the borders
|
//prohibit to place objects on the borders
|
||||||
for(const auto & t : zone.area().getBorder())
|
for(const auto & t : area->getBorder())
|
||||||
{
|
{
|
||||||
if(zone.areaPossible().contains(t))
|
if(areaPossible->contains(t))
|
||||||
{
|
{
|
||||||
std::vector<int3> landTiles;
|
std::vector<int3> landTiles;
|
||||||
map.foreachDirectNeighbour(t, [this, &landTiles, &t](const int3 & c)
|
map.foreachDirectNeighbour(t, [this, &landTiles, &t](const int3 & c)
|
||||||
@ -74,8 +79,8 @@ void WaterRoutes::process()
|
|||||||
int3 o = landTiles[0] + landTiles[1];
|
int3 o = landTiles[0] + landTiles[1];
|
||||||
if(o.x * o.x * o.y * o.y == 1)
|
if(o.x * o.x * o.y * o.y == 1)
|
||||||
{
|
{
|
||||||
zone.areaPossible().erase(t);
|
areaPossible->erase(t);
|
||||||
zone.areaUsed().add(t);
|
areaUsed->add(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,6 +101,9 @@ void WaterRoutes::init()
|
|||||||
|
|
||||||
char WaterRoutes::dump(const int3 & t)
|
char WaterRoutes::dump(const int3 & t)
|
||||||
{
|
{
|
||||||
|
auto area = zone.area();
|
||||||
|
auto freePaths = zone.freePaths();
|
||||||
|
|
||||||
for(auto & i : result)
|
for(auto & i : result)
|
||||||
{
|
{
|
||||||
if(t == i.boarding)
|
if(t == i.boarding)
|
||||||
@ -106,15 +114,15 @@ char WaterRoutes::dump(const int3 & t)
|
|||||||
return '#';
|
return '#';
|
||||||
if(i.water.contains(t))
|
if(i.water.contains(t))
|
||||||
{
|
{
|
||||||
if(zone.freePaths().contains(t))
|
if(freePaths->contains(t))
|
||||||
return '+';
|
return '+';
|
||||||
else
|
else
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(zone.freePaths().contains(t))
|
if(freePaths->contains(t))
|
||||||
return '.';
|
return '.';
|
||||||
if(zone.area().contains(t))
|
if(area->contains(t))
|
||||||
return '~';
|
return '~';
|
||||||
return ' ';
|
return ' ';
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user