mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Correct placement of non-overlapping objects.
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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 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; | ||||
| 		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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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); | ||||
| 	}	 | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user