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:
parent
d5b799278e
commit
1bb2b5b571
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user