From d3250512136b1f9c8ab2d61a6956d854c5f48d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 29 Apr 2023 11:46:03 +0200 Subject: [PATCH] Some working version, needs corrections still. --- lib/rmg/CMapGenerator.cpp | 3 +- lib/rmg/Functions.cpp | 1 + lib/rmg/ObjectDistributor.cpp | 33 ++++++++++++- lib/rmg/ObjectDistributor.h | 1 + lib/rmg/QuestArtifactPlacer.cpp | 33 +++++++++++-- lib/rmg/QuestArtifactPlacer.h | 8 ++++ lib/rmg/RmgMap.h | 2 + lib/rmg/TreasurePlacer.cpp | 84 +++++++++++++++++---------------- 8 files changed, 119 insertions(+), 46 deletions(-) diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 729933bf7..31030027e 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -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()); } } diff --git a/lib/rmg/Functions.cpp b/lib/rmg/Functions.cpp index 8713e358a..f9d257e23 100644 --- a/lib/rmg/Functions.cpp +++ b/lib/rmg/Functions.cpp @@ -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(); }); diff --git a/lib/rmg/ObjectDistributor.cpp b/lib/rmg/ObjectDistributor.cpp index d9b338ef2..79e230fbc 100644 --- a/lib/rmg/ObjectDistributor.cpp +++ b/lib/rmg/ObjectDistributor.cpp @@ -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()->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(); + if (qap) + { + for (;localArts > 0 && artIt != possibleQuestArts.end(); artIt++, localArts--) + { + qap->addRandomArtifact(*artIt); + } + } + } +} + VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/rmg/ObjectDistributor.h b/lib/rmg/ObjectDistributor.h index 125d9af62..346fb36cf 100644 --- a/lib/rmg/ObjectDistributor.h +++ b/lib/rmg/ObjectDistributor.h @@ -21,6 +21,7 @@ class ObjectTemplate; class ObjectDistributor : public Modificator { void distributeLimitedObjects(); + void distributeSeerHuts(); public: MODIFICATOR(ObjectDistributor); diff --git a/lib/rmg/QuestArtifactPlacer.cpp b/lib/rmg/QuestArtifactPlacer.cpp index 9867e15ae..1ad807bd9 100644 --- a/lib/rmg/QuestArtifactPlacer.cpp +++ b/lib/rmg/QuestArtifactPlacer.cpp @@ -54,9 +54,7 @@ std::vector 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); -} \ No newline at end of file +} + +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); +} diff --git a/lib/rmg/QuestArtifactPlacer.h b/lib/rmg/QuestArtifactPlacer.h index a74c61832..1a602b7b4 100644 --- a/lib/rmg/QuestArtifactPlacer.h +++ b/lib/rmg/QuestArtifactPlacer.h @@ -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> questArtZones; //artifacts required for Seer Huts will be placed here - or not if null std::vector questArtifactsToPlace; std::vector artifactsToReplace; //Common artifacts which may be replaced by quest artifacts from other zones + + size_t maxQuestArtifacts; + std::vector questArtifacts; }; VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/rmg/RmgMap.h b/lib/rmg/RmgMap.h index 1144d0d22..c8ea265ba 100644 --- a/lib/rmg/RmgMap.h +++ b/lib/rmg/RmgMap.h @@ -57,6 +57,8 @@ public: void setZoneID(const int3& tile, TRmgTemplateZoneId zid); using Zones = std::map>; + using ZonePair = std::pair>; + using ZoneVector = std::vector; Zones & getZones(); diff --git a/lib/rmg/TreasurePlacer.cpp b/lib/rmg/TreasurePlacer.cpp index 5428f370a..c88317fe6 100644 --- a/lib/rmg/TreasurePlacer.cpp +++ b/lib/rmg/TreasurePlacer.cpp @@ -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 lhs, std::shared_ptr 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(generator.getQuestArtsRemaning().size()); + auto * qap = zone.getModificator(); + 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(creatures.size())) - { - seerHutsPerType = questArtsRemaining / (genericSeerHuts + static_cast(creatures.size())); - } - else if(questArtsRemaining >= genericSeerHuts) - { - seerHutsPerType = 1; - } - oi.maxPerZone = seerHutsPerType; + std::vector 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::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(creatures.size()), questArtsRemaining - genericSeerHuts); i++) + for(int i = 0; i < static_cast(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(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(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast(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(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(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)); } } }