1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

+ Maintain clear perimeter of a treasure pile.

+ Make sure that separate blocked areas remain unconnected so it's possible to pass between them.
This commit is contained in:
Tomasz Zieliński 2023-06-10 14:56:03 +02:00
parent d5b799278e
commit 1bb2b5b571
4 changed files with 117 additions and 13 deletions

View File

@ -64,21 +64,35 @@ void Area::invalidate()
dBorderOutsideCache.clear();
}
bool Area::connected() const
bool Area::connected(bool noDiagonals) const
{
std::list<int3> queue({*dTiles.begin()});
Tileset connected = dTiles; //use invalidated cache - ok
while(!queue.empty())
{
auto t = queue.front();
connected.erase(t);
queue.pop_front();
for(auto & i : int3::getDirs())
if (noDiagonals)
{
if(connected.count(t + i))
for (auto& i : dirs4)
{
queue.push_back(t + i);
if (connected.count(t + i))
{
queue.push_back(t + i);
}
}
}
else
{
for (auto& i : int3::getDirs())
{
if (connected.count(t + i))
{
queue.push_back(t + i);
}
}
}
}

View File

@ -44,7 +44,7 @@ namespace rmg
Area getSubarea(const std::function<bool(const int3 &)> & filter) const;
bool connected() const; //is connected
bool connected(bool noDiagonals = false) const; //is connected
bool empty() const;
bool contains(const int3 & tile) const;
bool contains(const std::vector<int3> & tiles) const;

View File

@ -166,7 +166,6 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
}
}
//FIXME: Race condition for tiles? For Area?
if(result.valid())
obj.setPosition(result);
return result;
@ -213,6 +212,55 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
return -1.f;
}
rmg::Area perimeter;
rmg::Area areaToBlock;
if (obj.isGuarded())
{
auto guardedArea = obj.instances().back()->getAccessibleArea();
guardedArea.add(obj.instances().back()->getVisitablePosition());
areaToBlock = obj.getAccessibleArea(true);
areaToBlock.subtract(guardedArea);
if (!areaToBlock.empty())
{
perimeter = areaToBlock;
perimeter.unite(areaToBlock.getBorderOutside());
//We could have added border around guard
perimeter.subtract(guardedArea);
}
}
else
{
perimeter = obj.getArea();
perimeter.subtract(obj.getAccessibleArea());
if (!perimeter.empty())
{
perimeter.unite(perimeter.getBorderOutside());
perimeter.subtract(obj.getAccessibleArea());
}
}
//Check if perimeter of the object intersects with more than one blocked areas
auto tiles = perimeter.getTiles();
vstd::erase_if(tiles, [this](const int3& tile) -> bool
{
//Out-of-map area also is an obstacle
if (!map.isOnMap(tile))
return false;
return !(map.isBlocked(tile) || map.isUsed(tile));
});
if (!tiles.empty())
{
rmg::Area border(tiles);
border.subtract(areaToBlock);
if (!border.connected())
{
//We don't want to connect two blocked areas to create impassable obstacle
return -1.f;
}
}
return dist;
}, isGuarded, onlyStraight, optimizer);
}
@ -237,7 +285,7 @@ rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg
accessibleArea.intersect(guardedArea);
accessibleArea.add(obj.instances().back()->getPosition(true));
}
auto path = zone.searchPath(accessibleArea, onlyStraight, [&obj, isGuarded](const int3 & t)
{
if(isGuarded)

View File

@ -35,14 +35,56 @@ void ObstaclePlacer::process()
collectPossibleObstacles(zone.getTerrainType());
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);
blockedArea = zone.area().getSubarea([this](const int3& t)
{
return map.shouldBeBlocked(t);
});
blockedArea.subtract(zone.areaUsed());
zone.areaPossible().subtract(blockedArea);
prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
prohibitedArea = zone.freePaths() + zone.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().getTiles())
{
rmg::Area neighbors;
rmg::Area t;
t.add(tile);
for (const auto& n : t.getBorderOutside())
{
//Area outside the map is also impassable
if (!map.isOnMap(n) || map.shouldBeBlocked(n))
{
neighbors.add(n);
}
}
if (neighbors.empty())
{
continue;
}
//Will only be added if it doesn't connect two disjointed blocks
if (neighbors.connected(true)) //Do not block diagonal pass
{
toBlock.add(tile);
}
}
zone.areaPossible().subtract(toBlock);
for (const auto& tile : toBlock.getTiles())
{
map.setOccupied(tile, ETileType::BLOCKED);
}
} while (!toBlock.empty());
prohibitedArea.unite(zone.areaPossible());
}
auto objs = createObstacles(zone.getRand());
mapProxy->insertObjects(objs);