From 1bb2b5b57165a130eff9a5bb5f829d6bfa5b9c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 10 Jun 2023 14:56:03 +0200 Subject: [PATCH] + Maintain clear perimeter of a treasure pile. + Make sure that separate blocked areas remain unconnected so it's possible to pass between them. --- lib/rmg/RmgArea.cpp | 22 ++++++++-- lib/rmg/RmgArea.h | 2 +- lib/rmg/modificators/ObjectManager.cpp | 52 +++++++++++++++++++++++- lib/rmg/modificators/ObstaclePlacer.cpp | 54 ++++++++++++++++++++++--- 4 files changed, 117 insertions(+), 13 deletions(-) diff --git a/lib/rmg/RmgArea.cpp b/lib/rmg/RmgArea.cpp index 5be26a8c4..3131bf188 100644 --- a/lib/rmg/RmgArea.cpp +++ b/lib/rmg/RmgArea.cpp @@ -64,21 +64,35 @@ void Area::invalidate() dBorderOutsideCache.clear(); } -bool Area::connected() const +bool Area::connected(bool noDiagonals) const { std::list 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); + } } } } diff --git a/lib/rmg/RmgArea.h b/lib/rmg/RmgArea.h index 154c4acbf..1755f22d8 100644 --- a/lib/rmg/RmgArea.h +++ b/lib/rmg/RmgArea.h @@ -44,7 +44,7 @@ namespace rmg Area getSubarea(const std::function & 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 & tiles) const; diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 5b7b051c8..c82e632e0 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -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) diff --git a/lib/rmg/modificators/ObstaclePlacer.cpp b/lib/rmg/modificators/ObstaclePlacer.cpp index a19ffcb3f..62b58ec20 100644 --- a/lib/rmg/modificators/ObstaclePlacer.cpp +++ b/lib/rmg/modificators/ObstaclePlacer.cpp @@ -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);