瀏覽代碼

Merge pull request #2981 from Nordsoft91/rmg-template

Select random object template instead of first
Nordsoft91 2 年之前
父節點
當前提交
80be1e9f4c

+ 11 - 10
lib/rmg/RmgObject.cpp

@@ -111,18 +111,18 @@ void Object::Instance::setPositionRaw(const int3 & position)
 	dObject.pos = dPosition + dParent.getPosition();
 }
 
-void Object::Instance::setAnyTemplate()
+void Object::Instance::setAnyTemplate(CRandomGenerator & rng)
 {
 	auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates();
 	if(templates.empty())
 		throw rmgException(boost::str(boost::format("Did not find any graphics for object (%d,%d)") % dObject.ID % dObject.subID));
 
-	dObject.appearance = templates.front();
+	dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng);
 	dAccessibleAreaCache.clear();
 	setPosition(getPosition(false));
 }
 
-void Object::Instance::setTemplate(TerrainId terrain)
+void Object::Instance::setTemplate(TerrainId terrain, CRandomGenerator & rng)
 {
 	auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
 	if (templates.empty())
@@ -130,7 +130,8 @@ void Object::Instance::setTemplate(TerrainId terrain)
 		auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated();
 		throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName));
 	}
-	dObject.appearance = templates.front();
+	
+	dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng);
 	dAccessibleAreaCache.clear();
 	setPosition(getPosition(false));
 }
@@ -280,10 +281,10 @@ void Object::setPosition(const int3 & position)
 		i.setPositionRaw(i.getPosition());
 }
 
-void Object::setTemplate(const TerrainId & terrain)
+void Object::setTemplate(const TerrainId & terrain, CRandomGenerator & rng)
 {
 	for(auto& i : dInstances)
-		i.setTemplate(terrain);
+		i.setTemplate(terrain, rng);
 }
 
 const Area & Object::getArea() const
@@ -325,7 +326,7 @@ void rmg::Object::setGuardedIfMonster(const Instance& object)
 	}
 }
 
-void Object::Instance::finalize(RmgMap & map)
+void Object::Instance::finalize(RmgMap & map, CRandomGenerator & rng)
 {
 	if(!map.isOnMap(getPosition(true)))
 		throw rmgException(boost::str(boost::format("Position of object %d at %s is outside the map") % dObject.id % getPosition(true).toString()));
@@ -341,7 +342,7 @@ void Object::Instance::finalize(RmgMap & map)
 		}
 		else
 		{
-			setTemplate(terrainType->getId());
+			setTemplate(terrainType->getId(), rng);
 		}
 	}
 
@@ -362,14 +363,14 @@ void Object::Instance::finalize(RmgMap & map)
 	map.getMapProxy()->insertObject(&dObject);
 }
 
-void Object::finalize(RmgMap & map)
+void Object::finalize(RmgMap & map, CRandomGenerator & rng)
 {
 	if(dInstances.empty())
 		throw rmgException("Cannot finalize object without instances");
 
 	for(auto & dInstance : dInstances)
 	{
-		dInstance.finalize(map);
+		dInstance.finalize(map, rng);
 	}
 }
 

+ 6 - 5
lib/rmg/RmgObject.h

