From 3ea7988883c2cee6f726da95375fd6958625a4ad Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 30 Sep 2023 23:06:38 +0200 Subject: [PATCH] Treasure placement uses random templates now --- lib/rmg/modificators/ObjectDistributor.cpp | 19 +--- lib/rmg/modificators/TreasurePlacer.cpp | 125 +++++++++++---------- lib/rmg/modificators/TreasurePlacer.h | 6 +- 3 files changed, 70 insertions(+), 80 deletions(-) diff --git a/lib/rmg/modificators/ObjectDistributor.cpp b/lib/rmg/modificators/ObjectDistributor.cpp index 92570b791..7ed97708e 100644 --- a/lib/rmg/modificators/ObjectDistributor.cpp +++ b/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 lhs, std::shared_ptr 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()); //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()->addObjectToRandomPool(oi); } diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index c92637aea..eff834612 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/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 lhs, std::shared_ptr 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(map.getZoneCount(cre->getFaction())); oi.value = static_cast(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((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast(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(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast(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 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 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 diff --git a/lib/rmg/modificators/TreasurePlacer.h b/lib/rmg/modificators/TreasurePlacer.h index 822bc793b..ef4881e25 100644 --- a/lib/rmg/modificators/TreasurePlacer.h +++ b/lib/rmg/modificators/TreasurePlacer.h @@ -22,16 +22,14 @@ class CRandomGenerator; struct ObjectInfo { - std::shared_ptr templ; + std::vector> templates; ui32 value = 0; ui16 probability = 0; ui32 maxPerZone = 1; //ui32 maxPerMap; //unused std::function 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