瀏覽代碼

Correct placement of non-overlapping objects.

DjWarmonger 11 年之前
父節點
當前提交
22d26db694

+ 1 - 7
lib/mapObjects/CObjectHandler.cpp

@@ -241,13 +241,7 @@ int CGObjectInstance::getSightRadious() const
 
 int3 CGObjectInstance::getVisitableOffset() const
 {
-	for(int y = 0; y < appearance.getHeight(); y++)
-		for (int x = 0; x < appearance.getWidth(); x++)
-			if (appearance.isVisitableAt(x, y))
-				return int3(x,y,0);
-
-    //logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!";
-	return int3(0,0,0);
+	return appearance.getVisitableOffset();
 }
 
 void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const

+ 45 - 26
lib/mapObjects/ObjectTemplate.cpp

@@ -23,31 +23,34 @@
  *
  */
 
-static bool isVisitableFromTop(int identifier, int type)
+namespace vstd
 {
-	if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource
-		return true;
-
-	static const Obj visitableFromTop[] =
-		{Obj::FLOTSAM,
-		Obj::SEA_CHEST,
-		Obj::SHIPWRECK_SURVIVOR,
-		Obj::BUOY,
-		Obj::OCEAN_BOTTLE,
-		Obj::BOAT,
-		Obj::WHIRLPOOL,
-		Obj::GARRISON,
-		Obj::GARRISON2,
-		Obj::SCHOLAR,
-		Obj::CAMPFIRE,
-		Obj::BORDERGUARD,
-		Obj::BORDER_GATE,
-		Obj::QUEST_GUARD,
-		Obj::CORPSE
-	};
-	if (vstd::find_pos(visitableFromTop, identifier) != -1)
-		return true;
-	return false;
+	static bool isVisitableFromTop(int identifier, int type)
+	{
+		if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource
+			return true;
+
+		static const Obj visitableFromTop[] =
+			{Obj::FLOTSAM,
+			Obj::SEA_CHEST,
+			Obj::SHIPWRECK_SURVIVOR,
+			Obj::BUOY,
+			Obj::OCEAN_BOTTLE,
+			Obj::BOAT,
+			Obj::WHIRLPOOL,
+			Obj::GARRISON,
+			Obj::GARRISON2,
+			Obj::SCHOLAR,
+			Obj::CAMPFIRE,
+			Obj::BORDERGUARD,
+			Obj::BORDER_GATE,
+			Obj::QUEST_GUARD,
+			Obj::CORPSE
+		};
+		if (vstd::find_pos(visitableFromTop, identifier) != -1)
+			return true;
+		return false;
+	}
 }
 
 ObjectTemplate::ObjectTemplate():
@@ -106,7 +109,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
 	int type  = boost::lexical_cast<int>(strings[7]);
 	printPriority = boost::lexical_cast<int>(strings[8]) * 100; // to have some space in future
 
-	if (isVisitableFromTop(id, type))
+	if (vstd::isVisitableFromTop(id, type))
 		visitDir = 0xff;
 	else
 		visitDir = (8|16|32|64|128);
@@ -168,7 +171,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
 	int type = reader.readUInt8();
 	printPriority = reader.readUInt8() * 100; // to have some space in future
 
-	if (isVisitableFromTop(id, type))
+	if (vstd::isVisitableFromTop(id, type))
 		visitDir = 0xff;
 	else
 		visitDir = (8|16|32|64|128);
@@ -354,6 +357,22 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
 	return dirMap[dy][dx] != 0;
 }
 
