Browse Source

Complete solution for Subterranean Gate placement.

DjWarmonger 9 years ago
parent
commit
dea39b8497
3 changed files with 85 additions and 63 deletions
  1. 81 62
      lib/rmg/CMapGenerator.cpp
  2. 3 0
      lib/rmg/CRmgTemplateZone.cpp
  3. 1 1
      lib/rmg/CRmgTemplateZone.h

+ 81 - 62
lib/rmg/CMapGenerator.cpp

@@ -268,13 +268,14 @@ void CMapGenerator::fillZones()
 	for (auto faction : VLC->townh->getAllowedFactions())
 		zonesPerFaction[faction] = 0;
 
+	findZonesForQuestArts();
+
 	logGlobal->infoStream() << "Started filling zones";
 
 	//initialize possible tiles before any object is actually placed
 	for (auto it : zones)
 		it.second->initFreeTiles(this);
 
-	findZonesForQuestArts();
 	createDirectConnections(); //direct
 	//make sure all connections are passable before creating borders
 	for (auto it : zones)
@@ -545,100 +546,118 @@ void CMapGenerator::createConnections2()
 		auto zoneA = connection.getZoneA();
 		auto zoneB = connection.getZoneB();
 
-		auto tileSetA = zoneA->getPossibleTiles(),
-			tileSetB = zoneB->getPossibleTiles();
-
-		std::vector<int3> tilesA(tileSetA.begin(), tileSetA.end()),
-			tilesB(tileSetB.begin(), tileSetB.end());
-
 		int3 guardPos(-1, -1, -1);
 
 		int3 posA = zoneA->getPos();
 		int3 posB = zoneB->getPos();
-		auto zoneAid = zoneA->getId();
-		auto zoneBid = zoneB->getId();
+
+		auto strength = connection.getGuardStrength();
 
 		if (posA.z != posB.z) //try to place subterranean gates
 		{
-			//find common tiles for both zones
-
-			std::vector<int3> commonTiles;
+			auto sgt = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0)->getTemplates().front();
+			auto tilesBlockedByObject = sgt.getBlockedOffsets();
 
-			boost::sort(tilesA),
-				boost::sort(tilesB);
+			auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
+			auto gate1 = factory->create(ObjectTemplate());
+			auto gate2 = factory->create(ObjectTemplate());
 
-			boost::set_intersection(tilesA, tilesB, std::back_inserter(commonTiles), [](const int3 &lhs, const int3 &rhs) -> bool
+			while (!guardPos.valid())
 			{
-				//ignore z coordinate
-				if (lhs.x < rhs.x)
-					return true;
-				else
-					return lhs.y < rhs.y;
-			});
+				bool continueOuterLoop = false;
+				//find common tiles for both zones
+				auto tileSetA = zoneA->getPossibleTiles(),
+					tileSetB = zoneB->getPossibleTiles();
 
-			vstd::erase_if(commonTiles, [](const int3 &tile) -> bool
-			{
-				return (!tile.x) || (!tile.y); //gates shouldn't go outside map (x = 0) and look bad at the very top (y = 0)
-			});
+				std::vector<int3> tilesA(tileSetA.begin(), tileSetA.end()),
+					tilesB(tileSetB.begin(), tileSetB.end());
 
-			boost::sort(commonTiles, [posA, posB](const int3 &lhs, const int3 &rhs) -> bool
-			{
-				//choose tiles which are equidistant to zone centers
-				return (std::abs<double>(posA.dist2dSQ(lhs) - posB.dist2dSQ(lhs)) < std::abs<double>((posA.dist2dSQ(rhs) - posB.dist2dSQ(rhs))));
-			});
+				std::vector<int3> commonTiles;
 
-			auto sgt = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0)->getTemplates().front();
+				//required for set_intersection
+				boost::sort(tilesA);
+				boost::sort(tilesB);
 