@@ -17,6 +17,7 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CGObjectInstance;
+class CRandomGenerator;
 class RmgMap;
 
 namespace rmg {
@@ -35,8 +36,8 @@ public:
 		int3 getVisitablePosition() const;
 		bool isVisitableFrom(const int3 & tile) const;
 		const Area & getAccessibleArea() const;
-		void setTemplate(TerrainId terrain); //cache invalidation
-		void setAnyTemplate(); //cache invalidation
+		void setTemplate(TerrainId terrain, CRandomGenerator &); //cache invalidation
+		void setAnyTemplate(CRandomGenerator &); //cache invalidation
 		
 		int3 getTopTile() const;
 		int3 getPosition(bool isAbsolute = false) const;
@@ -45,7 +46,7 @@ public:
 		const CGObjectInstance & object() const;
 		CGObjectInstance & object();
 		
-		void finalize(RmgMap & map); //cache invalidation
+		void finalize(RmgMap & map, CRandomGenerator &); //cache invalidation
 		void clear();
 		
 	private:
@@ -73,7 +74,7 @@ public:
 	
 	const int3 & getPosition() const;
 	void setPosition(const int3 & position);
-	void setTemplate(const TerrainId & terrain);
+	void setTemplate(const TerrainId & terrain, CRandomGenerator &);
 	
 	const Area & getArea() const;  //lazy cache invalidation
 	const int3 getVisibleTop() const;
@@ -81,7 +82,7 @@ public:
 	bool isGuarded() const;
 	void setGuardedIfMonster(const Instance & object);
 	
-	void finalize(RmgMap & map);
+	void finalize(RmgMap & map, CRandomGenerator &);
 	void clear();
 	
 private:

+ 2 - 2
lib/rmg/modificators/ConnectionsPlacer.cpp

@@ -316,8 +316,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
 			auto * gate2 = factory->create();
 			rmg::Object rmgGate1(*gate1);
 			rmg::Object rmgGate2(*gate2);
-			rmgGate1.setTemplate(zone.getTerrainType());
-			rmgGate2.setTemplate(otherZone->getTerrainType());
+			rmgGate1.setTemplate(zone.getTerrainType(), zone.getRand());
+			rmgGate2.setTemplate(otherZone->getTerrainType(), zone.getRand());
 			bool guarded1 = manager.addGuard(rmgGate1, connection.getGuardStrength(), true);
 			bool guarded2 = managerOther.addGuard(rmgGate2, connection.getGuardStrength(), true);
 			int minDist = 3;

+ 4 - 15
lib/rmg/modificators/ObjectDistributor.cpp

@@ -79,25 +79,14 @@ void ObjectDistributor::distributeLimitedObjects()
 
 					for (auto& zone : matchingZones)
 					{
-						//We already know there are some templates
-						auto templates = handler->getTemplates(zone->getTerrainType());
-
-						//FIXME: Templates empty?! Maybe zone changed terrain type over time?
-
-						//Assume the template with fewest terrains is the most suitable
-						auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
-						{
-							return lhs->getAllowedTerrains().size() < rhs->getAllowedTerrains().size();
-						});
-
-						oi.generateObject = [temp]() -> CGObjectInstance *
+						oi.generateObject = [primaryID, secondaryID]() -> CGObjectInstance *
 						{
-							return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
+							return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create();
 						};
 						
 						oi.value = rmgInfo.value;
 						oi.probability = rmgInfo.rarity;
-						oi.templ = temp;
+						oi.setTemplates(primaryID, secondaryID, zone->getTerrainType());
 
 						//Rounding up will make sure all possible objects are exhausted
 						uint32_t mapLimit = rmgInfo.mapLimit.value();
@@ -109,7 +98,7 @@ void ObjectDistributor::distributeLimitedObjects()
 
 						rmgInfo.setMapLimit(mapLimit - oi.maxPerZone);
 						//Don't add objects with 0 count remaining
-						if (oi.maxPerZone)
+						if(oi.maxPerZone && !oi.templates.empty())
 						{
 							zone->getModificator<TreasurePlacer>()->addObjectToRandomPool(oi);
 						}

+ 5 - 5
lib/rmg/modificators/ObjectManager.cpp

@@ -354,7 +354,7 @@ bool ObjectManager::createRequiredObjects()
 	for(const auto & objInfo : requiredObjects)
 	{
 		rmg::Object rmgObject(*objInfo.obj);
-		rmgObject.setTemplate(zone.getTerrainType());
+		rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
 		bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
 
 		Zone::Lock lock(zone.areaMutex);
@@ -394,7 +394,7 @@ bool ObjectManager::createRequiredObjects()
 		auto possibleArea = zone.areaPossible();
 
 		rmg::Object rmgObject(*objInfo.obj);
-		rmgObject.setTemplate(zone.getTerrainType());
+		rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
 		bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
 		auto path = placeAndConnectObject(zone.areaPossible(), rmgObject,
 										  [this, &rmgObject](const int3 & tile)
@@ -480,7 +480,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
 		if (!monster->object().appearance)
 		{
 			//Needed to determine visitable offset
-			monster->setAnyTemplate();
+			monster->setAnyTemplate(zone.getRand());
 		}
 		object.getPosition();
 		auto visitableOffset = monster->object().getVisitableOffset();
@@ -492,7 +492,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
 		int3 parentOffset = monster->getPosition(true) - monster->getPosition(false);
 		monster->setPosition(fixedPos - parentOffset);
 	}
-	object.finalize(map);
+	object.finalize(map, zone.getRand());
 
 	Zone::Lock lock(zone.areaMutex);
 	zone.areaPossible().subtract(object.getArea());
@@ -689,7 +689,7 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
 	});
 	
 	auto & instance = object.addInstance(*guard);
