Browse Source

Reverse seer hut and reward values

Tomasz Zieliński 1 week ago
parent
commit
3e1ac566b9

+ 1 - 0
config/randomMap.json

@@ -40,6 +40,7 @@
   },
   "quests" : 
   {
+    "seerHutValue" : 500,
     "value" : [2000, 5333, 8666, 12000],
     "rewardValue" : [5000, 10000, 15000, 20000]
   },

+ 1 - 0
lib/mapObjects/CGObjectInstance.h

@@ -59,6 +59,7 @@ public:
 	/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
 	bool blockVisit;
 	bool removable;
+	ui32 rmgValue = 0; // Value assigned during random map generation (not serialized)
 
 	PlayerColor getOwner() const override
 	{

+ 7 - 0
lib/rmg/CMapGenerator.cpp

@@ -83,6 +83,13 @@ void CMapGenerator::loadConfig()
 		config.questValues.push_back(i.Integer());
 	for(auto & i : randomMapJson["quests"]["rewardValue"].Vector())
 		config.questRewardValues.push_back(i.Integer());
+	//auto seerHutNode = randomMapJson["seerHutValue"];
+	if (seerHutNode.isNull())
+	// {
+	// 	logGlobal->error("Seer Hut value is not set in randomMap.json, using default value of 500");
+	// }
+	config.seerHutValue = randomMapJson["quests"]["seerHutValue"].Integer();
+	logGlobal->info("Seer Hut value: %d", config.seerHutValue);
 	config.pandoraMultiplierGold = randomMapJson["pandoras"]["valueMultiplierGold"].Integer();
 	config.pandoraMultiplierExperience = randomMapJson["pandoras"]["valueMultiplierExperience"].Integer();
 	config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer();

+ 1 - 0
lib/rmg/CMapGenerator.h

@@ -51,6 +51,7 @@ public:
 		std::vector<int> pandoraCreatureValues;
 		std::vector<int> questValues;
 		std::vector<int> questRewardValues;
+		int seerHutValue;
 		bool singleThread;
 	};
 	

+ 4 - 2
lib/rmg/modificators/ObjectManager.cpp

@@ -685,11 +685,13 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
 		{
 			case Obj::RANDOM_TREASURE_ART:
 			case Obj::RANDOM_MINOR_ART: //In OH3 quest artifacts have higher value than normal arts
-			case Obj::RANDOM_RESOURCE:
+			case Obj::RANDOM_MAJOR_ART:
+			case Obj::RANDOM_RELIC_ART:
+			case Obj::PANDORAS_BOX:
 			{
 				if (auto * qap = zone.getModificator<QuestArtifactPlacer>())
 				{
-					qap->rememberPotentialArtifactToReplace(&instance->object());
+					qap->rememberPotentialArtifactToReplace(&instance->object(), instance->object().rmgValue);
 				}
 				break;
 			}

+ 51 - 19
lib/rmg/modificators/QuestArtifactPlacer.cpp

@@ -41,33 +41,44 @@ void QuestArtifactPlacer::addQuestArtZone(std::shared_ptr<Zone> otherZone)
 	questArtZones.push_back(otherZone);
 }
 
-void QuestArtifactPlacer::addQuestArtifact(const ArtifactID& id)
+void QuestArtifactPlacer::addQuestArtifact(const ArtifactID& id, ui32 desiredValue)
 {
-	logGlobal->trace("Need to place quest artifact %s", LIBRARY->artifacts()->getById(id)->getNameTranslated());
+	logGlobal->trace("Need to place quest artifact %s (desired value %u)",
+		LIBRARY->artifacts()->getById(id)->getNameTranslated(),
+		desiredValue);
 	RecursiveLock lock(externalAccessMutex);
-	questArtifactsToPlace.emplace_back(id);
+	questArtifactsToPlace.push_back({id, desiredValue});
 }
 
 void QuestArtifactPlacer::removeQuestArtifact(const ArtifactID& id)
 {
 	logGlobal->trace("Will not try to place quest artifact %s", LIBRARY->artifacts()->getById(id)->getNameTranslated());
 	RecursiveLock lock(externalAccessMutex);
-	vstd::erase_if_present(questArtifactsToPlace, id);
+	vstd::erase_if(questArtifactsToPlace, [&id](const QuestArtifactRequest& request)
+	{
+		return request.id == id;
+	});
 }
 
