浏览代码

+ Maintain clear perimeter of a treasure pile.
+ Make sure that separate blocked areas remain unconnected so it's possible to pass between them.

Tomasz Zieliński 2 年之前
父节点
当前提交
1bb2b5b571
共有 4 个文件被更改,包括 117 次插入13 次删除
  1. 18 4
      lib/rmg/RmgArea.cpp
  2. 1 1
      lib/rmg/RmgArea.h
  3. 50 2
      lib/rmg/modificators/ObjectManager.cpp
  4. 48 6
      lib/rmg/modificators/ObstaclePlacer.cpp

+ 18 - 4
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<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)
+		{
+			for (auto& i : dirs4)
+			{
+				if (connected.count(t + i))
+				{
+					queue.push_back(t + i);
+				}
+			}
+		}
+		else
 		{
-			if(connected.count(t + i))
+			for (auto& i : int3::getDirs())
 			{
-				queue.push_back(t + i);
+				if (connected.count(t + i))
+				{
+					queue.push_back(t + i);
+				}
 			}
 		}
 	}

+ 1 - 1
lib/rmg/RmgArea.h

@@ -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;

+ 50 - 2
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)

+ 48 - 6
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);