-	instance.setAnyTemplate(); //terrain is irrelevant for monsters, but monsters need some template now
+	instance.setAnyTemplate(zone.getRand()); //terrain is irrelevant for monsters, but monsters need some template now
 
 	//Fix HoTA monsters with offset template
 	auto visitableOffset = instance.object().getVisitableOffset();

+ 1 - 1
lib/rmg/modificators/RiverPlacer.cpp

@@ -397,7 +397,7 @@ void RiverPlacer::connectRiver(const int3 & tile)
 				{
 					auto * obj = handler->create(templ);
 					rmg::Object deltaObj(*obj, deltaPositions[pos]);
-					deltaObj.finalize(map);
+					deltaObj.finalize(map, zone.getRand());
 				}
 			}
 		}

+ 1 - 1
lib/rmg/modificators/TownPlacer.cpp

@@ -140,7 +140,7 @@ int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
 {
 	//towns are big objects and should be centered around visitable position
 	rmg::Object rmgObject(town);
-	rmgObject.setTemplate(zone.getTerrainType());
+	rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
 
 	int3 position(-1, -1, -1);
 	{

+ 64 - 61
lib/rmg/modificators/TreasurePlacer.cpp

@@ -72,26 +72,16 @@ void TreasurePlacer::addAllPossibleObjects()
 					continue;
 				}
 
-				auto templates = handler->getTemplates(zone.getTerrainType());
-				if (templates.empty())
-					continue;
-
-				//TODO: Reuse chooseRandomAppearance (eg. WoG treasure chests)
-				//Assume the template with fewest terrains is the most suitable
-				auto temp = *boost::min_element(templates, [](std::shared_ptr<const ObjectTemplate> lhs, std::shared_ptr<const ObjectTemplate> rhs) -> bool
+				oi.generateObject = [primaryID, secondaryID]() -> CGObjectInstance *
 				{
-					return lhs->getAllowedTerrains().size() < rhs->getAllowedTerrains().size();
-				});
-
-				oi.generateObject = [temp]() -> CGObjectInstance *
-				{
-					return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
+					return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create();
 				};
 				oi.value = rmgInfo.value;
 				oi.probability = rmgInfo.rarity;
-				oi.templ = temp;
+				oi.setTemplates(primaryID, secondaryID, zone.getTerrainType());
 				oi.maxPerZone = rmgInfo.zoneLimit;
-				addObjectToRandomPool(oi);
+				if(!oi.templates.empty())
+					addObjectToRandomPool(oi);
 			}
 		}
 	}
@@ -125,18 +115,18 @@ void TreasurePlacer::addAllPossibleObjects()
 				obj->exp = generator.getConfig().prisonExperience[i];
 				obj->setOwner(PlayerColor::NEUTRAL);
 				generator.banHero(hid);
-				obj->appearance = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0)->getTemplates(zone.getTerrainType()).front(); //can't init template with hero subID
 
 				return obj;
 			};
-			oi.setTemplate(Obj::PRISON, 0, zone.getTerrainType());
+			oi.setTemplates(Obj::PRISON, 0, zone.getTerrainType());
 			oi.value = generator.getConfig().prisonValues[i];
 			oi.probability = 30;
 
 			//Distribute all allowed prisons, starting from the most valuable
 			oi.maxPerZone = (std::ceil((float)prisonsLeft / (i + 1)));
 			prisonsLeft -= oi.maxPerZone;
-			addObjectToRandomPool(oi);
+			if(!oi.templates.empty())
+				addObjectToRandomPool(oi);
 		}
 	}
 
@@ -183,22 +173,16 @@ void TreasurePlacer::addAllPossibleObjects()
 				auto nativeZonesCount = static_cast<float>(map.getZoneCount(cre->getFaction()));
 				oi.value = static_cast<ui32>(cre->getAIValue() * cre->getGrowth() * (1 + (nativeZonesCount / map.getTotalZoneCount()) + (nativeZonesCount / 2)));
 				oi.probability = 40;
-
-				for(const auto & tmplate : dwellingHandler->getTemplates())
+				
+				oi.generateObject = [secondaryID, dwellingType]() -> CGObjectInstance *
 				{
-					if(tmplate->canBePlacedAt(zone.getTerrainType()))
-					{
-						oi.generateObject = [tmplate, secondaryID, dwellingType]() -> CGObjectInstance *
-						{
-							auto * obj = VLC->objtypeh->getHandlerFor(dwellingType, secondaryID)->create(tmplate);
-							obj->tempOwner = PlayerColor::NEUTRAL;
-							return obj;
-						};
-
-						oi.templ = tmplate;
-						addObjectToRandomPool(oi);
-					}
-				}
+					auto * obj = VLC->objtypeh->getHandlerFor(dwellingType, secondaryID)->create();
+					obj->tempOwner = PlayerColor::NEUTRAL;
+					return obj;
+				};
+				oi.setTemplates(dwellingType, secondaryID, zone.getTerrainType());
+				if(!oi.templates.empty())
+					addObjectToRandomPool(oi);
 			}
 		}
 	}