+int3 ObjectTemplate::getVisitableOffset() const
+{
+	for(int y = 0; y < getHeight(); y++)
+		for (int x = 0; x < getWidth(); x++)
+			if (isVisitableAt(x, y))
+				return int3(x,y,0);
+
+    //logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!";
+	return int3(0,0,0);
+}
+
+bool ObjectTemplate::isVisitableFromTop() const
+{
+	return isVisitableFrom (0, 1);
+}
+
 bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const
 {
 	return allowedTerrains.count(terrain) != 0;

+ 2 - 0
lib/mapObjects/ObjectTemplate.h

@@ -62,6 +62,8 @@ public:
 
 	// Checks if object is visitable from certain direction. X and Y must be between -1..+1
 	bool isVisitableFrom(si8 X, si8 Y) const;
+	int3 getVisitableOffset() const;
+	bool isVisitableFromTop() const;
 
 	// Checks if object can be placed on specific terrain
 	bool canBePlacedAt(ETerrainType terrain) const;

+ 1 - 1
lib/rmg/CMapGenerator.cpp

@@ -216,9 +216,9 @@ void CMapGenerator::fillZones()
 	logGlobal->infoStream() << "Started filling zones";
 
 	createConnections();
+	//make sure all connections are passable before creating borders
 	for (auto it : zones)
 	{
-		//make sure all connections are passable before creating borders
 		it.second->createBorder(this);
 		it.second->fill(this);
 	}	

+ 102 - 27
lib/rmg/CRmgTemplateZone.cpp

@@ -625,10 +625,12 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength)
 
 bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
 {
+	CTreasurePileInfo info;
+
 	std::map<int3, CGObjectInstance *> treasures;
 	std::set<int3> boundary;
 	int3 guardPos (-1,-1,-1);
-	int3 nextTreasurePos = pos;
+	info.nextTreasurePos = pos;
 
 	//default values
 	int maxValue = 5000;
@@ -654,9 +656,8 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
 	CGObjectInstance * object = nullptr;
 	while (currentValue < minValue)
 	{
-		//TODO: this works only for 1-tile objects
 		//make sure our shape is consistent
-		treasures[nextTreasurePos] = nullptr;
+		treasures[info.nextTreasurePos] = nullptr;
 		for (auto treasurePos : treasures)
 		{
 			gen->foreach_neighbour (treasurePos.first, [gen, &boundary](int3 pos)
@@ -678,14 +679,30 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
 
 		int remaining = maxValue - currentValue;
 
-		auto oi = getRandomObject(gen, remaining);
+		ObjectInfo oi = getRandomObject(gen, info, remaining);
 		object = oi.generateObject();
 		if (!object)
+		{
+			vstd::erase_if_present(treasures, info.nextTreasurePos);
 			break;
+		}
+		else
+		{
+			//update treasure pile area
+			int3 visitablePos = oi.templ.getVisitableOffset() + info.nextTreasurePos;
+
+			info.visitablePositions.insert(visitablePos); //can be accessed only from bottom or side
+			if (oi.templ.isVisitableFromTop())
+				info.visitableFromTopPositions.insert(visitablePos); //can be accessed from any direction
+
+			for (auto blockedOffset : oi.templ.getBlockedOffsets())
+				info.occupiedPositions.insert(info.nextTreasurePos + blockedOffset);
+			info.occupiedPositions.insert(visitablePos);
+		}
 
 		currentValue += oi.value;
 		
-		treasures[nextTreasurePos] = object;
+		treasures[info.nextTreasurePos] = object;
 
 		//now find place for next object
 		int3 placeFound(-1,-1,-1);
@@ -712,8 +729,9 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
 			}
 		}
 		if (placeFound.valid())
-			nextTreasurePos = placeFound;
+			info.nextTreasurePos = placeFound;
 	}
+
 	if (treasures.size())
 	{
 		//find object closest to zone center, then con nect it to the middle of the zone
@@ -752,21 +770,8 @@ bool CRmgTemplateZone::createTreasurePile (CMapGenerator* gen, int3 &pos)
 		{
 			for (auto treasure : treasures)
 			{
-				bool objectFitsHere = true; //temporary workaround
 				int3 visitableOffset = treasure.second->getVisitableOffset();
-				std::set<int3> blockedOffsets = treasure.second->getBlockedOffsets();
-				blockedOffsets.insert (visitableOffset);
-				for (auto blockingTile : blockedOffsets)
-				{
-					int3 t = treasure.first + visitableOffset + blockingTile;
-					if (!gen->map->isInTheMap(t))
-					{
-						objectFitsHere = false; //if at least one tile is not possible, object can't be placed here
-						break;
-					}
-				}
-				if (objectFitsHere)
-					placeObject(gen, treasure.second, treasure.first + visitableOffset);
+				placeObject(gen, treasure.second, treasure.first + visitableOffset);
 			}
 			if (addMonster(gen, guardPos, currentValue))
 			{//block only if the object is guarded
@@ -1362,7 +1367,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
 	return true;
 }
 
-ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
+ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 value)
 {
 	std::vector<std::pair<ui32, ObjectInfo>> tresholds;
 	ui32 total = 0;
@@ -1374,17 +1379,73 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
 	{
 		if (oi.value >= minValue && oi.value <= value)
 		{
-			//TODO: check place for non-removable object
-			//problem: we need at least template for the object that does not yet exist
+			int3 visitableOffset = oi.templ.getVisitableOffset(); //visitablePos assumes object will be shifter by visitableOffset
+			if (info.visitablePositions.size()) //do not try to match first object in zone
+			{
+				bool fitsHere = false;
+				int3 visitablePos = info.nextTreasurePos;
+				if (oi.templ.isVisitableFromTop())
+				{
+					for (auto tile : info.visitablePositions)
+					{
+						int3 actualTile = tile + visitableOffset;
+						if (visitablePos.areNeighbours(actualTile)) //we access object from any position
+						{
+							fitsHere = true;
+							break;
+						}
+					}
+				}
+				else
+				{
+					//if object is not visitable from top, it must be accessible from below or side
+					for (auto tile : info.visitableFromTopPositions)
+					{
+						int3 actualTile = tile + visitableOffset;
+						if (visitablePos.areNeighbours(actualTile) && visitablePos.y >= actualTile.y) //we access object from below or side
+						{
+							fitsHere = true;
+							break;
+						}
+					}
+				}
+
+				if (!fitsHere)
+					continue;
+			}
+
+				//now check blockmap, including our already reserved pile area
+
+			bool fitsBlockmap = true;
+
+			std::set<int3> blockedOffsets = oi.templ.getBlockedOffsets();
+			blockedOffsets.insert (visitableOffset);
+			for (auto blockingTile : blockedOffsets)
+			{
+				int3 t = info.nextTreasurePos + visitableOffset + blockingTile;
+				if (!gen->map->isInTheMap(t) || vstd::contains(info.occupiedPositions, t))
+				{
+					fitsBlockmap = false; //if at least one tile is not possible, object can't be placed here
+					break;
+				}
+				if (!(gen->isPossible(t) || gen->isBlocked(t))) //blocked tiles of object may cover blocked tiles, but not used or free tiles
+				{
+					fitsBlockmap = false;
+					break;
+				}
+			}
+			if (!fitsBlockmap)
+				continue;
+
 			total += oi.probability;
 			tresholds.push_back (std::make_pair (total, oi));
 		}
 	}
 
-	//TODO: generate pandora box with gold if the value is very high
+	//Generate pandora Box with gold if the value is extremely high
+	ObjectInfo oi;
 	if (tresholds.empty())
 	{
-		ObjectInfo oi;
 		if (minValue > 20000) //we don't have object valuable enough
 		{
 			oi.generateObject = [minValue]() -> CGObjectInstance *
@@ -1395,6 +1456,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
 				obj->resources[Res::GOLD] = minValue;
 				return obj;
 			};
+			oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
 			oi.value = minValue;
 			oi.probability = 0;
 		}
@@ -1404,6 +1466,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
 			{
 				return nullptr;
 			};
+			oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); //TODO: null template or something? should be never used, but hell knows
 			oi.value = 0;
 			oi.probability = 0;
 		}
@@ -1422,8 +1485,6 @@ ObjectInfo CRmgTemplateZone::getRandomObject (CMapGenerator* gen, ui32 value)
 
 void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 {
-	//TODO: move typical objects to config
-
 	ObjectInfo oi;
 
 	for (auto primaryID : VLC->objtypeh->knownObjects()) 
@@ -1443,6 +1504,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 						};
 						oi.value = handler->getRMGInfo().value;
 						oi.probability = handler->getRMGInfo().rarity;
+						oi.templ = temp;
 						possibleObjects.push_back (oi);
 					}
 				}
