2
0
Эх сурвалжийг харах

Improve treasure placement logic (#775)

Improve treasure placement logic (#775)
Nordsoft91 3 жил өмнө
parent
commit
454889598f

+ 1 - 1
lib/rmg/CMapGenerator.cpp

@@ -286,8 +286,8 @@ void CMapGenerator::fillZones()
 	
 	for(auto it : map->getZones())
 	{
-		it.second->initModificators();
 		it.second->initFreeTiles();
+		it.second->initModificators();
 	}
 
 	std::vector<std::shared_ptr<Zone>> treasureZones;

+ 2 - 2
lib/rmg/ConnectionsPlacer.cpp

@@ -224,10 +224,10 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
 				rmg::Area toPlace(rmgGate1.getArea() + rmgGate1.getAccessibleArea());
 				toPlace.translate(-zShift);
 				
-				path2 = managerOther.placeAndConnectObject(toPlace, rmgGate2, minDist, guarded2, true, false);
+				path2 = managerOther.placeAndConnectObject(toPlace, rmgGate2, minDist, guarded2, true, ObjectManager::OptimizeType::NONE);
 				
 				return path2.valid() ? 1.f : -1.f;
-			}, guarded1, true, false);
+			}, guarded1, true, ObjectManager::OptimizeType::NONE);
 			
 			if(path1.valid() && path2.valid())
 			{

+ 75 - 20
lib/rmg/ObjectManager.cpp

@@ -34,6 +34,16 @@ void ObjectManager::init()
 {
 	DEPENDENCY(WaterAdopter);
 	POSTFUNCTION(RoadPlacer);
+	createDistancesPriorityQueue();
+}
+
+void ObjectManager::createDistancesPriorityQueue()
+{
+	tilesByDistance.clear();
+	for (auto & tile : zone.areaPossible().getTilesVector())
+	{
+		tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
+	}
 }
 
 void ObjectManager::addRequiredObject(CGObjectInstance * obj, si32 strength)
@@ -53,10 +63,12 @@ void ObjectManager::addNearbyObject(CGObjectInstance * obj, CGObjectInstance * n
 
 void ObjectManager::updateDistances(const rmg::Object & obj)
 {
+	tilesByDistance.clear();
 	for (auto tile : zone.areaPossible().getTiles()) //don't need to mark distance for not possible tiles
 	{
 		ui32 d = obj.getArea().distanceSqr(tile); //optimization, only relative distance is interesting
 		map.setNearestObjectDistance(tile, std::min((float)d, map.getNearestObjectDistance(tile)));
+		tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
 	}
 }
 
@@ -65,59 +77,102 @@ const rmg::Area & ObjectManager::getVisitableArea() const
 	return objectsVisitableArea;
 }
 
-int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool optimizer) const
+int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, OptimizeType optimizer) const
 {
 	float bestWeight = 0.f;
 	int3 result(-1, -1, -1);
 	
-	for(const auto & tile : searchArea.getTiles())
+	if(optimizer & OptimizeType::DISTANCE)
 	{
-		obj.setPosition(tile);
-		
-		if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
-			continue;
-		
-		float weight = weightFunction(tile);
-		if(weight > bestWeight)
+		auto open = tilesByDistance;
+		while(!open.empty())
+		{
+			auto node = open.top();
+			open.pop();
+			int3 tile = node.first;
+			
+			if(!searchArea.contains(tile))
+				continue;
+			
+			obj.setPosition(tile);
+			
+			if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
+				continue;
+			
+			float weight = weightFunction(tile);
+			if(weight > bestWeight)
+			{
+				bestWeight = weight;
+				result = tile;
+				if(!(optimizer & OptimizeType::WEIGHT))
+					break;
+			}
+		}
+	}
+	else
+	{
+		for(const auto & tile : searchArea.getTiles())
 		{
-			bestWeight = weight;
-			result = tile;
-			if(!optimizer)
-				break;
+			obj.setPosition(tile);
+			
+			if(!searchArea.contains(obj.getArea()) || !searchArea.overlap(obj.getAccessibleArea()))
+				continue;
+			
+			float weight = weightFunction(tile);
+			if(weight > bestWeight)
+			{
+				bestWeight = weight;
+				result = tile;
+				if(!(optimizer & OptimizeType::WEIGHT))
+					break;
+			}
 		}
 	}
+	
 	if(result.valid())
 		obj.setPosition(result);
 	return result;
 }
 
-int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool optimizer) const
+int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, OptimizeType optimizer) const
 {
-	return findPlaceForObject(searchArea, obj, [this, min_dist](const int3 & tile)
+	return findPlaceForObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
 	{
 		auto ti = map.getTile(tile);
 		float dist = ti.getNearestObjectDistance();
 		if(dist < min_dist)
 			return -1.f;
 		
+		for(auto & t : obj.getArea().getTilesVector())
+		{
+			if(map.getTile(t).getNearestObjectDistance() < min_dist)
+				return -1.f;
+		}
+		
 		return dist;
 	}, optimizer);
 }
 
-rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, bool optimizer) const
+rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
 {
-	return placeAndConnectObject(searchArea, obj, [this, min_dist](const int3 & tile)
+	return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
 	{
 		auto ti = map.getTile(tile);
 		float dist = ti.getNearestObjectDistance();
 		if(dist < min_dist)
 			return -1.f;
 		
+		for(auto & t : obj.getArea().getTilesVector())
+		{
+			if(map.getTile(t).getNearestObjectDistance() < min_dist)
+				return -1.f;
+		}
+		
 		return dist;
 	}, isGuarded, onlyStraight, optimizer);
 }
 
-rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, bool optimizer) const
+rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
 {
 	int3 pos;
 	auto possibleArea = searchArea;
@@ -172,7 +227,7 @@ bool ObjectManager::createRequiredObjects()
 		rmg::Object rmgObject(*obj);
 		rmgObject.setTemplate(zone.getTerrainType());
 		bool guarded = addGuard(rmgObject, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY));
-		auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, true);
+		auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
 		
 		if(!path.valid())
 		{
@@ -217,7 +272,7 @@ bool ObjectManager::createRequiredObjects()
 			dist *= (dist > 12.f * 12.f) ? 10.f : 1.f; //tiles closer 12 are preferrable
 			dist = 1000000.f - dist; //some big number
 			return dist + map.getNearestObjectDistance(tile);
-		}, guarded, false, true);
+		}, guarded, false, OptimizeType::WEIGHT);
 		
 		if(!path.valid())
 		{

+ 26 - 4
lib/rmg/ObjectManager.h

@@ -12,13 +12,31 @@
 
 #include "Zone.h"
 #include "RmgObject.h"
+#include <boost/heap/priority_queue.hpp> //A*
 
 class CGObjectInstance;
 class ObjectTemplate;
 class CGCreature;
 
+typedef std::pair<int3, float> TDistance;
+struct DistanceMaximizeFunctor
+{
+	bool operator()(const TDistance & lhs, const TDistance & rhs) const
+	{
+		return (rhs.second > lhs.second);
+	}
+};
+
 class ObjectManager: public Modificator
 {
+public:
+	enum OptimizeType
+	{
+		NONE = 0x00000000,
+		WEIGHT = 0x00000001,
+		DISTANCE = 0x00000010
+	};
+	
 public:
 	MODIFICATOR(ObjectManager);
 	
@@ -31,17 +49,18 @@ public:
 		
 	bool createRequiredObjects();
 	
-	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool optimizer) const;
-	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool optimizer) const;
+	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, OptimizeType optimizer) const;
+	int3 findPlaceForObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, OptimizeType optimizer) const;
 	
-	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, bool optimizer) const;
-	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, bool optimizer) const;
+	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
+	rmg::Path placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, std::function<float(const int3)> weightFunction, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const;
 	
 	CGCreature * chooseGuard(si32 strength, bool zoneGuard = false);
 	bool addGuard(rmg::Object & object, si32 strength, bool zoneGuard = false);
 	void placeObject(rmg::Object & object, bool guarded, bool updateDistance);
 	
 	void updateDistances(const rmg::Object & obj);
+	void createDistancesPriorityQueue();
 	
 	const rmg::Area & getVisitableArea() const;
 	
@@ -53,4 +72,7 @@ protected:
 	std::vector<std::pair<CGObjectInstance*, CGObjectInstance*>> nearbyObjects;
 	std::vector<CGObjectInstance*> objects;
 	rmg::Area objectsVisitableArea;
+	
+	boost::heap::priority_queue<TDistance, boost::heap::compare<DistanceMaximizeFunctor>> tilesByDistance;
+	
 };

+ 1 - 1
lib/rmg/TownPlacer.cpp

@@ -142,7 +142,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
 	{
 		float distance = zone.getPos().dist2dSQ(t);
 		return 100000.f - distance; //some big number
-	}, true);
+	}, ObjectManager::OptimizeType::WEIGHT);
 	rmgObject.setPosition(position);
 	manager.placeObject(rmgObject, false, true);
 	cleanupBoundaries(rmgObject);

+ 41 - 7
lib/rmg/TreasurePlacer.cpp

@@ -545,7 +545,7 @@ std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo
 	return objectInfos;
 }
 
