1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Generate all treasures of certain value beforehand and try to place them all, don't interrupt at first failure.

This commit is contained in:
Tomasz Zieliński
2023-06-08 19:51:21 +02:00
parent 66b6fba51f
commit 52d33fc7a6
3 changed files with 137 additions and 83 deletions

View File

@@ -161,17 +161,21 @@ const CGObjectInstance & Object::Instance::object() const
return dObject; return dObject;
} }
Object::Object(CGObjectInstance & object, const int3 & position) Object::Object(CGObjectInstance & object, const int3 & position):
guarded(false)
{ {
addInstance(object, position); addInstance(object, position);
} }
Object::Object(CGObjectInstance & object) Object::Object(CGObjectInstance & object):
guarded(false)
{ {
addInstance(object); 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) for(const auto & i : object.dInstances)
addInstance(const_cast<CGObjectInstance &>(i.object()), i.getPosition()); addInstance(const_cast<CGObjectInstance &>(i.object()), i.getPosition());
@@ -197,7 +201,9 @@ std::list<const Object::Instance*> Object::instances() const
void Object::addInstance(Instance & object) void Object::addInstance(Instance & object)
{ {
//assert(object.dParent == *this); //assert(object.dParent == *this);
setGuardedIfMonster(object);
dInstances.push_back(object); dInstances.push_back(object);
dFullAreaCache.clear(); dFullAreaCache.clear();
dAccessibleAreaCache.clear(); dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear(); dAccessibleAreaFullCache.clear();
@@ -206,6 +212,8 @@ void Object::addInstance(Instance & object)
Object::Instance & Object::addInstance(CGObjectInstance & object) Object::Instance & Object::addInstance(CGObjectInstance & object)
{ {
dInstances.emplace_back(*this, object); dInstances.emplace_back(*this, object);
setGuardedIfMonster(dInstances.back());
dFullAreaCache.clear(); dFullAreaCache.clear();
dAccessibleAreaCache.clear(); dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear(); dAccessibleAreaFullCache.clear();
@@ -215,6 +223,8 @@ Object::Instance & Object::addInstance(CGObjectInstance & object)
Object::Instance & Object::addInstance(CGObjectInstance & object, const int3 & position) Object::Instance & Object::addInstance(CGObjectInstance & object, const int3 & position)
{ {
dInstances.emplace_back(*this, object, position); dInstances.emplace_back(*this, object, position);
setGuardedIfMonster(dInstances.back());
dFullAreaCache.clear(); dFullAreaCache.clear();
dAccessibleAreaCache.clear(); dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear(); dAccessibleAreaFullCache.clear();
@@ -302,6 +312,19 @@ const int3 Object::getVisibleTop() const
return topTile; 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) void Object::Instance::finalize(RmgMap & map)
{ {
if(!map.isOnMap(getPosition(true))) if(!map.isOnMap(getPosition(true)))

View File

@@ -78,6 +78,9 @@ public:
const Area & getArea() const; //lazy cache invalidation const Area & getArea() const; //lazy cache invalidation
const int3 getVisibleTop() const; const int3 getVisibleTop() const;
bool isGuarded() const;
void setGuardedIfMonster(const Instance & object);
void finalize(RmgMap & map); void finalize(RmgMap & map);
void clear(); void clear();
@@ -87,6 +90,7 @@ private:
mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache; mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache;
int3 dPosition; int3 dPosition;
ui32 dStrength; ui32 dStrength;
bool guarded;
}; };
} }

View File

@@ -701,23 +701,24 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
} }
} }
void TreasurePlacer::createTreasures(ObjectManager & manager) void TreasurePlacer::createTreasures(ObjectManager& manager)
{ {
const int maxAttempts = 2; const int maxAttempts = 2;
const int minDistance = 2;
int mapMonsterStrength = map.getMapGenOptions().getMonsterStrength(); 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 }; static int minGuardedValues[] = { 6500, 4167, 3000, 1833, 1333 };
minGuardedValue = minGuardedValues[monsterStrength]; 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; return lhs.max > rhs.max;
}; };
auto restoreZoneLimits = [](const std::vector<ObjectInfo*> & treasurePile) auto restoreZoneLimits = [](const std::vector<ObjectInfo*>& treasurePile)
{ {
for(auto * oi : treasurePile) for (auto* oi : treasurePile)
{ {
oi->maxPerZone++; oi->maxPerZone++;
} }
@@ -733,9 +734,18 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
return oi1.value < oi2.value; return oi1.value < oi2.value;
}); });
size_t size = 0;
{
Zone::Lock lock(zone.areaMutex);
size = zone.areaPossible().getTiles().size();
}
int totalDensity = 0; int totalDensity = 0;
for (auto t : treasureInfo) for (auto t : treasureInfo)
{ {
std::vector<rmg::Object> treasures;
//discard objects with too high value to be ever placed //discard objects with too high value to be ever placed
vstd::erase_if(possibleObjects, [t](const ObjectInfo& oi) -> bool vstd::erase_if(possibleObjects, [t](const ObjectInfo& oi) -> bool
{ {
@@ -744,26 +754,33 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
totalDensity += t.density; 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 //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 //also, normalize it to zone count - higher count means relatively smaller zones
//this is squared distance for optimization purposes //this is squared distance for optimization purposes
const float minDistance = std::max<float>((125.f / totalDensity), 2.0f); const float minDistance = std::max<float>((125.f / totalDensity), 1.0f);
//distance lower than 2 causes objects to overlap and crash
for(int attempt = 0; attempt <= maxAttempts;) for (size_t i = 0; i < count;)
{ {
auto treasurePileInfos = prepareTreasurePile(t); auto treasurePileInfos = prepareTreasurePile(t);
if(treasurePileInfos.empty()) if (treasurePileInfos.empty())
{ {
++attempt;
continue; continue;
} }
else
{
i++;
}
int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0, [](int v, const ObjectInfo * oi){return v + oi->value;}); 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++)
{
auto rmgObject = constructTreasurePile(treasurePileInfos, attempt == maxAttempts); auto rmgObject = constructTreasurePile(treasurePileInfos, attempt == maxAttempts);
if(rmgObject.instances().empty()) //handle incorrect placement
if (rmgObject.instances().empty()) //handle incorrect placement
{ {
restoreZoneLimits(treasurePileInfos); restoreZoneLimits(treasurePileInfos);
continue; continue;
@@ -771,32 +788,43 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
//guard treasure pile //guard treasure pile
bool guarded = isGuardNeededForTreasure(value); bool guarded = isGuardNeededForTreasure(value);
if(guarded) if (guarded)
guarded = manager.addGuard(rmgObject, value); guarded = manager.addGuard(rmgObject, value);
treasures.push_back(rmgObject);
break;
}
}
for (auto& rmgObject : treasures)
{
const bool guarded = rmgObject.isGuarded();
for (int attempt = 0; attempt <= maxAttempts;)
{
auto path = rmg::Path::invalid();
Zone::Lock lock(zone.areaMutex); //We are going to subtract this area Zone::Lock lock(zone.areaMutex); //We are going to subtract this area
//TODO: Don't place
auto possibleArea = zone.areaPossible(); auto possibleArea = zone.areaPossible();
auto path = rmg::Path::invalid(); if (guarded)
if(guarded)
{ {
path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3 & tile) path = manager.placeAndConnectObject(possibleArea, rmgObject, [this, &rmgObject, &minDistance, &manager](const int3& tile)
{ {
auto ti = map.getTileInfo(tile); auto ti = map.getTileInfo(tile);
if(ti.getNearestObjectDistance() < minDistance) if (ti.getNearestObjectDistance() < minDistance)
return -1.f; return -1.f;
for(const auto & t : rmgObject.getArea().getTilesVector()) for (const auto& t : rmgObject.getArea().getTilesVector())
{ {
if(map.getTileInfo(t).getNearestObjectDistance() < minDistance) if (map.getTileInfo(t).getNearestObjectDistance() < minDistance)
return -1.f; return -1.f;
} }
auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); auto guardedArea = rmgObject.instances().back()->getAccessibleArea();
auto areaToBlock = rmgObject.getAccessibleArea(true); auto areaToBlock = rmgObject.getAccessibleArea(true);
areaToBlock.subtract(guardedArea); areaToBlock.subtract(guardedArea);
if(areaToBlock.overlap(zone.freePaths()) || areaToBlock.overlap(manager.getVisitableArea())) if (areaToBlock.overlap(zone.freePaths()) || areaToBlock.overlap(manager.getVisitableArea()))
return -1.f; return -1.f;
return ti.getNearestObjectDistance(); return ti.getNearestObjectDistance();
@@ -807,11 +835,11 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE); path = manager.placeAndConnectObject(possibleArea, rmgObject, minDistance, guarded, false, ObjectManager::OptimizeType::DISTANCE);
} }
if(path.valid()) if (path.valid())
{ {
//debug purposes //debug purposes
treasureArea.unite(rmgObject.getArea()); treasureArea.unite(rmgObject.getArea());
if(guarded) if (guarded)
{ {
guards.unite(rmgObject.instances().back()->getBlockedArea()); guards.unite(rmgObject.instances().back()->getBlockedArea());
auto guardedArea = rmgObject.instances().back()->getAccessibleArea(); auto guardedArea = rmgObject.instances().back()->getAccessibleArea();
@@ -821,16 +849,15 @@ void TreasurePlacer::createTreasures(ObjectManager & manager)
} }
zone.connectPath(path); zone.connectPath(path);
manager.placeObject(rmgObject, guarded, true); manager.placeObject(rmgObject, guarded, true);
attempt = 0; break;
} }
else else
{ {
restoreZoneLimits(treasurePileInfos);
rmgObject.clear();
++attempt; ++attempt;
} }
} }
} }
}
} }
char TreasurePlacer::dump(const int3 & t) char TreasurePlacer::dump(const int3 & t)