mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Some working version, needs corrections still.
This commit is contained in:
		| @@ -113,7 +113,8 @@ void CMapGenerator::initQuestArtsRemaining() | ||||
| 	//TODO: Move to QuestArtifactPlacer? | ||||
| 	for (auto art : VLC->arth->objects) | ||||
| 	{ | ||||
| 		if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->getId()) && art->constituentOf.empty()) //don't use parts of combined artifacts | ||||
| 		//Don't use parts of combined artifacts | ||||
| 		if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->getId()) && art->constituentOf.empty()) | ||||
| 			questArtifacts.push_back(art->getId()); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -82,6 +82,7 @@ int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId t | ||||
| 	auto factories = VLC->objtypeh->knownSubObjects(ObjID); | ||||
| 	vstd::erase_if(factories, [ObjID, &terrain](si32 f) | ||||
| 	{ | ||||
| 		//TODO: Use templates with lowest number of terrains (most specific) | ||||
| 		return VLC->objtypeh->getHandlerFor(ObjID, f)->getTemplates(terrain).empty(); | ||||
| 	}); | ||||
| 	 | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include "RmgMap.h" | ||||
| #include "CMapGenerator.h" | ||||
| #include "TreasurePlacer.h" | ||||
| #include "QuestArtifactPlacer.h" | ||||
| #include "TownPlacer.h" | ||||
| #include "TerrainPainter.h" | ||||
| #include "../mapObjects/CObjectClassesHandler.h" | ||||
| @@ -29,7 +30,8 @@ void ObjectDistributor::process() | ||||
| 	//Firts call will add objects to ALL zones, once they were added skip it | ||||
| 	if (zone.getModificator<TreasurePlacer>()->getPossibleObjectsSize() == 0) | ||||
| 	{ | ||||
| 		ObjectDistributor::distributeLimitedObjects(); | ||||
| 		distributeLimitedObjects(); | ||||
| 		distributeSeerHuts(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -118,4 +120,33 @@ void ObjectDistributor::distributeLimitedObjects() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ObjectDistributor::distributeSeerHuts() | ||||
| { | ||||
| 	//TODO: Move typedef outside the class? | ||||
|  | ||||
| 	//Copy by value to random shuffle | ||||
| 	const auto & zoneMap = map.getZones(); | ||||
| 	RmgMap::ZoneVector zones(zoneMap.begin(), zoneMap.end()); | ||||
|  | ||||
| 	RandomGeneratorUtil::randomShuffle(zones, generator.rand); | ||||
|  | ||||
| 	const auto & possibleQuestArts = generator.getQuestArtsRemaning(); | ||||
| 	size_t availableArts = possibleQuestArts.size(); | ||||
| 	auto artIt = possibleQuestArts.begin(); | ||||
| 	for (int i = zones.size() - 1; i >= 0 ; i--) | ||||
| 	{ | ||||
| 		size_t localArts = std::ceil((float)availableArts / (i + 1)); | ||||
| 		availableArts -= localArts; | ||||
|  | ||||
| 		auto * qap = zones[i].second->getModificator<QuestArtifactPlacer>(); | ||||
| 		if (qap) | ||||
| 		{ | ||||
| 			for (;localArts > 0 && artIt != possibleQuestArts.end(); artIt++, localArts--) | ||||
| 			{ | ||||
| 				qap->addRandomArtifact(*artIt); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| @@ -21,6 +21,7 @@ class ObjectTemplate; | ||||
| class ObjectDistributor : public Modificator | ||||
| { | ||||
| 	void distributeLimitedObjects(); | ||||
| 	void distributeSeerHuts(); | ||||
|  | ||||
| public: | ||||
| 	MODIFICATOR(ObjectDistributor); | ||||
|   | ||||
| @@ -54,9 +54,7 @@ std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplac | ||||
|  | ||||
| void QuestArtifactPlacer::findZonesForQuestArts() | ||||
| { | ||||
| 	//FIXME: Store and access CZonePlacer from CMapGenerator | ||||
|  | ||||
| 	const auto& distances = 	generator.getZonePlacer()->getDistanceMap().at(zone.getId()); | ||||
| 	const auto& distances = generator.getZonePlacer()->getDistanceMap().at(zone.getId()); | ||||
| 	for (auto const& connectedZone : distances) | ||||
| 	{ | ||||
| 		// Choose zones that are 1 or 2 connections away | ||||
| @@ -96,6 +94,8 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator * rand) | ||||
| 			artifactToReplace->appearance = templates.front(); | ||||
| 			//FIXME: Instance name is still "randomArtifact" | ||||
|  | ||||
| 			//FIXME: Every qap has its OWN collection of artifacts, | ||||
| 			//which means different qaps can replace the same object many times | ||||
| 			qap->dropReplacedArtifact(artifactToReplace); | ||||
|  | ||||
| 			break; | ||||
| @@ -106,4 +106,29 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator * rand) | ||||
| void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj) | ||||
| { | ||||
| 	boost::remove(artifactsToReplace, obj); | ||||
| } | ||||
| } | ||||
|  | ||||
| size_t QuestArtifactPlacer::getMaxQuestArtifactCount() const | ||||
| { | ||||
| 	return questArtifacts.size(); | ||||
| } | ||||
|  | ||||
| ArtifactID QuestArtifactPlacer::drawRandomArtifact() | ||||
| { | ||||
| 	if (!questArtifacts.empty()) | ||||
| 	{ | ||||
| 		ArtifactID ret = questArtifacts.back(); | ||||
| 		questArtifacts.pop_back(); | ||||
| 		RandomGeneratorUtil::randomShuffle(questArtifacts, generator.rand); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		throw rmgException("No quest artifacts left for this zone!"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void QuestArtifactPlacer::addRandomArtifact(ArtifactID artid) | ||||
| { | ||||
| 	questArtifacts.push_back(artid); | ||||
| } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|  | ||||
| #pragma once | ||||
| #include "Zone.h" | ||||
| #include "Functions.h" | ||||
| #include "../mapObjects/ObjectTemplate.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| @@ -33,11 +34,18 @@ public: | ||||
| 	void placeQuestArtifacts(CRandomGenerator* rand); | ||||
| 	void dropReplacedArtifact(CGObjectInstance* obj); | ||||
|  | ||||
| 	size_t getMaxQuestArtifactCount() const; | ||||
| 	ArtifactID drawRandomArtifact(); | ||||
| 	void addRandomArtifact(ArtifactID artid); | ||||
|  | ||||
| protected: | ||||
|  | ||||
| 	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 | ||||
|  | ||||
| 	size_t maxQuestArtifacts; | ||||
| 	std::vector<ArtifactID> questArtifacts; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| @@ -57,6 +57,8 @@ public: | ||||
| 	void setZoneID(const int3& tile, TRmgTemplateZoneId zid); | ||||
| 	 | ||||
| 	using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<Zone>>; | ||||
| 	using ZonePair = std::pair<TRmgTemplateZoneId, std::shared_ptr<Zone>>; | ||||
| 	using ZoneVector = std::vector<ZonePair>; | ||||
| 	 | ||||
| 	Zones & getZones(); | ||||
| 	 | ||||
|   | ||||
| @@ -70,6 +70,7 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 				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 | ||||
| 				{ | ||||
| @@ -385,44 +386,28 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 	oi.probability = 2; | ||||
| 	addObjectToRandomPool(oi); | ||||
| 	 | ||||
| 	//seer huts with creatures or generic rewards | ||||
| 	//Seer huts with creatures or generic rewards | ||||
|  | ||||
| 	if(zone.getConnections().size()) //Unlikely, but... | ||||
| 	{ | ||||
| 		static const int genericSeerHuts = 8; | ||||
| 		int seerHutsPerType = 0; | ||||
| 		const int questArtsRemaining = static_cast<int>(generator.getQuestArtsRemaning().size()); | ||||
| 		auto * qap = zone.getModificator<QuestArtifactPlacer>(); | ||||
| 		if(!qap) | ||||
| 		{ | ||||
| 			return; //TODO: throw? | ||||
| 		} | ||||
| 		 | ||||
| 		//general issue is that not many artifact types are available for quests | ||||
| 		const int questArtsRemaining = qap->getMaxQuestArtifactCount(); | ||||
| 		 | ||||
| 		//Generate Seer Hut one by one. Duplicated oi possible and should work fine. | ||||
| 		oi.maxPerZone = 1; | ||||
|  | ||||
| 		if(questArtsRemaining >= genericSeerHuts + static_cast<int>(creatures.size())) | ||||
| 		{ | ||||
| 			seerHutsPerType = questArtsRemaining / (genericSeerHuts + static_cast<int>(creatures.size())); | ||||
| 		} | ||||
| 		else if(questArtsRemaining >= genericSeerHuts) | ||||
| 		{ | ||||
| 			seerHutsPerType = 1; | ||||
| 		} | ||||
| 		oi.maxPerZone = seerHutsPerType; | ||||
| 		std::vector<ObjectInfo> possibleSeerHuts; | ||||
| 		//14 creatures per town + 4 for each of gold / exp reward | ||||
| 		possibleSeerHuts.reserve(14 + 4 + 4); | ||||
| 		 | ||||
| 		RandomGeneratorUtil::randomShuffle(creatures, generator.rand); | ||||
|  | ||||
| 		auto generateArtInfo = [this](const ArtifactID & id) -> ObjectInfo | ||||
| 		{ | ||||
| 			ObjectInfo artInfo; | ||||
| 			artInfo.probability = std::numeric_limits<ui16>::max(); //99,9% to spawn that art in first treasure pile | ||||
| 			artInfo.maxPerZone = 1; | ||||
| 			artInfo.value = 2000; //treasure art | ||||
| 			artInfo.setTemplate(Obj::ARTIFACT, id, this->zone.getTerrainType()); | ||||
| 			artInfo.generateObject = [id]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto handler = VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, id); | ||||
| 				return handler->create(handler->getTemplates().front()); | ||||
| 			}; | ||||
| 			return artInfo; | ||||
| 		}; | ||||
|  | ||||
| 		for(int i = 0; i < std::min(static_cast<int>(creatures.size()), questArtsRemaining - genericSeerHuts); i++) | ||||
| 		for(int i = 0; i < static_cast<int>(creatures.size()); i++) | ||||
| 		{ | ||||
| 			auto * creature = creatures[i]; | ||||
| 			int creaturesAmount = creatureToCount(creature); | ||||
| @@ -432,7 +417,7 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 			 | ||||
| 			int randomAppearance = chooseRandomAppearance(generator.rand, Obj::SEER_HUT, zone.getTerrainType()); | ||||
| 			 | ||||
| 			oi.generateObject = [creature, creaturesAmount, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * | ||||
| 			oi.generateObject = [creature, creaturesAmount, randomAppearance, this, qap]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); | ||||
| 				auto * obj = dynamic_cast<CGSeerHut *>(factory->create()); | ||||
| @@ -441,7 +426,8 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 				obj->rVal = creaturesAmount; | ||||
| 				 | ||||
| 				obj->quest->missionType = CQuest::MISSION_ART; | ||||
| 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand); | ||||
| 				 | ||||
| 				ArtifactID artid = qap->drawRandomArtifact(); | ||||
| 				obj->quest->addArtifactID(artid); | ||||
| 				obj->quest->lastDay = -1; | ||||
| 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false; | ||||
| @@ -451,10 +437,17 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 				 | ||||
| 				return obj; | ||||
| 			}; | ||||
| 			oi.probability = 3; | ||||
| 			oi.setTemplate(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); | ||||
| 			oi.probability = 3; | ||||
| 			addObjectToRandomPool(oi); | ||||
| 			if (oi.value > zone.getMaxTreasureValue()) | ||||
| 			{ | ||||
| 				continue; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				possibleSeerHuts.push_back(oi); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		static int seerLevels = std::min(generator.getConfig().questValues.size(), generator.getConfig().questRewardValues.size()); | ||||
| @@ -464,9 +457,15 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 			 | ||||
| 			oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType()); | ||||
| 			oi.value = generator.getConfig().questValues[i]; | ||||
| 			if (oi.value > zone.getMaxTreasureValue()) | ||||
| 			{ | ||||
| 				//Both variants have same value | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			oi.probability = 10; | ||||
| 			 | ||||
| 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * | ||||
| 			oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); | ||||
| 				auto * obj = dynamic_cast<CGSeerHut *>(factory->create()); | ||||
| @@ -476,7 +475,7 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 				obj->rVal = generator.getConfig().questRewardValues[i]; | ||||
| 				 | ||||
| 				obj->quest->missionType = CQuest::MISSION_ART; | ||||
| 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand); | ||||
| 				ArtifactID artid = qap->drawRandomArtifact(); | ||||
| 				obj->quest->addArtifactID(artid); | ||||
| 				obj->quest->lastDay = -1; | ||||
| 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false; | ||||
| @@ -487,9 +486,9 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 				return obj; | ||||
| 			}; | ||||
| 			 | ||||
| 			addObjectToRandomPool(oi); | ||||
| 			possibleSeerHuts.push_back(oi); | ||||
| 			 | ||||
| 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * | ||||
| 			oi.generateObject = [i, randomAppearance, this, qap]() -> CGObjectInstance * | ||||
| 			{ | ||||
| 				auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); | ||||
| 				auto * obj = dynamic_cast<CGSeerHut *>(factory->create()); | ||||
| @@ -498,7 +497,7 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 				obj->rVal = generator.getConfig().questRewardValues[i]; | ||||
| 				 | ||||
| 				obj->quest->missionType = CQuest::MISSION_ART; | ||||
| 				ArtifactID artid = *RandomGeneratorUtil::nextItem(generator.getQuestArtsRemaning(), generator.rand); | ||||
| 				ArtifactID artid = qap->drawRandomArtifact(); | ||||
| 				obj->quest->addArtifactID(artid); | ||||
| 				obj->quest->lastDay = -1; | ||||
| 				obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false; | ||||
| @@ -509,7 +508,12 @@ void TreasurePlacer::addAllPossibleObjects() | ||||
| 				return obj; | ||||
| 			}; | ||||
| 			 | ||||
| 			addObjectToRandomPool(oi); | ||||
| 			possibleSeerHuts.push_back(oi); | ||||
| 		} | ||||
|  | ||||
| 		for (size_t i = 0; i < questArtsRemaining; i++) | ||||
| 		{ | ||||
| 			addObjectToRandomPool(*RandomGeneratorUtil::nextItem(possibleSeerHuts, generator.rand)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user