@@ -1484,6 +1546,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 						return obj;
 					};
 
+					oi.templ = temp;
 					possibleObjects.push_back (oi);
 				}
 			}
@@ -1515,6 +1578,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 			obj->storedArtifact = a;
 			return obj;
 		};
+		oi.setTemplate (Obj::SPELL_SCROLL, 0, terrainType);
 		oi.value = scrollValues[i];
 		oi.probability = 30;
 		possibleObjects.push_back (oi);
@@ -1531,6 +1595,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 			obj->resources[Res::GOLD] = i * 5000;
 			return obj;
 		};
+		oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
 		oi.value = i * 5000;;
 		oi.probability = 5;
 		possibleObjects.push_back (oi);
@@ -1547,6 +1612,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 			obj->gainedExp = i * 5000;
 			return obj;
 		};
+		oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
 		oi.value = i * 6000;;
 		oi.probability = 20;
 		possibleObjects.push_back (oi);
@@ -1586,6 +1652,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 				obj->creatures.putStack(SlotID(0), stack);
 				return obj;
 			};
+			oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
 			oi.value = (2 * (creature->AIValue) * creaturesAmount * (1 + (float)(gen->getZoneCount(creature->faction)) / gen->getTotalZoneCount()))/3; //TODO: count number of towns on the map
 			oi.probability = 3;
 			possibleObjects.push_back (oi);