-void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj)
+void QuestArtifactPlacer::rememberPotentialArtifactToReplace(CGObjectInstance* obj, ui32 value)
 {
 	RecursiveLock lock(externalAccessMutex);
-	artifactsToReplace.push_back(obj);
+	artifactsToReplace.push_back({obj, value});
 }
 
 std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplace() const
 {
 	RecursiveLock lock(externalAccessMutex);
-	return artifactsToReplace;
+	std::vector<CGObjectInstance*> result;
+	result.reserve(artifactsToReplace.size());
+	for (const auto& candidate : artifactsToReplace)
+	{
+		result.push_back(candidate.object);
+	}
+	return result;
 }
 
-CGObjectInstance * QuestArtifactPlacer::drawObjectToReplace()
+CGObjectInstance * QuestArtifactPlacer::drawObjectToReplace(ui32 desiredValue)
 {
 	RecursiveLock lock(externalAccessMutex);
 
@@ -75,12 +86,30 @@ CGObjectInstance * QuestArtifactPlacer::drawObjectToReplace()
 	{
 		return nullptr;
 	}
-	else
+
+	auto bestIt = artifactsToReplace.end();
+	ui32 bestDiff = std::numeric_limits<ui32>::max();
+	for (auto it = artifactsToReplace.begin(); it != artifactsToReplace.end(); ++it)
 	{
-		auto ret = *RandomGeneratorUtil::nextItem(artifactsToReplace, zone.getRand());
-		vstd::erase_if_present(artifactsToReplace, ret);
-		return ret;
+		const ui32 value = it->value;
+		auto diff = std::abs<int>((int)value - (int)desiredValue);
+		if (diff < bestDiff)
+		{
+			bestDiff = diff;
+			bestIt = it;
+			if (diff == 0)
+				break;
+		}
 	}
+
+	if (bestIt == artifactsToReplace.end())
+	{
+		return nullptr;
+	}
+
+	auto* ret = bestIt->object;
+	artifactsToReplace.erase(bestIt);
+	return ret;
 }
 
 void QuestArtifactPlacer::findZonesForQuestArts()