@@ -222,10 +206,11 @@ void TreasurePlacer::addAllPossibleObjects()
 			obj->storedArtifact = a;
 			return obj;
 		};
-		oi.setTemplate(Obj::SPELL_SCROLL, 0, zone.getTerrainType());
+		oi.setTemplates(Obj::SPELL_SCROLL, 0, zone.getTerrainType());
 		oi.value = generator.getConfig().scrollValues[i];
 		oi.probability = 30;
-		addObjectToRandomPool(oi);
+		if(!oi.templates.empty())
+			addObjectToRandomPool(oi);
 	}
 	
 	//pandora box with gold
@@ -243,10 +228,11 @@ void TreasurePlacer::addAllPossibleObjects()
 			
 			return obj;
 		};
-		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
+		oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = i * generator.getConfig().pandoraMultiplierGold;
 		oi.probability = 5;
-		addObjectToRandomPool(oi);
+		if(!oi.templates.empty())
+			addObjectToRandomPool(oi);
 	}
 	
 	//pandora box with experience
@@ -264,10 +250,11 @@ void TreasurePlacer::addAllPossibleObjects()
 			
 			return obj;
 		};
-		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
+		oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = i * generator.getConfig().pandoraMultiplierExperience;
 		oi.probability = 20;
-		addObjectToRandomPool(oi);
+		if(!oi.templates.empty())
+			addObjectToRandomPool(oi);
 	}
 	
 	//pandora box with creatures
@@ -325,10 +312,11 @@ void TreasurePlacer::addAllPossibleObjects()
 			
 			return obj;
 		};
-		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
+		oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = static_cast<ui32>((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) / 3);
 		oi.probability = 3;
-		addObjectToRandomPool(oi);
+		if(!oi.templates.empty())
+			addObjectToRandomPool(oi);
 	}
 	
 	//Pandora with 12 spells of certain level
@@ -357,10 +345,11 @@ void TreasurePlacer::addAllPossibleObjects()
 			
 			return obj;
 		};
-		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
+		oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = (i + 1) * generator.getConfig().pandoraMultiplierSpells; //5000 - 15000
 		oi.probability = 2;
-		addObjectToRandomPool(oi);
+		if(!oi.templates.empty())
+			addObjectToRandomPool(oi);
 	}
 	
 	//Pandora with 15 spells of certain school
@@ -389,10 +378,11 @@ void TreasurePlacer::addAllPossibleObjects()
 			
 			return obj;
 		};
-		oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
+		oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 		oi.value = generator.getConfig().pandoraSpellSchool;
 		oi.probability = 2;
-		addObjectToRandomPool(oi);
+		if(!oi.templates.empty())
+			addObjectToRandomPool(oi);
 	}
 	
 	// Pandora box with 60 random spells
@@ -420,10 +410,11 @@ void TreasurePlacer::addAllPossibleObjects()
 		
 		return obj;
 	};
-	oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
+	oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
 	oi.value = generator.getConfig().pandoraSpell60;
 	oi.probability = 2;
-	addObjectToRandomPool(oi);
+	if(!oi.templates.empty())
+		addObjectToRandomPool(oi);
 	
 	//Seer huts with creatures or generic rewards
 
@@ -483,7 +474,7 @@ void TreasurePlacer::addAllPossibleObjects()
 				return obj;
 			};
 			oi.probability = 3;
-			oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
+			oi.setTemplates(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
 			oi.value = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) - 4000) / 3);
 			if (oi.value > zone.getMaxTreasureValue())
 			{
@@ -491,7 +482,8 @@ void TreasurePlacer::addAllPossibleObjects()
 			}
 			else
 			{
-				possibleSeerHuts.push_back(oi);
+				if(!oi.templates.empty())
+					possibleSeerHuts.push_back(oi);
 			}
 		}
 		
@@ -500,7 +492,7 @@ void TreasurePlacer::addAllPossibleObjects()
 		{
 			int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType());
 			
