From 52d33fc7a6d6e2653f9347691526ee4592d59772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Thu, 8 Jun 2023 19:51:21 +0200 Subject: [PATCH] Generate all treasures of certain value beforehand and try to place them all, don't interrupt at first failure. --- lib/rmg/RmgObject.cpp | 29 +++- lib/rmg/RmgObject.h | 4 + lib/rmg/modificators/TreasurePlacer.cpp | 187 ++++++++++++++---------- 3 files changed, 137 insertions(+), 83 deletions(-) diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index bfe775f1c..540463948 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -161,17 +161,21 @@ const CGObjectInstance & Object::Instance::object() const return dObject; } -Object::Object(CGObjectInstance & object, const int3 & position) +Object::Object(CGObjectInstance & object, const int3 & position): + guarded(false) { addInstance(object, position); } -Object::Object(CGObjectInstance & object) +Object::Object(CGObjectInstance & object): + guarded(false) { addInstance(object); } -Object::Object(const Object & object): dStrength(object.dStrength) +Object::Object(const Object & object): + dStrength(object.dStrength), + guarded(false) { for(const auto & i : object.dInstances) addInstance(const_cast(i.object()), i.getPosition()); @@ -197,7 +201,9 @@ std::list Object::instances() const void Object::addInstance(Instance & object) { //assert(object.dParent == *this); + setGuardedIfMonster(object); dInstances.push_back(object); + dFullAreaCache.clear(); dAccessibleAreaCache.clear(); dAccessibleAreaFullCache.clear(); @@ -206,6 +212,8 @@ void Object::addInstance(Instance & object) Object::Instance & Object::addInstance(CGObjectInstance & object) { dInstances.emplace_back(*this, object); + setGuardedIfMonster(dInstances.back()); + dFullAreaCache.clear(); dAccessibleAreaCache.clear(); dAccessibleAreaFullCache.clear(); @@ -215,6 +223,8 @@ Object::Instance & Object::addInstance(CGObjectInstance & object) Object::Instance & Object::addInstance(CGObjectInstance & object, const int3 & position) { dInstances.emplace_back(*this, object, position); + setGuardedIfMonster(dInstances.back()); + dFullAreaCache.clear(); dAccessibleAreaCache.clear(); dAccessibleAreaFullCache.clear(); @@ -302,6 +312,19 @@ const int3 Object::getVisibleTop() const return topTile; } +bool rmg::Object::isGuarded() const +{ + return guarded; +} + +void rmg::Object::setGuardedIfMonster(const Instance& object) +{ + if (object.object().ID == Obj::MONSTER) + { + guarded = true; + } +} + void Object::Instance::finalize(RmgMap & map) { if(!map.isOnMap(getPosition(true))) diff --git a/lib/rmg/RmgObject.h b/lib/rmg/RmgObject.h index a164b9161..d444a90b4 100644 --- a/lib/rmg/RmgObject.h +++ b/lib/rmg/RmgObject.h @@ -77,6 +77,9 @@ public: const Area & getArea() const; //lazy cache invalidation const int3 getVisibleTop() const; + + bool isGuarded() const; + void setGuardedIfMonster(const Instance & object); void finalize(RmgMap & map); void clear(); @@ -87,6 +90,7 @@ private: mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache; int3 dPosition; ui32 dStrength; + bool guarded; }; } diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 2fd18de25..f39c5107b 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -701,133 +701,160 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu } } -void TreasurePlacer::createTreasures(ObjectManager & manager) +void TreasurePlacer::createTreasures(ObjectManager& manager) { const int maxAttempts = 2; - + const int minDistance = 2; + int mapMonsterStrength = map.getMapGenOptions().getMonsterStrength(); - int monsterStrength = (zone.monsterStrength == EMonsterStrength::ZONE_NONE ? 0 : zone.monsterStrength + mapMonsterStrength - 1); //array index from 0 to 4; pick any correct value for ZONE_NONE, minGuardedValue won't be used in this case anyway + int monsterStrength = (zone.monsterStrength == EMonsterStrength::ZONE_NONE ? 0 : zone.monsterStrength + mapMonsterStrength - 1); //array index from 0 to 4; pick any correct value for ZONE_NONE, minGuardedValue won't be used in this case anyway static int minGuardedValues[] = { 6500, 4167, 3000, 1833, 1333 }; minGuardedValue = minGuardedValues[monsterStrength]; - - auto valueComparator = [](const CTreasureInfo & lhs, const CTreasureInfo & rhs) -> bool + + auto valueComparator = [](const CTreasureInfo& lhs, const CTreasureInfo& rhs) -> bool { return lhs.max > rhs.max; }; - - auto restoreZoneLimits = [](const std::vector & treasurePile) + + auto restoreZoneLimits = [](const std::vector& treasurePile) { - for(auto * oi : treasurePile) + for (auto* oi : treasurePile) { oi->maxPerZone++; } }; - + //place biggest treasures first at large distance, place smaller ones inbetween auto treasureInfo = zone.getTreasureInfo(); boost::sort(treasureInfo, valueComparator); - + //sort treasures by ascending value so we can stop checking treasures with too high value boost::sort(possibleObjects, [](const ObjectInfo& oi1, const ObjectInfo& oi2) -> bool { return oi1.value < oi2.value; }); - + + size_t size = 0; + { + Zone::Lock lock(zone.areaMutex); + size = zone.areaPossible().getTiles().size(); + } + int totalDensity = 0; + for (auto t : treasureInfo) { + std::vector treasures; + //discard objects with too high value to be ever placed vstd::erase_if(possibleObjects, [t](const ObjectInfo& oi) -> bool { return oi.value > t.max; }); - + totalDensity += t.density; - + + const size_t count = size * t.density / 300; + //treasure density is inversely proportional to zone size but must be scaled back to map size //also, normalize it to zone count - higher count means relatively smaller zones //this is squared distance for optimization purposes - const float minDistance = std::max((125.f / totalDensity), 2.0f); - //distance lower than 2 causes objects to overlap and crash - - for(int attempt = 0; attempt <= maxAttempts;) + const float minDistance = std::max((125.f / totalDensity), 1.0f); + + for (size_t i = 0; i < count;) { auto treasurePileInfos = prepareTreasurePile(t); - if(treasurePileInfos.empty()) + if (treasurePileInfos.empty()) { - ++attempt; continue; } - - int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0, [](int v, const ObjectInfo * oi){return v + oi->value;}); - - auto rmgObject = constructTreasurePile(treasurePileInfos, attempt == maxAttempts); - if(rmgObject.instances().empty()) //handle incorrect placement + else { - restoreZoneLimits(treasurePileInfos); - continue; + i++; } - - //guard treasure pile - bool guarded = isGuardNeededForTreasure(value); - if(guarded) - guarded = manager.addGuard(rmgObject, value); - - Zone::Lock lock(zone.areaMutex); //We are going to subtract this area - //TODO: Don't place - auto possibleArea = zone.areaPossible(); - - auto path = rmg::Path::invalid(); - if(guarded) - { - path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3 & tile) - { - auto ti = map.getTileInfo(tile); - if(ti.getNearestObjectDistance() < minDistance) - return -1.f; - for(const auto & t : rmgObject.getArea().getTilesVector()) - { - if(map.getTileInfo(t).getNearestObjectDistance() < minDistance) - return -1.f; - } - - auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); - auto areaToBlock = rmgObject.getAccessibleArea(true); - areaToBlock.subtract(guardedArea); - if(areaToBlock.overlap(zone.freePaths()) || areaToBlock.overlap(manager.getVisitableArea())) - return -1.f; - - return ti.getNearestObjectDistance(); - }, guarded, false, ObjectManager::OptimizeType::DISTANCE); - } - else + int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0, [](int v, const ObjectInfo* oi) {return v + oi->value; }); + + for (ui32 attempt = 0; attempt <= 2; attempt++) { - path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE); - } - - if(path.valid()) - { - //debug purposes - treasureArea.unite(rmgObject.getArea()); - if(guarded) + auto rmgObject = constructTreasurePile(treasurePileInfos, attempt == maxAttempts); + + if (rmgObject.instances().empty()) //handle incorrect placement { - guards.unite(rmgObject.instances().back()->getBlockedArea()); - auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); - auto areaToBlock = rmgObject.getAccessibleArea(true); - areaToBlock.subtract(guardedArea); - treasureBlockArea.unite(areaToBlock); + restoreZoneLimits(treasurePileInfos); + continue; } - zone.connectPath(path); - manager.placeObject(rmgObject, guarded, true); - attempt = 0; + + //guard treasure pile + bool guarded = isGuardNeededForTreasure(value); + if (guarded) + guarded = manager.addGuard(rmgObject, value); + + treasures.push_back(rmgObject); + break; } - else + } + + for (auto& rmgObject : treasures) + { + const bool guarded = rmgObject.isGuarded(); + + for (int attempt = 0; attempt <= maxAttempts;) { - restoreZoneLimits(treasurePileInfos); - rmgObject.clear(); - ++attempt; + auto path = rmg::Path::invalid(); + + Zone::Lock lock(zone.areaMutex); //We are going to subtract this area + auto possibleArea = zone.areaPossible(); + + if (guarded) + { + path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile) + { + auto ti = map.getTileInfo(tile); + if (ti.getNearestObjectDistance() < minDistance) + return -1.f; + + for (const auto& t : rmgObject.getArea().getTilesVector()) + { + if (map.getTileInfo(t).getNearestObjectDistance() < minDistance) + return -1.f; + } + + auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); + auto areaToBlock = rmgObject.getAccessibleArea(true); + areaToBlock.subtract(guardedArea); + if (areaToBlock.overlap(zone.freePaths()) || areaToBlock.overlap(manager.getVisitableArea())) + return -1.f; + + return ti.getNearestObjectDistance(); + }, guarded, false, ObjectManager::OptimizeType::DISTANCE); + } + else + { + path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE); + } + + if (path.valid()) + { + //debug purposes + treasureArea.unite(rmgObject.getArea()); + if (guarded) + { + guards.unite(rmgObject.instances().back()->getBlockedArea()); + auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); + auto areaToBlock = rmgObject.getAccessibleArea(true); + areaToBlock.subtract(guardedArea); + treasureBlockArea.unite(areaToBlock); + } + zone.connectPath(path); + manager.placeObject(rmgObject, guarded, true); + break; + } + else + { + ++attempt; + } } } }