@@ -1616,6 +1683,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 
 			return obj;
 		};
+		oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
 		oi.value = (i + 1) * 2500; //5000 - 15000
 		oi.probability = 2;
 		possibleObjects.push_back (oi);
@@ -1660,6 +1728,7 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 
 			return obj;
 		};
+		oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
 		oi.value = 15000;
 		oi.probability = 2;
 		possibleObjects.push_back (oi);
@@ -1688,7 +1757,13 @@ void CRmgTemplateZone::addAllPossibleObjects (CMapGenerator* gen)
 
 		return obj;
 	};
+	oi.setTemplate (Obj::PANDORAS_BOX, 0, terrainType);
 	oi.value = 3000;
 	oi.probability = 2;
 	possibleObjects.push_back (oi);
 }
+
+void ObjectInfo::setTemplate (si32 type, si32 subtype, ETerrainType terrainType)
+{
+	templ = VLC->objtypeh->getHandlerFor(type, subtype)->getTemplates(terrainType).front();
+}

+ 13 - 1
lib/rmg/CRmgTemplateZone.h

@@ -16,6 +16,7 @@
 #include "float3.h"
 #include "../int3.h"
 #include "../ResourceSet.h" //for TResource (?)
+#include "../mapObjects/ObjectTemplate.h"
 
 class CMapGenerator;
 class CTileInfo;
@@ -67,9 +68,20 @@ public:
 
 struct DLL_LINKAGE ObjectInfo
 {
+	ObjectTemplate templ;
 	ui32 value;
 	ui16 probability;
 	std::function<CGObjectInstance *()> generateObject;
+
+	void setTemplate (si32 type, si32 subtype, ETerrainType terrain);
+};
+
+struct DLL_LINKAGE CTreasurePileInfo
+{
+	std::set<int3> visitablePositions; //can be visited only from bottom or side
+	std::set<int3> visitableFromTopPositions; //they can be visited from any direction
+	std::set<int3> occupiedPositions;
+	int3 nextTreasurePos;
 };
 
 /// The CRmgTemplateZone describes a zone in a template.
@@ -155,7 +167,7 @@ public:
 	std::vector<CTreasureInfo> getTreasureInfo();
 	std::set<int3>* getFreePaths();
 
-	ObjectInfo getRandomObject (CMapGenerator* gen, ui32 value);
+	ObjectInfo getRandomObject (CMapGenerator* gen, CTreasurePileInfo &info, ui32 value);
 
 	void placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str);