-			oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
+			oi.setTemplates(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
 			oi.value = generator.getConfig().questValues[i];
 			if (oi.value > zone.getMaxTreasureValue())
 			{
@@ -533,7 +525,8 @@ void TreasurePlacer::addAllPossibleObjects()
 				return obj;
 			};
 			
-			possibleSeerHuts.push_back(oi);
+			if(!oi.templates.empty())
+				possibleSeerHuts.push_back(oi);
 			
 			oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance *
 			{
@@ -557,7 +550,8 @@ void TreasurePlacer::addAllPossibleObjects()
 				return obj;
 			};
 			
-			possibleSeerHuts.push_back(oi);
+			if(!oi.templates.empty())
+				possibleSeerHuts.push_back(oi);
 		}
 
 		if (possibleSeerHuts.empty())
@@ -610,7 +604,12 @@ std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo
 		if(!oi) //fail
 			break;
 		
-		if(oi->templ->isVisitableFromTop())
+		bool visitableFromTop = true;
+		for(auto & t : oi->templates)
+			if(!t->isVisitableFromTop())
+				visitableFromTop = false;
+		
+		if(visitableFromTop)
 		{
 			objectInfos.push_back(oi);
 		}
@@ -641,7 +640,10 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
 			accessibleArea.add(int3());
 		
 		auto * object = oi->generateObject();
-		object->appearance = oi->templ;
+		if(oi->templates.empty())
+			continue;
+		
+		object->appearance = *RandomGeneratorUtil::nextItem(oi->templates, zone.getRand());
 		auto & instance = rmgObject.addInstance(*object);
 
 		do
@@ -717,7 +719,12 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
 		if(oi.value > maxVal)
 			break; //this assumes values are sorted in ascending order
 		
-		if(!oi.templ->isVisitableFromTop() && !allowLargeObjects)
+		bool visitableFromTop = true;
+		for(auto & t : oi.templates)
+			if(!t->isVisitableFromTop())
+				visitableFromTop = false;
+		
+		if(!visitableFromTop && !allowLargeObjects)
 			continue;
 		
 		if(oi.value >= minValue && oi.maxPerZone > 0)
@@ -921,17 +928,13 @@ char TreasurePlacer::dump(const int3 & t)
 	return Modificator::dump(t);
 }
 
-void ObjectInfo::setTemplate(si32 type, si32 subtype, TerrainId terrainType)
+void ObjectInfo::setTemplates(si32 type, si32 subtype, TerrainId terrainType)
 {
 	auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
 	if(!templHandler)
 		return;
 	
-	auto templates = templHandler->getTemplates(terrainType);
-	if(templates.empty())
-		return;
-	
-	templ = templates.front();
+	templates = templHandler->getTemplates(terrainType);
 }
 
 VCMI_LIB_NAMESPACE_END

+ 2 - 4
lib/rmg/modificators/TreasurePlacer.h

@@ -22,16 +22,14 @@ class CRandomGenerator;
 
 struct ObjectInfo
 {
-	std::shared_ptr<const ObjectTemplate> templ;
+	std::vector<std::shared_ptr<const ObjectTemplate>> templates;
 	ui32 value = 0;
 	ui16 probability = 0;
 	ui32 maxPerZone = 1;
 	//ui32 maxPerMap; //unused
 	std::function<CGObjectInstance *()> generateObject;
 	
-	void setTemplate(si32 type, si32 subtype, TerrainId terrain);
-
-	bool operator==(const ObjectInfo& oi) const { return (templ == oi.templ); }
+	void setTemplates(si32 type, si32 subtype, TerrainId terrain);
 };
 
 class TreasurePlacer: public Modificator

+ 2 - 2
lib/rmg/modificators/WaterProxy.cpp

@@ -254,7 +254,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, bool createRoad, Rout
 	auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, zone.getRand()))->create());
 
 	rmg::Object rmgObject(*boat);
-	rmgObject.setTemplate(zone.getTerrainType());
+	rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
 
 	auto waterAvailable = zone.areaPossible() + zone.freePaths();
 	rmg::Area coast = lake.neighbourZones.at(land.getId()); //having land tiles
@@ -319,7 +319,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, bool
 	shipyard->tempOwner = PlayerColor::NEUTRAL;
 	
 	rmg::Object rmgObject(*shipyard);
-	rmgObject.setTemplate(land.getTerrainType());
+	rmgObject.setTemplate(land.getTerrainType(), zone.getRand());
 	bool guarded = manager->addGuard(rmgObject, guard);
 	
 	auto waterAvailable = zone.areaPossible() + zone.freePaths();