mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +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);
|
||||
|
||||
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");
|
||||
|
||||
|
@ -858,7 +858,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
||||
auto moveZoneToCenterOfMass = [width, height](const std::shared_ptr<Zone> & zone) -> void
|
||||
{
|
||||
int3 total(0, 0, 0);
|
||||
auto tiles = zone->area().getTiles();
|
||||
auto tiles = zone->area()->getTiles();
|
||||
for(const auto & tile : tiles)
|
||||
{
|
||||
total += tile;
|
||||
@ -892,14 +892,14 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if(zone.second->area().empty())
|
||||
if(zone.second->area()->empty())
|
||||
throw rmgException("Empty zone is generated, probably RMG template is inappropriate for map size");
|
||||
|
||||
moveZoneToCenterOfMass(zone.second);
|
||||
@ -948,14 +948,14 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
|
||||
|
||||
//Tile closest to vertex belongs to zone
|
||||
auto closestZone = boost::min_element(distances, simpleCompareByDistance)->first;
|
||||
closestZone->area().add(pos);
|
||||
closestZone->area()->add(pos);
|
||||
map.setZoneID(pos, closestZone->getId());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
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
|
||||
|
||||
auto v = zone.second->getArea().getTilesVector();
|
||||
auto v = zone.second->area()->getTilesVector();
|
||||
map.getMapProxy()->drawTerrain(*rand, v, ETerrainId::SUBTERRANEAN);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,8 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
rmg::Tileset collectDistantTiles(const Zone& zone, int 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;
|
||||
});
|
||||
|
@ -75,25 +75,44 @@ void Zone::setPos(const int3 &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 dAreaPossible;
|
||||
return ThreadSafeProxy<rmg::Area>(dAreaPossible, areaMutex);
|
||||
}
|
||||
|
||||
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()
|
||||
@ -122,11 +141,6 @@ void Zone::initFreeTiles()
|
||||
}
|
||||
}
|
||||
|
||||
rmg::Area & Zone::freePaths()
|
||||
{
|
||||
return dAreaFree;
|
||||
}
|
||||
|
||||
FactionID Zone::getTownType() const
|
||||
{
|
||||
return townType;
|
||||
@ -225,18 +239,16 @@ TModificators Zone::getModificators()
|
||||
void Zone::connectPath(const rmg::Path & path)
|
||||
///connect current tile to any other free tile within zone
|
||||
{
|
||||
dAreaPossible.subtract(path.getPathArea());
|
||||
dAreaFree.unite(path.getPathArea());
|
||||
//Lock lock(areaMutex);
|
||||
|
||||
areaPossible()->subtract(path.getPathArea());
|
||||
freePaths()->unite(path.getPathArea());
|
||||
for(const auto & t : path.getPathArea().getTilesVector())
|
||||
map.setOccupied(t, ETileType::FREE);
|
||||
}
|
||||
|
||||
void Zone::fractalize()
|
||||
{
|
||||
rmg::Area clearedTiles(dAreaFree);
|
||||
rmg::Area possibleTiles(dAreaPossible);
|
||||
rmg::Area tilesToIgnore; //will be erased in this iteration
|
||||
|
||||
//Squared
|
||||
float minDistance = 9 * 9;
|
||||
float freeDistance = pos.z ? (10 * 10) : (9 * 9);
|
||||
@ -282,6 +294,13 @@ void Zone::fractalize()
|
||||
freeDistance = freeDistance * marginFactor;
|
||||
vstd::amax(freeDistance, 4 * 4);
|
||||
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)
|
||||
{
|
||||
@ -336,7 +355,7 @@ void Zone::fractalize()
|
||||
}
|
||||
}
|
||||
|
||||
Lock lock(areaMutex);
|
||||
//Lock lock(areaMutex);
|
||||
//Connect with free areas
|
||||
auto areas = connectedAreas(clearedTiles, true);
|
||||
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;
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
@ -48,11 +78,14 @@ public:
|
||||
int3 getPos() const;
|
||||
void setPos(const int3 &pos);
|
||||
|
||||
const rmg::Area & getArea() const;
|
||||
rmg::Area & area();
|
||||
rmg::Area & areaPossible();
|
||||
rmg::Area & freePaths();
|
||||
rmg::Area & areaUsed();
|
||||
ThreadSafeProxy<rmg::Area> area();
|
||||
ThreadSafeProxy<const rmg::Area> area() const;
|
||||
ThreadSafeProxy<rmg::Area> areaPossible();
|
||||
ThreadSafeProxy<const rmg::Area> areaPossible() const;
|
||||
ThreadSafeProxy<rmg::Area> freePaths();
|
||||
ThreadSafeProxy<const rmg::Area> freePaths() const;
|
||||
ThreadSafeProxy<rmg::Area> areaUsed();
|
||||
ThreadSafeProxy<const rmg::Area> areaUsed() const;
|
||||
|
||||
void initFreeTiles();
|
||||
void clearTiles();
|
||||
@ -89,7 +122,7 @@ public:
|
||||
|
||||
CRandomGenerator & getRand();
|
||||
public:
|
||||
boost::recursive_mutex areaMutex;
|
||||
mutable boost::recursive_mutex areaMutex;
|
||||
using Lock = boost::unique_lock<boost::recursive_mutex>;
|
||||
|
||||
protected:
|
||||
|
@ -28,6 +28,24 @@
|
||||
|
||||
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()
|
||||
{
|
||||
collectNeighbourZones();
|
||||
@ -36,16 +54,14 @@ void ConnectionsPlacer::process()
|
||||
{
|
||||
for (auto& c : dConnections)
|
||||
{
|
||||
if (c.getZoneA() != zone.getId() && c.getZoneB() != zone.getId())
|
||||
continue;
|
||||
|
||||
auto otherZone = map.getZones().at(c.getZoneB());
|
||||
auto* cp = otherZone->getModificator<ConnectionsPlacer>();
|
||||
|
||||
while (cp)
|
||||
{
|
||||
RecursiveLock lock1(externalAccessMutex, boost::try_to_lock_t{});
|
||||
RecursiveLock lock2(cp->externalAccessMutex, boost::try_to_lock_t{});
|
||||
// FIXME: It does lock ConnectionPlacer, but not areaMutex
|
||||
RecursiveLock lock1(externalAccessMutex, boost::try_to_lock);
|
||||
RecursiveLock lock2(cp->externalAccessMutex, boost::try_to_lock);
|
||||
if (lock1.owns_lock() && lock2.owns_lock())
|
||||
{
|
||||
if (!vstd::contains(dCompleted, c))
|
||||
@ -78,8 +94,15 @@ void ConnectionsPlacer::init()
|
||||
POSTFUNCTION(RoadPlacer);
|
||||
POSTFUNCTION(ObjectManager);
|
||||
|
||||
auto id = zone.getId();
|
||||
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
|
||||
addConnection(c);
|
||||
{
|
||||
// Only consider connected zones
|
||||
if (c.getZoneA() == id || c.getZoneB() == id)
|
||||
{
|
||||
addConnection(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
|| vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType());
|
||||
|
||||
auto lock = lockZones(otherZone);
|
||||
|
||||
auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
|
||||
|
||||
if (directConnectionIterator != dNeighbourZones.end())
|
||||
@ -115,19 +141,19 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
||||
for (auto borderPos : directConnectionIterator->second)
|
||||
{
|
||||
//TODO: Refactor common code with direct connection
|
||||
int3 potentialPos = zone.areaPossible().nearest(borderPos);
|
||||
int3 potentialPos = zone.areaPossible()->nearest(borderPos);
|
||||
assert(borderPos != potentialPos);
|
||||
|
||||
auto safetyGap = rmg::Area({ potentialPos });
|
||||
safetyGap.unite(safetyGap.getBorderOutside());
|
||||
safetyGap.intersect(zone.areaPossible());
|
||||
safetyGap.intersect(zone.areaPossible().get());
|
||||
if (!safetyGap.empty())
|
||||
{
|
||||
safetyGap.intersect(otherZone->areaPossible());
|
||||
safetyGap.intersect(otherZone->areaPossible().get());
|
||||
if (safetyGap.empty())
|
||||
{
|
||||
rmg::Area border(zone.getArea().getBorder());
|
||||
border.unite(otherZone->getArea().getBorder());
|
||||
rmg::Area border(zone.area()->getBorder());
|
||||
border.unite(otherZone->area()->getBorder());
|
||||
|
||||
auto costFunction = [&border](const int3& s, const int3& d)
|
||||
{
|
||||
@ -139,9 +165,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
||||
theirArea.add(potentialPos);
|
||||
rmg::Path ourPath(ourArea);
|
||||
rmg::Path theirPath(theirArea);
|
||||
ourPath.connect(zone.freePaths());
|
||||
ourPath.connect(zone.freePaths().get());
|
||||
ourPath = ourPath.search(potentialPos, true, costFunction);
|
||||
theirPath.connect(otherZone->freePaths());
|
||||
theirPath.connect(otherZone->freePaths().get());
|
||||
theirPath = theirPath.search(potentialPos, true, costFunction);
|
||||
|
||||
if (ourPath.valid() && theirPath.valid())
|
||||
@ -174,7 +200,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
||||
int3 roadNode;
|
||||
for (auto borderPos : directConnectionIterator->second)
|
||||
{
|
||||
int3 potentialPos = zone.areaPossible().nearest(borderPos);
|
||||
int3 potentialPos = zone.areaPossible()->nearest(borderPos);
|
||||
assert(borderPos != potentialPos);
|
||||
|
||||
//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 });
|
||||
safetyGap.unite(safetyGap.getBorderOutside());
|
||||
safetyGap.intersect(zone.areaPossible());
|
||||
safetyGap.intersect(zone.areaPossible().get());
|
||||
if (!safetyGap.empty())
|
||||
{
|
||||
safetyGap.intersect(otherZone->areaPossible());
|
||||
safetyGap.intersect(otherZone->areaPossible().get());
|
||||
if (safetyGap.empty())
|
||||
{
|
||||
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 * monsterType = manager.chooseGuard(connection.getGuardStrength(), true);
|
||||
|
||||
rmg::Area border(zone.getArea().getBorder());
|
||||
border.unite(otherZone->getArea().getBorder());
|
||||
rmg::Area border(zone.area()->getBorder());
|
||||
border.unite(otherZone->area()->getBorder());
|
||||
|
||||
auto costFunction = [&border](const int3 & s, const int3 & d)
|
||||
{
|
||||
@ -241,9 +267,9 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
||||
theirArea.add(guardPos);
|
||||
rmg::Path ourPath(ourArea);
|
||||
rmg::Path theirPath(theirArea);
|
||||
ourPath.connect(zone.freePaths());
|
||||
ourPath.connect(zone.freePaths().get());
|
||||
ourPath = ourPath.search(guardPos, true, costFunction);
|
||||
theirPath.connect(otherZone->freePaths());
|
||||
theirPath.connect(otherZone->freePaths().get());
|
||||
theirPath = theirPath.search(guardPos, true, costFunction);
|
||||
|
||||
if(ourPath.valid() && theirPath.valid())
|
||||
@ -262,10 +288,11 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
|
||||
else
|
||||
{
|
||||
//Update distances from empty passage, too
|
||||
zone.areaPossible().erase(guardPos);
|
||||
zone.freePaths().add(guardPos);
|
||||
zone.areaPossible()->erase(guardPos);
|
||||
zone.freePaths()->add(guardPos);
|
||||
map.setOccupied(guardPos, ETileType::FREE);
|
||||
manager.updateDistances(guardPos);
|
||||
// FIXME: Accessing other manager might lead to deadlock
|
||||
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);
|
||||
|
||||
auto lock = lockZones(otherZone);
|
||||
|
||||
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())
|
||||
{
|
||||
assert(zone.getModificator<ObjectManager>());
|
||||
@ -339,7 +368,7 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
||||
bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
|
||||
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)
|
||||
{
|
||||
auto ti = map.getTileInfo(tile);
|
||||
@ -403,7 +432,7 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
|
||||
|
||||
void ConnectionsPlacer::collectNeighbourZones()
|
||||
{
|
||||
auto border = zone.area().getBorderOutside();
|
||||
auto border = zone.area()->getBorderOutside();
|
||||
for(const auto & i : border)
|
||||
{
|
||||
if(!map.isOnMap(i))
|
||||
@ -423,8 +452,8 @@ bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection
|
||||
|
||||
void ConnectionsPlacer::createBorder()
|
||||
{
|
||||
rmg::Area borderArea(zone.getArea().getBorder());
|
||||
rmg::Area borderOutsideArea(zone.getArea().getBorderOutside());
|
||||
rmg::Area borderArea(zone.area()->getBorder());
|
||||
rmg::Area borderOutsideArea(zone.area()->getBorderOutside());
|
||||
auto blockBorder = borderArea.getSubarea([this, &borderOutsideArea](const int3 & 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())
|
||||
{
|
||||
if(map.isPossible(tile))
|
||||
{
|
||||
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())
|
||||
{
|
||||
map.setOccupied(nearbyPos, ETileType::BLOCKED);
|
||||
zone.areaPossible().erase(nearbyPos);
|
||||
areaPossible->erase(nearbyPos);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ public:
|
||||
|
||||
protected:
|
||||
void collectNeighbourZones();
|
||||
std::pair<Zone::Lock, Zone::Lock> lockZones(std::shared_ptr<Zone> otherZone);
|
||||
|
||||
protected:
|
||||
std::vector<rmg::ZoneConnection> dConnections;
|
||||
|
@ -35,7 +35,7 @@ const std::string & Modificator::getName() const
|
||||
|
||||
bool Modificator::isReady()
|
||||
{
|
||||
Lock lock(mx, boost::try_to_lock_t{});
|
||||
Lock lock(mx, boost::try_to_lock);
|
||||
if (!lock.owns_lock())
|
||||
{
|
||||
return false;
|
||||
@ -63,7 +63,7 @@ bool Modificator::isReady()
|
||||
|
||||
bool Modificator::isFinished()
|
||||
{
|
||||
Lock lock(mx, boost::try_to_lock_t{});
|
||||
Lock lock(mx, boost::try_to_lock);
|
||||
if (!lock.owns_lock())
|
||||
{
|
||||
return false;
|
||||
@ -119,6 +119,8 @@ void Modificator::postfunction(Modificator * modificator)
|
||||
|
||||
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()));
|
||||
int levels = map.levels();
|
||||
int width = map.width();
|
||||
@ -140,13 +142,13 @@ void Modificator::dump()
|
||||
|
||||
char Modificator::dump(const int3 & t)
|
||||
{
|
||||
if(zone.freePaths().contains(t))
|
||||
if(zone.freePaths()->contains(t))
|
||||
return '.'; //free path
|
||||
if(zone.areaPossible().contains(t))
|
||||
if(zone.areaPossible()->contains(t))
|
||||
return ' '; //possible
|
||||
if(zone.areaUsed().contains(t))
|
||||
if(zone.areaUsed()->contains(t))
|
||||
return 'U'; //used
|
||||
if(zone.area().contains(t))
|
||||
if(zone.area()->contains(t))
|
||||
{
|
||||
if(map.shouldBeBlocked(t))
|
||||
return '#'; //obstacle
|
||||
|
@ -40,7 +40,30 @@ void ObjectManager::process()
|
||||
void ObjectManager::init()
|
||||
{
|
||||
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(MinePlacer);
|
||||
POSTFUNCTION(RoadPlacer);
|
||||
@ -49,9 +72,11 @@ void ObjectManager::init()
|
||||
|
||||
void ObjectManager::createDistancesPriorityQueue()
|
||||
{
|
||||
const auto tiles = zone.areaPossible()->getTilesVector();
|
||||
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
tilesByDistance.clear();
|
||||
for(const auto & tile : zone.areaPossible().getTilesVector())
|
||||
for(const auto & tile : tiles)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
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);
|
||||
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)
|
||||
{
|
||||
// Do not add or remove tiles while we iterate on them
|
||||
//RecursiveLock lock(externalAccessMutex);
|
||||
auto open = tilesByDistance;
|
||||
|
||||
while(!open.empty())
|
||||
{
|
||||
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
|
||||
{
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
//RecursiveLock lock(externalAccessMutex);
|
||||
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
|
||||
{
|
||||
float bestDistance = 10e9;
|
||||
@ -372,7 +415,7 @@ bool ObjectManager::createMonoliths()
|
||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, true);
|
||||
|
||||
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())
|
||||
{
|
||||
@ -395,7 +438,7 @@ bool ObjectManager::createRequiredObjects()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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));
|
||||
|
||||
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())
|
||||
{
|
||||
@ -421,7 +464,7 @@ bool ObjectManager::createRequiredObjects()
|
||||
|
||||
rmg::Object rmgNearObject(*nearby.obj);
|
||||
rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside());
|
||||
possibleArea.intersect(zone.areaPossible());
|
||||
possibleArea.intersect(zone.areaPossible().get());
|
||||
if(possibleArea.empty())
|
||||
{
|
||||
rmgNearObject.clear();
|
||||
@ -436,12 +479,11 @@ bool ObjectManager::createRequiredObjects()
|
||||
for(const auto & objInfo : closeObjects)
|
||||
{
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
auto possibleArea = zone.areaPossible();
|
||||
|
||||
rmg::Object rmgObject(*objInfo.obj);
|
||||
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||
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)
|
||||
{
|
||||
float dist = rmgObject.getArea().distanceSqr(zone.getPos());
|
||||
@ -470,15 +512,15 @@ bool ObjectManager::createRequiredObjects()
|
||||
|
||||
rmg::Object rmgNearObject(*nearby.obj);
|
||||
std::set<int3> blockedArea = targetObject->getBlockedPos();
|
||||
rmg::Area possibleArea(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
|
||||
possibleArea.intersect(zone.areaPossible());
|
||||
if(possibleArea.empty())
|
||||
rmg::Area areaForObject(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
|
||||
areaForObject.intersect(zone.areaPossible().get());
|
||||
if(areaForObject.empty())
|
||||
{
|
||||
rmgNearObject.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand()));
|
||||
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(areaForObject.getTiles(), zone.getRand()));
|
||||
placeObject(rmgNearObject, false, false);
|
||||
auto path = zone.searchPath(rmgNearObject.getVisitablePosition(), false);
|
||||
if (path.valid())
|
||||
@ -515,8 +557,6 @@ bool ObjectManager::createRequiredObjects()
|
||||
|
||||
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)
|
||||
{
|
||||
//Fix for HoTA offset - lonely guards
|
||||
@ -539,31 +579,33 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
||||
}
|
||||
object.finalize(map, zone.getRand());
|
||||
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
zone.areaPossible().subtract(object.getArea());
|
||||
bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition());
|
||||
zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping
|
||||
if(keepVisitable)
|
||||
zone.freePaths().add(object.getVisitablePosition());
|
||||
zone.areaUsed().unite(object.getArea());
|
||||
zone.areaUsed().erase(object.getVisitablePosition());
|
||||
|
||||
if(guarded) //We assume the monster won't be guarded
|
||||
{
|
||||
auto guardedArea = object.instances().back()->getAccessibleArea();
|
||||
guardedArea.add(object.instances().back()->getVisitablePosition());
|
||||
auto areaToBlock = object.getAccessibleArea(true);
|
||||
areaToBlock.subtract(guardedArea);
|
||||
zone.areaPossible().subtract(areaToBlock);
|
||||
for(const auto & i : areaToBlock.getTilesVector())
|
||||
if(map.isOnMap(i) && map.isPossible(i))
|
||||
map.setOccupied(i, ETileType::BLOCKED);
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
|
||||
zone.areaPossible()->subtract(object.getArea());
|
||||
bool keepVisitable = zone.freePaths()->contains(object.getVisitablePosition());
|
||||
zone.freePaths()->subtract(object.getArea()); //just to avoid areas overlapping
|
||||
if(keepVisitable)
|
||||
zone.freePaths()->add(object.getVisitablePosition());
|
||||
zone.areaUsed()->unite(object.getArea());
|
||||
zone.areaUsed()->erase(object.getVisitablePosition());
|
||||
|
||||
if(guarded) //We assume the monster won't be guarded
|
||||
{
|
||||
auto guardedArea = object.instances().back()->getAccessibleArea();
|
||||
guardedArea.add(object.instances().back()->getVisitablePosition());
|
||||
auto areaToBlock = object.getAccessibleArea(true);
|
||||
areaToBlock.subtract(guardedArea);
|
||||
zone.areaPossible()->subtract(areaToBlock);
|
||||
for(const auto & i : areaToBlock.getTilesVector())
|
||||
if(map.isOnMap(i) && map.isPossible(i))
|
||||
map.setOccupied(i, ETileType::BLOCKED);
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
|
||||
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;
|
||||
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);
|
||||
if ((otherZone->getType() == ETemplateZoneType::WATER) == (zone.getType() == ETemplateZoneType::WATER))
|
||||
{
|
||||
|
@ -37,22 +37,26 @@ void ObstaclePlacer::process()
|
||||
collectPossibleObstacles(zone.getTerrainType());
|
||||
|
||||
{
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
blockedArea = zone.area().getSubarea([this](const int3& t)
|
||||
{
|
||||
return map.shouldBeBlocked(t);
|
||||
});
|
||||
blockedArea.subtract(zone.areaUsed());
|
||||
zone.areaPossible().subtract(blockedArea);
|
||||
//Zone::Lock lock(zone.areaMutex);
|
||||
auto area = zone.area();
|
||||
auto areaPossible = zone.areaPossible();
|
||||
auto areaUsed = zone.areaUsed();
|
||||
|
||||
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
|
||||
blockedArea = area->getSubarea([this](const int3& t)
|
||||
{
|
||||
return map.shouldBeBlocked(t);
|
||||
});
|
||||
blockedArea.subtract(*areaUsed);
|
||||
areaPossible->subtract(blockedArea);
|
||||
|
||||
prohibitedArea = zone.freePaths() + areaUsed + manager->getVisitableArea();
|
||||
|
||||
//Progressively block tiles, but make sure they don't seal any gap between blocks
|
||||
rmg::Area toBlock;
|
||||
do
|
||||
{
|
||||
toBlock.clear();
|
||||
for (const auto& tile : zone.areaPossible().getTilesVector())
|
||||
for (const auto& tile : areaPossible->getTilesVector())
|
||||
{
|
||||
rmg::Area neighbors;
|
||||
rmg::Area t;
|
||||
@ -76,7 +80,7 @@ void ObstaclePlacer::process()
|
||||
toBlock.add(tile);
|
||||
}
|
||||
}
|
||||
zone.areaPossible().subtract(toBlock);
|
||||
areaPossible->subtract(toBlock);
|
||||
for (const auto& tile : toBlock.getTilesVector())
|
||||
{
|
||||
map.setOccupied(tile, ETileType::BLOCKED);
|
||||
@ -84,7 +88,7 @@ void ObstaclePlacer::process()
|
||||
|
||||
} while (!toBlock.empty());
|
||||
|
||||
prohibitedArea.unite(zone.areaPossible());
|
||||
prohibitedArea.unite(areaPossible.get());
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)};
|
||||
return {map.shouldBeBlocked(t), zone.areaPossible()->contains(t)};
|
||||
}
|
||||
|
||||
void ObstaclePlacer::postProcess(const rmg::Object & object)
|
||||
@ -141,7 +145,7 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
|
||||
if(prohibitedArea.overlap(objArea))
|
||||
return true;
|
||||
|
||||
if(!zone.area().contains(objArea))
|
||||
if(!zone.area()->contains(objArea))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -106,14 +106,14 @@ char RiverPlacer::dump(const int3 & t)
|
||||
return '2';
|
||||
if(source.contains(t))
|
||||
return '1';
|
||||
if(zone.area().contains(t))
|
||||
if(zone.area()->contains(t))
|
||||
return ' ';
|
||||
return '?';
|
||||
}
|
||||
|
||||
void RiverPlacer::addRiverNode(const int3 & node)
|
||||
{
|
||||
assert(zone.area().contains(node));
|
||||
assert(zone.area()->contains(node));
|
||||
riverNodes.insert(node);
|
||||
}
|
||||
|
||||
@ -140,14 +140,17 @@ void RiverPlacer::prepareHeightmap()
|
||||
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);
|
||||
|
||||
if(roads.contains(t))
|
||||
heightMap[t] += 30.f;
|
||||
|
||||
if(zone.areaUsed().contains(t))
|
||||
if(areaUsed->contains(t))
|
||||
heightMap[t] += 1000.f;
|
||||
}
|
||||
|
||||
@ -157,7 +160,7 @@ void RiverPlacer::prepareHeightmap()
|
||||
for(int i = 0; i < map.width(); i += 2)
|
||||
{
|
||||
int3 t{i, j, zone.getPos().z};
|
||||
if(zone.area().contains(t))
|
||||
if(area->contains(t))
|
||||
heightMap[t] += 10.f;
|
||||
}
|
||||
}
|
||||
@ -167,9 +170,10 @@ void RiverPlacer::preprocess()
|
||||
{
|
||||
rmg::Area outOfMapTiles;
|
||||
std::map<TRmgTemplateZoneId, rmg::Area> neighbourZonesTiles;
|
||||
rmg::Area borderArea(zone.getArea().getBorder());
|
||||
|
||||
rmg::Area borderArea(zone.area()->getBorder());
|
||||
TRmgTemplateZoneId connectedToWaterZoneId = -1;
|
||||
for(const auto & t : zone.getArea().getBorderOutside())
|
||||
for(const auto & t : zone.area()->getBorderOutside())
|
||||
{
|
||||
if(!map.isOnMap(t))
|
||||
{
|
||||
@ -182,6 +186,7 @@ void RiverPlacer::preprocess()
|
||||
neighbourZonesTiles[map.getZoneID(t)].add(t);
|
||||
}
|
||||
}
|
||||
|
||||
rmg::Area outOfMapInternal(outOfMapTiles.getBorderOutside());
|
||||
outOfMapInternal.intersect(borderArea);
|
||||
|
||||
@ -297,7 +302,7 @@ void RiverPlacer::preprocess()
|
||||
prepareHeightmap();
|
||||
|
||||
//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()));
|
||||
}
|
||||
@ -347,7 +352,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
|
||||
return cost;
|
||||
};
|
||||
|
||||
auto availableArea = zone.area() - prohibit;
|
||||
auto availableArea = zone.area().get() - prohibit;
|
||||
|
||||
rmg::Path pathToSource(availableArea);
|
||||
pathToSource.connect(source);
|
||||
|
@ -144,8 +144,8 @@ void RoadPlacer::drawRoads(bool secondary)
|
||||
return !terrain->isPassable() || !terrain->isLand();
|
||||
});
|
||||
|
||||
zone.areaPossible().subtract(roads);
|
||||
zone.freePaths().unite(roads);
|
||||
zone.areaPossible()->subtract(roads);
|
||||
zone.freePaths()->unite(roads);
|
||||
}
|
||||
|
||||
if(!generator.getMapGenOptions().isRoadEnabled())
|
||||
|
@ -65,10 +65,10 @@ void RockFiller::init()
|
||||
}
|
||||
|
||||
char RockFiller::dump(const int3 & t)
|
||||
{
|
||||
{
|
||||
if(!map.getTile(t).terType->isPassable())
|
||||
{
|
||||
return zone.area().contains(t) ? 'R' : 'E';
|
||||
return zone.area()->contains(t) ? 'R' : 'E';
|
||||
}
|
||||
return Modificator::dump(t);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ void RockPlacer::blockRock()
|
||||
accessibleArea.unite(m->getVisitableArea());
|
||||
|
||||
//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);
|
||||
});
|
||||
@ -48,17 +48,20 @@ void RockPlacer::blockRock()
|
||||
|
||||
void RockPlacer::postProcess()
|
||||
{
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
//Finally mark rock tiles as occupied, spawn no obstacles there
|
||||
rockArea = zone.area().getSubarea([this](const int3 & t)
|
||||
{
|
||||
return !map.getTile(t).terType->isPassable();
|
||||
});
|
||||
|
||||
zone.areaUsed().unite(rockArea);
|
||||
zone.areaPossible().subtract(rockArea);
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
//Finally mark rock tiles as occupied, spawn no obstacles there
|
||||
rockArea = zone.area()->getSubarea([this](const int3 & t)
|
||||
{
|
||||
return !map.getTile(t).terType->isPassable();
|
||||
});
|
||||
|
||||
zone.areaUsed()->unite(rockArea);
|
||||
zone.areaPossible()->subtract(rockArea);
|
||||
}
|
||||
|
||||
//RecursiveLock lock(externalAccessMutex);
|
||||
|
||||
//TODO: Might need mutex here as well
|
||||
if(auto * m = zone.getModificator<RiverPlacer>())
|
||||
m->riverProhibit().unite(rockArea);
|
||||
if(auto * m = zone.getModificator<RoadPlacer>())
|
||||
@ -84,7 +87,7 @@ char RockPlacer::dump(const int3 & t)
|
||||
{
|
||||
if(!map.getTile(t).terType->isPassable())
|
||||
{
|
||||
return zone.area().contains(t) ? 'R' : 'E';
|
||||
return zone.area()->contains(t) ? 'R' : 'E';
|
||||
}
|
||||
return Modificator::dump(t);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ void TerrainPainter::process()
|
||||
{
|
||||
initTerrainType();
|
||||
|
||||
auto v = zone.getArea().getTilesVector();
|
||||
auto v = zone.area()->getTilesVector();
|
||||
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
|
||||
int3 position(-1, -1, -1);
|
||||
{
|
||||
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);
|
||||
return 100000.f - distance; //some big number
|
||||
@ -169,8 +169,8 @@ void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
|
||||
if (map.isOnMap(t))
|
||||
{
|
||||
map.setOccupied(t, ETileType::FREE);
|
||||
zone.areaPossible().erase(t);
|
||||
zone.freePaths().add(t);
|
||||
zone.areaPossible()->erase(t);
|
||||
zone.freePaths()->add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -852,11 +852,7 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
|
||||
return oi1.value < oi2.value;
|
||||
});
|
||||
|
||||
size_t size = 0;
|
||||
{
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
size = zone.getArea().getTilesVector().size();
|
||||
}
|
||||
const size_t size = zone.area()->getTilesVector().size();
|
||||
|
||||
int totalDensity = 0;
|
||||
|
||||
@ -920,42 +916,45 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
|
||||
|
||||
auto path = rmg::Path::invalid();
|
||||
|
||||
Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
|
||||
auto possibleArea = zone.areaPossible();
|
||||
possibleArea.erase_if([this, &minDistance](const int3& tile) -> bool
|
||||
{
|
||||
auto ti = map.getTileInfo(tile);
|
||||
return (ti.getNearestObjectDistance() < minDistance);
|
||||
});
|
||||
Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
|
||||
// FIXME: Possible area will be regenerated for every object
|
||||
auto searchArea = zone.areaPossible().get();
|
||||
searchArea.erase_if([this, &minDistance](const int3& tile) -> bool
|
||||
{
|
||||
auto ti = map.getTileInfo(tile);
|
||||
return (ti.getNearestObjectDistance() < minDistance);
|
||||
});
|
||||
|
||||
if (guarded)
|
||||
{
|
||||
path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile)
|
||||
{
|
||||
float bestDistance = 10e9;
|
||||
for (const auto& t : rmgObject.getArea().getTilesVector())
|
||||
if (guarded)
|
||||
{
|
||||
path = manager.placeAndConnectObject(searchArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile)
|
||||
{
|
||||
float distance = map.getTileInfo(t).getNearestObjectDistance();
|
||||
if (distance < minDistance)
|
||||
float bestDistance = 10e9;
|
||||
for (const auto& t : rmgObject.getArea().getTilesVector())
|
||||
{
|
||||
float distance = map.getTileInfo(t).getNearestObjectDistance();
|
||||
if (distance < minDistance)
|
||||
return -1.f;
|
||||
else
|
||||
vstd::amin(bestDistance, distance);
|
||||
}
|
||||
|
||||
const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea();
|
||||
const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
|
||||
|
||||
if (zone.freePaths()->overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock))
|
||||
return -1.f;
|
||||
else
|
||||
vstd::amin(bestDistance, distance);
|
||||
}
|
||||
|
||||
const auto & guardedArea = rmgObject.instances().back()->getAccessibleArea();
|
||||
const auto areaToBlock = rmgObject.getAccessibleArea(true) - guardedArea;
|
||||
|
||||
if (zone.freePaths().overlap(areaToBlock) || manager.getVisitableArea().overlap(areaToBlock))
|
||||
return -1.f;
|
||||
|
||||
return bestDistance;
|
||||
}, guarded, false, ObjectManager::OptimizeType::BOTH);
|
||||
return bestDistance;
|
||||
}, guarded, false, ObjectManager::OptimizeType::BOTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = manager.placeAndConnectObject(searchArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
|
||||
}
|
||||
lock.unlock();
|
||||
//lock.unlock();
|
||||
|
||||
if (path.valid())
|
||||
{
|
||||
|
@ -43,13 +43,13 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
|
||||
if(waterContent == EWaterContent::NONE || zone.isUnderground() || zone.getType() == ETemplateZoneType::WATER)
|
||||
return; //do nothing
|
||||
|
||||
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap);
|
||||
distanceMap = zone.area()->computeDistanceMap(reverseDistanceMap);
|
||||
|
||||
//add border tiles as water for ISLANDS
|
||||
if(waterContent == EWaterContent::ISLANDS)
|
||||
{
|
||||
waterArea.unite(collectDistantTiles(zone, zone.getSize() + 1));
|
||||
waterArea.unite(zone.area().getBorder());
|
||||
waterArea.unite(zone.area()->getBorder());
|
||||
}
|
||||
|
||||
//protect some parts from water for NORMAL
|
||||
@ -199,7 +199,7 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
|
||||
std::vector<int3> groundCoast;
|
||||
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);
|
||||
}
|
||||
@ -223,12 +223,12 @@ void WaterAdopter::createWater(EWaterContent::EWaterContent waterContent)
|
||||
|
||||
{
|
||||
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.area().subtract(waterArea);
|
||||
zone.areaPossible().subtract(waterArea);
|
||||
distanceMap = zone.area().computeDistanceMap(reverseDistanceMap);
|
||||
zone.area()->subtract(waterArea);
|
||||
zone.areaPossible()->subtract(waterArea);
|
||||
distanceMap = zone.area()->computeDistanceMap(reverseDistanceMap);
|
||||
}
|
||||
|
||||
void WaterAdopter::setWaterZone(TRmgTemplateZoneId water)
|
||||
|
@ -34,45 +34,51 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
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.setOccupied(t, ETileType::POSSIBLE);
|
||||
}
|
||||
|
||||
auto v = zone.getArea().getTilesVector();
|
||||
auto v = area->getTilesVector();
|
||||
mapProxy->drawTerrain(zone.getRand(), v, zone.getTerrainType());
|
||||
|
||||
//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.getTile(t).terType->getId() == zone.getTerrainType());
|
||||
}
|
||||
|
||||
// FIXME: Possible deadlock for 2 zones
|
||||
|
||||
auto areaPossible = zone.areaPossible();
|
||||
for(const auto & z : map.getZones())
|
||||
{
|
||||
if(z.second->getId() == zone.getId())
|
||||
continue;
|
||||
|
||||
Zone::Lock lock(z.second->areaMutex);
|
||||
for(const auto & t : z.second->area().getTilesVector())
|
||||
auto secondArea = z.second->area();
|
||||
auto secondAreaPossible = z.second->areaPossible();
|
||||
for(const auto & t : secondArea->getTilesVector())
|
||||
{
|
||||
if(map.getTile(t).terType->getId() == zone.getTerrainType())
|
||||
{
|
||||
z.second->areaPossible().erase(t);
|
||||
z.second->area().erase(t);
|
||||
zone.area().add(t);
|
||||
zone.areaPossible().add(t);
|
||||
secondArea->erase(t);
|
||||
secondAreaPossible->erase(t);
|
||||
area->add(t);
|
||||
areaPossible->add(t);
|
||||
map.setZoneID(t, zone.getId());
|
||||
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();
|
||||
@ -105,7 +111,7 @@ void WaterProxy::collectLakes()
|
||||
{
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
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.back().area = lake;
|
||||
@ -117,8 +123,8 @@ void WaterProxy::collectLakes()
|
||||
lakeMap[t] = lakeId;
|
||||
|
||||
//each lake must have at least one free tile
|
||||
if(!lake.overlap(zone.freePaths()))
|
||||
zone.freePaths().add(*lakes.back().reverseDistanceMap[lakes.back().reverseDistanceMap.size() - 1].begin());
|
||||
if(!lake.overlap(zone.freePaths().get()))
|
||||
zone.freePaths()->add(*lakes.back().reverseDistanceMap[lakes.back().reverseDistanceMap.size() - 1].begin());
|
||||
|
||||
++lakeId;
|
||||
}
|
||||
@ -151,7 +157,7 @@ RouteInfo WaterProxy::waterRoute(Zone & dst)
|
||||
}
|
||||
|
||||
Zone::Lock lock(dst.areaMutex);
|
||||
dst.areaPossible().subtract(lake.neighbourZones[dst.getId()]);
|
||||
dst.areaPossible()->subtract(lake.neighbourZones[dst.getId()]);
|
||||
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
|
||||
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
|
||||
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);
|
||||
|
||||
//search path to boarding position
|
||||
auto searchArea = land.areaPossible() - rmgObject.getArea();
|
||||
auto searchArea = land.areaPossible().get() - rmgObject.getArea();
|
||||
rmg::Path pathToBoarding(searchArea);
|
||||
pathToBoarding.connect(land.freePaths());
|
||||
pathToBoarding.connect(land.freePaths().get());
|
||||
pathToBoarding.connect(path);
|
||||
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);
|
||||
|
||||
zone.areaPossible().subtract(shipyardOutToBlock);
|
||||
zone.areaPossible()->subtract(shipyardOutToBlock);
|
||||
for(const auto & i : shipyardOutToBlock.getTilesVector())
|
||||
if(map.isOnMap(i) && map.isPossible(i))
|
||||
map.setOccupied(i, ETileType::BLOCKED);
|
||||
|
@ -43,22 +43,27 @@ void WaterRoutes::process()
|
||||
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
|
||||
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);
|
||||
zone.areaPossible().subtract(lake.area);
|
||||
freePaths->subtract(lake.area);
|
||||
areaPossible->subtract(lake.area);
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
map.foreachDirectNeighbour(t, [this, &landTiles, &t](const int3 & c)
|
||||
@ -74,8 +79,8 @@ void WaterRoutes::process()
|
||||
int3 o = landTiles[0] + landTiles[1];
|
||||
if(o.x * o.x * o.y * o.y == 1)
|
||||
{
|
||||
zone.areaPossible().erase(t);
|
||||
zone.areaUsed().add(t);
|
||||
areaPossible->erase(t);
|
||||
areaUsed->add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,6 +101,9 @@ void WaterRoutes::init()
|
||||
|
||||
char WaterRoutes::dump(const int3 & t)
|
||||
{
|
||||
auto area = zone.area();
|
||||
auto freePaths = zone.freePaths();
|
||||
|
||||
for(auto & i : result)
|
||||
{
|
||||
if(t == i.boarding)
|
||||
@ -106,15 +114,15 @@ char WaterRoutes::dump(const int3 & t)
|
||||
return '#';
|
||||
if(i.water.contains(t))
|
||||
{
|
||||
if(zone.freePaths().contains(t))
|
||||
if(freePaths->contains(t))
|
||||
return '+';
|
||||
else
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
if(zone.freePaths().contains(t))
|
||||
if(freePaths->contains(t))
|
||||
return '.';
|
||||
if(zone.area().contains(t))
|
||||
if(area->contains(t))
|
||||
return '~';
|
||||
return ' ';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user