@@ -100,27 +129,27 @@ void QuestArtifactPlacer::findZonesForQuestArts()
 
 void QuestArtifactPlacer::placeQuestArtifacts(vstd::RNG & rand)
 {
-	for (const auto & artifactToPlace : questArtifactsToPlace)
+	for (const auto & questRequest : questArtifactsToPlace)
 	{
 		RandomGeneratorUtil::randomShuffle(questArtZones, rand);
 		for (auto zone : questArtZones)
 		{
 			auto* qap = zone->getModificator<QuestArtifactPlacer>();
 			
-			auto objectToReplace = qap->drawObjectToReplace();
+			auto objectToReplace = qap->drawObjectToReplace(questRequest.desiredValue);
 			if (!objectToReplace)
 				continue;
 
-			logGlobal->trace("Replacing %s at %s with the quest artifact %s",
+			logGlobal->trace("Replacing %s at %s with the quest artifact %s (desired value %u)",
 				objectToReplace->getObjectName(),
 				objectToReplace->anchorPos().toString(),
-				LIBRARY->artifacts()->getById(artifactToPlace)->getNameTranslated());
+				LIBRARY->artifacts()->getById(questRequest.id)->getNameTranslated(),
+				questRequest.desiredValue);
 
 			//Update appearance. Terrain is irrelevant.
-			auto handler = LIBRARY->objtypeh->getHandlerFor(Obj::ARTIFACT, artifactToPlace);
+			auto handler = LIBRARY->objtypeh->getHandlerFor(Obj::ARTIFACT, questRequest.id);
 			auto newObj = handler->create(map.mapInstance->cb, nullptr);
 			auto templates = handler->getTemplates();
-			//artifactToReplace->appearance = templates.front();
 			newObj->appearance  = templates.front();
 			newObj->setAnchorPos(objectToReplace->anchorPos());
 			mapProxy->insertObject(newObj);
@@ -134,7 +163,10 @@ void QuestArtifactPlacer::placeQuestArtifacts(vstd::RNG & rand)
 void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj)
 {
 	RecursiveLock lock(externalAccessMutex);
-	boost::remove(artifactsToReplace, obj);
+	vstd::erase_if(artifactsToReplace, [obj](const ReplacementCandidate& candidate)
+	{
+		return candidate.object == obj;
+	});
 }
 
 size_t QuestArtifactPlacer::getMaxQuestArtifactCount() const

+ 16 - 5
lib/rmg/modificators/QuestArtifactPlacer.h

@@ -26,10 +26,10 @@ public:
 	void addQuestArtZone(std::shared_ptr<Zone> otherZone);
 	void findZonesForQuestArts();
 
-	void addQuestArtifact(const ArtifactID& id);
+	void addQuestArtifact(const ArtifactID& id, ui32 desiredValue);
 	void removeQuestArtifact(const ArtifactID& id);
-	void rememberPotentialArtifactToReplace(CGObjectInstance* obj);
-	CGObjectInstance * drawObjectToReplace();
+	void rememberPotentialArtifactToReplace(CGObjectInstance* obj, ui32 value);
+	CGObjectInstance * drawObjectToReplace(ui32 desiredValue);
 	std::vector<CGObjectInstance*> getPossibleArtifactsToReplace() const;
 	void placeQuestArtifacts(vstd::RNG & rand);
 	void dropReplacedArtifact(CGObjectInstance* obj);
@@ -39,10 +39,21 @@ public:
 	void addRandomArtifact(const ArtifactID & artid);
 
 protected:
+	struct QuestArtifactRequest
+	{
+		ArtifactID id;
+		ui32 desiredValue;
+	};
+
+	struct ReplacementCandidate
+	{
+		CGObjectInstance* object;
+		ui32 value;
+	};
 
 	std::vector<std::shared_ptr<Zone>> questArtZones; //artifacts required for Seer Huts will be placed here - or not if null
-	std::vector<ArtifactID> questArtifactsToPlace;
-	std::vector<CGObjectInstance*> artifactsToReplace; //Common artifacts which may be replaced by quest artifacts from other zones
+	std::vector<QuestArtifactRequest> questArtifactsToPlace;
+	std::vector<ReplacementCandidate> artifactsToReplace; //Common artifacts which may be replaced by quest artifacts from other zones
 
 	size_t maxQuestArtifacts;
 	std::vector<ArtifactID> questArtifacts;

+ 22 - 21
lib/rmg/modificators/TreasurePlacer.cpp

@@ -487,6 +487,7 @@ void TreasurePlacer::addSeerHuts()
 	//Seer huts with creatures or generic rewards
 
 	ObjectInfo oi(Obj::SEER_HUT, 0);
+	const ui32 seerHutPlacementValue = static_cast<ui32>(generator.getConfig().seerHutValue);
 
 	if(zone.getConnectedZoneIds().size()) //Unlikely, but...
 	{
@@ -504,6 +505,7 @@ void TreasurePlacer::addSeerHuts()
 		
 		//Generate Seer Hut one by one. Duplicated oi possible and should work fine.
 		oi.maxPerZone = 1;
+		oi.value = seerHutPlacementValue;
 
 		std::vector<ObjectInfo> possibleSeerHuts;
 		//14 creatures per town + 4 for each of gold / exp reward
@@ -511,11 +513,11 @@ void TreasurePlacer::addSeerHuts()
 		
 		RandomGeneratorUtil::randomShuffle(creatures, zone.getRand());
 
-		auto setRandomArtifact = [qap](CGSeerHut * obj)
+		auto setRandomArtifact = [qap](CGSeerHut * obj, ui32 rewardValue)
 		{
 			ArtifactID artid = qap->drawRandomArtifact();
 			obj->getQuest().mission.artifacts.push_back(artid);
-			qap->addQuestArtifact(artid);
+			qap->addQuestArtifact(artid, rewardValue);
 		};
 		auto destroyObject = [qap](CGObjectInstance & obj)
 		{
@@ -537,7 +539,14 @@ void TreasurePlacer::addSeerHuts()
 			int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType());
 			
 			// FIXME: Remove duplicated code for gold, exp and creaure reward
-			oi.generateObject = [cb=map.mapInstance->cb, creature, creaturesAmount, randomAppearance, setRandomArtifact]() -> std::shared_ptr<CGObjectInstance>
+			const ui32 rewardValue = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFactionID())) / map.getTotalZoneCount())) - 4000) / 3);
+
+			if (rewardValue > zone.getMaxTreasureValue())
+			{
+				continue;
+			}
+
+			oi.generateObject = [cb=map.mapInstance->cb, creature, creaturesAmount, randomAppearance, setRandomArtifact, rewardValue]() -> std::shared_ptr<CGObjectInstance>
 			{
 				auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
 				auto obj = std::dynamic_pointer_cast<CGSeerHut>(factory->create(cb, nullptr));
@@ -546,23 +555,15 @@ void TreasurePlacer::addSeerHuts()
 				reward.reward.creatures.emplace_back(creature->getId(), creaturesAmount);
 				reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
 				obj->configuration.info.push_back(reward);
-				setRandomArtifact(obj.get());
+				setRandomArtifact(obj.get(), rewardValue);
 				
 				return obj;
 			};
 			oi.destroyObject = destroyObject;
 			oi.probability = 3;
 			oi.setTemplates(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
-			oi.value = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFactionID())) / map.getTotalZoneCount())) - 4000) / 3);
-			if (oi.value > zone.getMaxTreasureValue())
-			{
-				continue;
-			}
-			else
-			{
-				if(!oi.templates.empty())
-					possibleSeerHuts.push_back(oi);
-			}
+			if(!oi.templates.empty())
+				possibleSeerHuts.push_back(oi);
 		}
 		
 		static const int seerLevels = std::min(generator.getConfig().questValues.size(), generator.getConfig().questRewardValues.size());
