mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Complete solution for Subterranean Gate placement.
This commit is contained in:
		| @@ -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 | ||||
| 			auto sgt = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0)->getTemplates().front(); | ||||
| 			auto tilesBlockedByObject = sgt.getBlockedOffsets(); | ||||
|  | ||||
| 			std::vector<int3> commonTiles; | ||||
| 			auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0); | ||||
| 			auto gate1 = factory->create(ObjectTemplate()); | ||||
| 			auto gate2 = factory->create(ObjectTemplate()); | ||||
|  | ||||
| 			boost::sort(tilesA), | ||||
| 			while (!guardPos.valid()) | ||||
| 			{ | ||||
| 				bool continueOuterLoop = false; | ||||
| 				//find common tiles for both zones | ||||
| 				auto tileSetA = zoneA->getPossibleTiles(), | ||||
| 					tileSetB = zoneB->getPossibleTiles(); | ||||
|  | ||||
| 				std::vector<int3> tilesA(tileSetA.begin(), tileSetA.end()), | ||||
| 					tilesB(tileSetB.begin(), tileSetB.end()); | ||||
|  | ||||
| 				std::vector<int3> commonTiles; | ||||
|  | ||||
| 				//required for set_intersection | ||||
| 				boost::sort(tilesA); | ||||
| 				boost::sort(tilesB); | ||||
|  | ||||
| 			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; | ||||
| 			}); | ||||
|  | ||||
| 			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) | ||||
| 			}); | ||||
|  | ||||
| 			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)))); | ||||
| 			}); | ||||
|  | ||||
| 			auto sgt = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0)->getTemplates().front(); | ||||
|  | ||||
| 			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) | ||||
| 				boost::set_intersection(tilesA, tilesB, std::back_inserter(commonTiles), [](const int3 &lhs, const int3 &rhs) -> bool | ||||
| 				{ | ||||
| 					//all neightbouring tiles also belong to zone | ||||
| 					if (getZoneID(tile) == zoneAid && getZoneID(otherTile) == zoneBid) | ||||
| 					{ | ||||
| 						bool withinZone = true; | ||||
| 					//ignore z coordinate | ||||
| 					if (lhs.x < rhs.x) | ||||
| 						return true; | ||||
| 					else | ||||
| 						return lhs.y < rhs.y; | ||||
| 				}); | ||||
|  | ||||
| 						foreach_neighbour(tile, [&withinZone, zoneAid, this](int3 &pos) | ||||
| 				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 (commonTiles.empty()) | ||||
| 					break; //nothing more to do | ||||
|  | ||||
| 				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)))); | ||||
| 				}); | ||||
|  | ||||
| 				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)) | ||||
| 						{ | ||||
| 							if (getZoneID(pos) != zoneAid) | ||||
| 								withinZone = false; | ||||
| 						}); | ||||
| 						foreach_neighbour(otherTile, [&withinZone, zoneBid, this](int3 &pos) | ||||
| 						{ | ||||
| 							if (getZoneID(pos) != zoneBid) | ||||
| 								withinZone = false; | ||||
| 						}); | ||||
| 						if (withinZone) | ||||
| 						{ | ||||
| 							//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); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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); | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user