-			for (auto tile : commonTiles)
-			{
-				tile.z = posA.z;
-				int3 otherTile = tile;
-				otherTile.z = posB.z;
+				boost::set_intersection(tilesA, tilesB, std::back_inserter(commonTiles), [](const int3 &lhs, const int3 &rhs) -> bool
+				{
+					//ignore z coordinate
+					if (lhs.x < rhs.x)
+						return true;
+					else
+						return lhs.y < rhs.y;
+				});
 
-				float distanceFromA = posA.dist2d(tile);
-				float distanceFromB = posB.dist2d(otherTile);
+				vstd::erase_if(commonTiles, [](const int3 &tile) -> bool
+				{
+					return (!tile.x) || (!tile.y); //gates shouldn't go outside map (x = 0) and look bad at the very top (y = 0)
+				});
 
-				if (distanceFromA > 5 && distanceFromB > 5)
+				if (commonTiles.empty())
+					break; //nothing more to do
+
+				boost::sort(commonTiles, [posA, posB](const int3 &lhs, const int3 &rhs) -> bool
 				{
-					//all neightbouring tiles also belong to zone
-					if (getZoneID(tile) == zoneAid && getZoneID(otherTile) == zoneBid)
-					{
-						bool withinZone = true;
+					//choose tiles which are equidistant to zone centers
+					return (std::abs<double>(posA.dist2dSQ(lhs) - posB.dist2dSQ(lhs)) < std::abs<double>((posA.dist2dSQ(rhs) - posB.dist2dSQ(rhs))));
+				});
 
-						foreach_neighbour(tile, [&withinZone, zoneAid, this](int3 &pos)
-						{
-							if (getZoneID(pos) != zoneAid)
-								withinZone = false;
-						});
-						foreach_neighbour(otherTile, [&withinZone, zoneBid, this](int3 &pos)
-						{
-							if (getZoneID(pos) != zoneBid)
-								withinZone = false;
-						});
-						if (withinZone)
+				for (auto tile : commonTiles)
+				{
+					tile.z = posA.z;
+					int3 otherTile = tile;
+					otherTile.z = posB.z;
+
+					float distanceFromA = posA.dist2d(tile);
+					float distanceFromB = posB.dist2d(otherTile);
+
+					if (distanceFromA > 5 && distanceFromB > 5)
+					{
+						if (zoneA->areAllTilesAvailable(this, gate1, tile, tilesBlockedByObject) &&
+							zoneB->areAllTilesAvailable(this, gate2, otherTile, tilesBlockedByObject))
 						{
-							//make sure both gates has some free tiles below them
 							if (zoneA->getAccessibleOffset(this, sgt, tile).valid() && zoneB->getAccessibleOffset(this, sgt, otherTile).valid())
 							{
-								zoneA->placeSubterraneanGate(this, tile, connection.getGuardStrength());
-								zoneB->placeSubterraneanGate(this, otherTile, connection.getGuardStrength());
-								guardPos = tile; //just any valid value
-								break; //we're done
+								EObjectPlacingResult::EObjectPlacingResult result1 = zoneA->tryToPlaceObjectAndConnectToPath(this, gate1, tile);
+								EObjectPlacingResult::EObjectPlacingResult result2 = zoneB->tryToPlaceObjectAndConnectToPath(this, gate2, otherTile);
+
+								if ((result1 == EObjectPlacingResult::SUCCESS) && (result2 == EObjectPlacingResult::SUCCESS))
+								{
+									zoneA->placeObject(this, gate1, tile);
+									zoneA->guardObject(this, gate1, strength, true, true);
+									zoneB->placeObject(this, gate2, otherTile);
+									zoneB->guardObject(this, gate2, strength, true, true);
+									guardPos = tile; //set to break the loop
+									break;
+								}
+								else if ((result1 == EObjectPlacingResult::SEALED_OFF) || (result2 == EObjectPlacingResult::SEALED_OFF))
+								{
+									//sealed-off tiles were blocked, exit inner loop and get another tile set
+									bool continueOuterLoop = true;
+									break;
+								}
+								else
+									continue; //try with another position
 							}
 						}
 					}
 				}
+				if (!continueOuterLoop) //we didn't find ANY tile - break outer loop			
+					break;
+			}
+			if (!guardPos.valid()) //cleanup? is this safe / enough?
+			{
+				delete gate1;
+				delete gate2;
 			}
 		}
 		if (!guardPos.valid())
 		{
 			auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, getNextMonlithIndex());
 			auto teleport1 = factory->create(ObjectTemplate());
-
 			auto teleport2 = factory->create(ObjectTemplate());
 
-			zoneA->addRequiredObject(teleport1, connection.getGuardStrength());
-			zoneB->addRequiredObject(teleport2, connection.getGuardStrength());
+			zoneA->addRequiredObject(teleport1, strength);
+			zoneB->addRequiredObject(teleport2, strength);
 		}
 	}
 }

+ 3 - 0
lib/rmg/CRmgTemplateZone.cpp

@@ -427,7 +427,10 @@ void CRmgTemplateZone::initFreeTiles (CMapGenerator* gen)
 		return gen->isPossible(tile);
 	});
 	if (freePaths.empty())
+	{
+		gen->setOccupied(pos, ETileType::FREE);
 		freePaths.insert(pos); //zone must have at least one free tile where other paths go - for instance in the center
+	}
 }
 
 void CRmgTemplateZone::createBorder(CMapGenerator* gen)

+ 1 - 1
lib/rmg/CRmgTemplateZone.h

@@ -191,6 +191,7 @@ public:
 	bool connectWithCenter(CMapGenerator* gen, const int3& src, bool onlyStraight);
 
 	std::vector<int3> getAccessibleOffsets (CMapGenerator* gen, CGObjectInstance* object);
+	bool areAllTilesAvailable(CMapGenerator* gen, CGObjectInstance* obj, int3& tile, std::set<int3>& tilesBlockedByObject) const;
 
 	void addConnection(TRmgTemplateZoneId otherZone);
 	void setQuestArtZone(CRmgTemplateZone * otherZone);
@@ -257,6 +258,5 @@ private:
 	bool findPlaceForTreasurePile(CMapGenerator* gen, float min_dist, int3 &pos, int value);
 	bool canObstacleBePlacedHere(CMapGenerator* gen, ObjectTemplate &temp, int3 &pos);
 	void setTemplateForObject(CMapGenerator* gen, CGObjectInstance* obj);
-	bool areAllTilesAvailable(CMapGenerator* gen, CGObjectInstance* obj, int3& tile, std::set<int3>& tilesBlockedByObject) const;
 	void checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos);
 };