@@ -571,17 +572,16 @@ void TreasurePlacer::addSeerHuts()
 			int randomAppearance = chooseRandomAppearance(zone.getRand(), Obj::SEER_HUT, zone.getTerrainType());
 			
 			oi.setTemplates(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
-			oi.value = generator.getConfig().questValues[i];
-			if (oi.value > zone.getMaxTreasureValue())
+			const ui32 rewardValue = static_cast<ui32>(generator.getConfig().questRewardValues[i]);
+			if (rewardValue > zone.getMaxTreasureValue())
 			{
-				//Both variants have same value
 				continue;
 			}
 
 			oi.probability = 10;
 			oi.maxPerZone = 1;
 			
-			oi.generateObject = [i, randomAppearance, this, setRandomArtifact]() -> std::shared_ptr<CGObjectInstance>
+			oi.generateObject = [i, randomAppearance, this, setRandomArtifact, rewardValue]() -> std::shared_ptr<CGObjectInstance>
 			{
 				auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
 				auto obj = std::dynamic_pointer_cast<CGSeerHut>(factory->create(map.mapInstance->cb, nullptr));
@@ -590,7 +590,7 @@ void TreasurePlacer::addSeerHuts()
 				reward.reward.heroExperience = generator.getConfig().questRewardValues[i];
 				reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
 				obj->configuration.info.push_back(reward);
-				setRandomArtifact(obj.get());
+				setRandomArtifact(obj.get(), rewardValue);
 
 				return obj;
 			};
@@ -599,7 +599,7 @@ void TreasurePlacer::addSeerHuts()
 			if(!oi.templates.empty())
 				possibleSeerHuts.push_back(oi);
 			
-			oi.generateObject = [i, randomAppearance, this, setRandomArtifact]() -> std::shared_ptr<CGObjectInstance>
+			oi.generateObject = [i, randomAppearance, this, setRandomArtifact, rewardValue]() -> std::shared_ptr<CGObjectInstance>
 			{
 				auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
 				auto obj = std::dynamic_pointer_cast<CGSeerHut>(factory->create(map.mapInstance->cb, nullptr));
@@ -609,7 +609,7 @@ void TreasurePlacer::addSeerHuts()
 				reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
 				obj->configuration.info.push_back(reward);
 				
-				setRandomArtifact(obj.get());
+				setRandomArtifact(obj.get(), rewardValue);
 				
 				return obj;
 			};
@@ -749,6 +749,7 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
 		if (oi->generateObject)
 		{
 			object = oi->generateObject();
+			object->rmgValue = oi->value;
 			if(oi->templates.empty())
 			{
 				logGlobal->warn("Deleting randomized object with no templates: %s", object->getObjectName());