Browse Source

Fixed placing of close objects #2447. Some additional refactoring.

DjWarmonger 9 years ago
parent
commit
aecf834cc1
2 changed files with 84 additions and 58 deletions
  1. 74 58
      lib/rmg/CRmgTemplateZone.cpp
  2. 10 0
      lib/rmg/CRmgTemplateZone.h

+ 74 - 58
lib/rmg/CRmgTemplateZone.cpp

@@ -1551,6 +1551,31 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
 	return true;
 }
 
+EObjectPlacingResult::EObjectPlacingResult CRmgTemplateZone::tryToPlaceObjectAndConnectToPath(CMapGenerator* gen, CGObjectInstance *obj, int3 &pos)
+{
+	//check if we can find a path around this object. Tiles will be set to "USED" after object is successfully placed.
+	obj->pos = pos;
+	gen->setOccupied(obj->visitablePos(), ETileType::BLOCKED);
+	for (auto tile : obj->getBlockedPos())
+	{
+		if (gen->map->isInTheMap(tile))
+			gen->setOccupied(tile, ETileType::BLOCKED);
+	}
+	int3 accessibleOffset = getAccessibleOffset(gen, obj->appearance, pos);
+	if (!accessibleOffset.valid())
+	{
+		logGlobal->warnStream() << boost::format("Cannot access required object at position %s, retrying") % pos;
+		return EObjectPlacingResult::CANNOT_FIT;
+	}
+	if (!connectPath(gen, accessibleOffset, true))
+	{
+		logGlobal->traceStream() << boost::format("Failed to create path to required object at position %s, retrying") % pos;
+		return EObjectPlacingResult::SEALED_OFF;
+	}
+	else
+		return EObjectPlacingResult::SUCCESS;
+}
+
 bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
 {
 	logGlobal->traceStream() << "Creating required objects";
@@ -1559,7 +1584,6 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
 	{
 		auto obj = object.first;
 		int3 pos;
-		int3 accessibleOffset;
 		while (true)
 		{
 			if (!findPlaceForObject(gen, obj, 3, pos))
@@ -1567,81 +1591,78 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
 				logGlobal->errorStream() << boost::format("Failed to fill zone %d due to lack of space") % id;
 				return false;
 			}
-
-			//check if we can find a path around this object. Tiles will be set to "USED" after object is successfully placed.
-			obj->pos = pos;
-			gen->setOccupied (obj->visitablePos(), ETileType::BLOCKED);
-			for (auto tile : obj->getBlockedPos())
-			{
-				if (gen->map->isInTheMap(tile))
-					gen->setOccupied(tile, ETileType::BLOCKED);
-			}
-			accessibleOffset = getAccessibleOffset(gen, obj->appearance, pos);
-			if (!accessibleOffset.valid())
+			if (tryToPlaceObjectAndConnectToPath(gen, obj, pos) == EObjectPlacingResult::SUCCESS)
 			{
-				logGlobal->warnStream() << boost::format("Cannot access required object at position %s, retrying") % pos;
-				continue;
-			}
-			if (!connectPath(gen, accessibleOffset, true))
-			{
-				logGlobal->traceStream() << boost::format("Failed to create path to required object at position %s, retrying") % pos;
-				continue;
-			}
-			else
+				//paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones
+				placeObject(gen, obj, pos);
+				guardObject(gen, obj, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY), true);
 				break;
+			}
 		}