-rmg::Object TreasurePlacer::constuctTreasurePile(const std::vector<ObjectInfo*> & treasureInfos)
+rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*> & treasureInfos, bool densePlacement)
 {
 	rmg::Object rmgObject;
 	for(auto & oi : treasureInfos)
@@ -568,7 +568,32 @@ rmg::Object TreasurePlacer::constuctTreasurePile(const std::vector<ObjectInfo*>
 				return rmgObject;
 			}
 			
-			int3 nextPos = *RandomGeneratorUtil::nextItem(accessibleArea.getTiles(), generator.rand);
+			std::vector<int3> bestPositions;
+			if(densePlacement)
+			{
+				int bestPositionsWeight = std::numeric_limits<int>::max();
+				for(auto & t : accessibleArea.getTilesVector())
+				{
+					instance.setPosition(t);
+					int w = rmgObject.getAccessibleArea().getTilesVector().size();
+					if(w < bestPositionsWeight)
+					{
+						bestPositions.clear();
+						bestPositions.push_back(t);
+						bestPositionsWeight = w;
+					}
+					else if(w == bestPositionsWeight)
+					{
+						bestPositions.push_back(t);
+					}
+				}
+			}
+			else
+			{
+				bestPositions = accessibleArea.getTilesVector();
+			}
+			
+			int3 nextPos = *RandomGeneratorUtil::nextItem(bestPositions, generator.rand);
 			instance.setPosition(nextPos - rmgObject.getPosition());
 			
 			auto instanceAccessibleArea = instance.getAccessibleArea();
@@ -637,6 +662,8 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
 
 void TreasurePlacer::createTreasures(ObjectManager & manager)
 {
+	const int maxAttempts = 2;
+	
 	int mapMonsterStrength = map.getMapGenOptions().getMonsterStrength();
 	int monsterStrength = zone.zoneMonsterStrength + mapMonsterStrength - 1; //array index from 0 to 4
 	
@@ -684,7 +711,7 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 		const float minDistance = std::max<float>((125.f / totalDensity), 2.0f);
 		//distance lower than 2 causes objects to overlap and crash
 		
-		while(true)
+		for(int attempt = 0; attempt <= maxAttempts;)
 		{
 			auto treasurePileInfos = prepareTreasurePile(t);
 			if(treasurePileInfos.empty())
@@ -692,7 +719,7 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 			
 			int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0, [](int v, const ObjectInfo * oi){return v + oi->value;});
 			
-			auto rmgObject = constuctTreasurePile(treasurePileInfos);
+			auto rmgObject = constructTreasurePile(treasurePileInfos, attempt == maxAttempts);
 			if(rmgObject.instances().empty()) //handle incorrect placement
 			{
 				restoreZoneLimits(treasurePileInfos);
@@ -716,6 +743,12 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 					if(ti.getNearestObjectDistance() < minDistance)
 						return -1.f;
 					
+					for(auto & t : rmgObject.getArea().getTilesVector())
+					{
+						if(map.getTile(t).getNearestObjectDistance() < minDistance)
+							return -1.f;
+					}
+					
 					auto guardedArea = rmgObject.instances().back()->getAccessibleArea();
 					auto areaToBlock = rmgObject.getAccessibleArea(true);
 					areaToBlock.subtract(guardedArea);
@@ -723,11 +756,11 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 						return -1.f;
 					
 					return ti.getNearestObjectDistance();
-				}, guarded, false, false);
+				}, guarded, false, ObjectManager::OptimizeType::DISTANCE);
 			}
 			else
 			{
-				path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, false);
+				path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
 			}
 			
 			if(path.valid())
@@ -744,12 +777,13 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
 				}
 				zone.connectPath(path);
 				manager.placeObject(rmgObject, guarded, true);
+				attempt = 0;
 			}
 			else
 			{
 				restoreZoneLimits(treasurePileInfos);
 				rmgObject.clear();
-				break;
+				++attempt;
 			}
 		}
 	}

+ 1 - 1
lib/rmg/TreasurePlacer.h

@@ -52,7 +52,7 @@ protected:
 	
 	ObjectInfo * getRandomObject(ui32 desiredValue, ui32 currentValue, ui32 maxValue, bool allowLargeObjects);
 	std::vector<ObjectInfo*> prepareTreasurePile(const CTreasureInfo & treasureInfo);
-	rmg::Object constuctTreasurePile(const std::vector<ObjectInfo*> & treasureInfos);
+	rmg::Object constructTreasurePile(const std::vector<ObjectInfo*> & treasureInfos, bool densePlacement = false);
 	
 	
 protected:	

+ 2 - 2
lib/rmg/WaterProxy.cpp

@@ -230,7 +230,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
 		}
 		
 		//try to place boat at water, create paths on water and land
-		auto path = manager->placeAndConnectObject(shipPositions, rmgObject, 2, false, true, false);
+		auto path = manager->placeAndConnectObject(shipPositions, rmgObject, 2, false, true, ObjectManager::OptimizeType::NONE);
 		auto landPath = land.searchPath(boardingPosition, false);
 		if(!path.valid() || !landPath.valid())
 		{
@@ -298,7 +298,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, Route
 				return -1.f;
 			
 			return 1.0f;
-		}, guarded, true, false);
+		}, guarded, true, ObjectManager::OptimizeType::NONE);
 		
 		//search path to boarding position
 		auto searchArea = land.areaPossible() - rmgObject.getArea();