-
-
-		placeObject(gen, obj, pos);
-		guardObject (gen, obj, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY), true);
-		//paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones
 	}
 
 	for (const auto &obj : closeObjects)
 	{
-		std::vector<int3> tiles(possibleTiles.begin(), possibleTiles.end()); //new tiles vector after each object has been placed
+		setTemplateForObject(gen, obj.first);
+		auto tilesBlockedByObject = obj.first->getBlockedOffsets();
 
-		// smallest distance to zone center, greatest distance to nearest object
-		auto isCloser = [this, gen](const int3 & lhs, const int3 & rhs) -> bool
+		bool finished = false;
+		while (!finished)
 		{
-			float lDist = this->pos.dist2d(lhs);
-			float rDist = this->pos.dist2d(rhs);
-			lDist *= (lDist > 12) ? 10 : 1; //objects within 12 tile radius are preferred (smaller distance rating)
-			rDist *= (rDist > 12) ? 10 : 1;
+			std::vector<int3> tiles(possibleTiles.begin(), possibleTiles.end());
+			//new tiles vector after each object has been placed, OR misplaced area has been sealed off
 
-			return (lDist * 0.5f - std::sqrt(gen->getNearestObjectDistance(lhs))) < (rDist * 0.5f - std::sqrt(gen->getNearestObjectDistance(rhs)));
-		};
+			boost::remove_if(tiles, [gen, obj, this](int3 &tile)-> bool
+			{
+				//object must be accessible from at least one surounding tile
+				return !this->isAccessibleFromAnywhere(gen, obj.first->appearance, tile);
+			});
 
-		boost::sort (tiles, isCloser);
+			// smallest distance to zone center, greatest distance to nearest object
+			auto isCloser = [this, gen](const int3 & lhs, const int3 & rhs) -> bool
+			{
+				float lDist = this->pos.dist2d(lhs);
+				float rDist = this->pos.dist2d(rhs);
+				lDist *= (lDist > 12) ? 10 : 1; //objects within 12 tile radius are preferred (smaller distance rating)
+				rDist *= (rDist > 12) ? 10 : 1;
 
-		setTemplateForObject(gen, obj.first);
-		auto tilesBlockedByObject = obj.first->getBlockedOffsets();
-		bool result = false;
+				return (lDist * 0.5f - std::sqrt(gen->getNearestObjectDistance(lhs))) < (rDist * 0.5f - std::sqrt(gen->getNearestObjectDistance(rhs)));
+			};
 
-		for (auto tile : tiles)
-		{
-			//object must be accessible from at least one surounding tile
-			if (!isAccessibleFromAnywhere(gen, obj.first->appearance, tile))
-				continue;
+			boost::sort(tiles, isCloser);
 
-			//avoid borders
-			if (gen->isPossible(tile))
+			if (tiles.empty())
+			{
+				logGlobal->errorStream() << boost::format("Failed to fill zone %d due to lack of space") % id;
+				return false;
+			}
+			for (auto tile : tiles)
 			{
+				//code partially adapted from findPlaceForObject()
+
 				if (areAllTilesAvailable(gen, obj.first, tile, tilesBlockedByObject))
+					gen->setOccupied(pos, ETileType::BLOCKED); //why?
+				else
+					continue;
+
+				EObjectPlacingResult::EObjectPlacingResult result = tryToPlaceObjectAndConnectToPath(gen, obj.first, tile);
+				if (result == EObjectPlacingResult::SUCCESS)
 				{
 					placeObject(gen, obj.first, tile);
 					guardObject(gen, obj.first, obj.second, (obj.first->ID == Obj::MONOLITH_TWO_WAY), true);
-					result = true;
+					finished = true;
 					break;
 				}
+				else if (result == EObjectPlacingResult::CANNOT_FIT)
+					continue; // next tile
+				else if (result == EObjectPlacingResult::SEALED_OFF)
+				{
+					break; //tiles expired, pick new ones
+				}
+				else
+					throw (rmgException("Wrong result of tryToPlaceObjectAndConnectToPath()"));
 			}
 		}
-		if (!result)
-		{
-			logGlobal->errorStream() << boost::format("Failed to fill zone %d due to lack of space") % id;
-			//TODO CLEANUP!
-			return false;
-		}
 	}
 
 	return true;
@@ -2004,13 +2025,8 @@ bool CRmgTemplateZone::findPlaceForObject(CMapGenerator* gen, CGObjectInstance*
 	//we need object apperance to deduce free tile
 	setTemplateForObject(gen, obj);
 
-	//si32 min_dist = sqrt(tileinfo.size()/density);
 	int best_distance = 0;
 	bool result = false;
-	//si32 w = gen->map->width;
-	//si32 h = gen->map->height;
-
-	//logGlobal->infoStream() << boost::format("Min dist for density %f is %d") % density % min_dist;
 
 	auto tilesBlockedByObject = obj->getBlockedOffsets();
 

+ 10 - 0
lib/rmg/CRmgTemplateZone.h

@@ -34,6 +34,15 @@ namespace ETemplateZoneType
 		JUNCTION
 	};
 }
+namespace EObjectPlacingResult
+{
+	enum EObjectPlacingResult
+	{
+		SUCCESS,
+		CANNOT_FIT,
+		SEALED_OFF
+	};
+}
 class DLL_LINKAGE CTileInfo
 {
 public:
@@ -170,6 +179,7 @@ public:
 	void createBorder(CMapGenerator* gen);
 	void fractalize(CMapGenerator* gen);
 	void connectLater(CMapGenerator* gen);
+	EObjectPlacingResult::EObjectPlacingResult tryToPlaceObjectAndConnectToPath(CMapGenerator* gen, CGObjectInstance *obj, int3 &pos); //return true if the position cna be connected
 	bool createRequiredObjects(CMapGenerator* gen);
 	void createTreasures(CMapGenerator* gen);
 	void createObstacles1(CMapGenerator* gen);