1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

Merge pull request #3259 from vcmi/fix_corpse

Fix corpse
This commit is contained in:
Ivan Savenko
2023-12-07 23:55:07 +02:00
committed by GitHub
14 changed files with 206 additions and 64 deletions

View File

@@ -10,7 +10,11 @@
}
},
"types" : {
"prison" : { "index" : 0, "aiValue" : 5000 }
"prison" : {
"index" : 0,
"aiValue" : 5000,
"removable": true
}
}
},
@@ -136,6 +140,7 @@
"object" : {
"index" : 0,
"aiValue" : 10000,
"removable": true,
"templates" : {
"normal" : { "animation" : "ava0128.def", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
},
@@ -150,6 +155,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"rmg" : {
}
}
@@ -313,6 +319,7 @@
"object" : {
"index" : 0,
"aiValue" : 0,
"removable": true,
"rmg" : {
}
}
@@ -444,6 +451,7 @@
"object" : {
"index" : 0,
"aiValue" : 10000,
"removable": true,
"rmg" : {
}
}
@@ -495,6 +503,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"rmg" : {
"value" : 2000,
"rarity" : 150
@@ -511,6 +520,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"rmg" : {
"value" : 5000,
"rarity" : 150
@@ -527,6 +537,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"rmg" : {
"value" : 10000,
"rarity" : 150
@@ -543,6 +554,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"rmg" : {
"value" : 20000,
"rarity" : 150
@@ -572,6 +584,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"templates" : {
"normal" : { "animation" : "AVWmon1", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
}
@@ -584,6 +597,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"templates" : {
"normal" : { "animation" : "AVWmon2", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
}
@@ -596,6 +610,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"templates" : {
"normal" : { "animation" : "AVWmon3", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
}
@@ -608,6 +623,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"templates" : {
"normal" : { "animation" : "AVWmon4", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
}
@@ -632,6 +648,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"templates" : {
"normal" : { "animation" : "AVWmon6", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
}
@@ -644,6 +661,7 @@
"types" : {
"object" : {
"index" : 0,
"removable": true,
"templates" : {
"normal" : { "animation" : "AVWmon7", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
}
@@ -687,6 +705,7 @@
"object" : {
"index" : 0,
"aiValue" : 0,
"removable": true,
"rmg" : {
}
}
@@ -723,6 +742,7 @@
"index" :95,
"handler": "generic",
"base" : {
"blockVisit": true,
"sounds" : {
"ambient" : ["LOOPTAV"],
"visit" : ["STORE"]

View File

@@ -8,6 +8,7 @@
"index" :5,
"handler": "artifact",
"base" : {
"removable": true,
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VV", "VA"]
@@ -25,6 +26,7 @@
"handler": "hero",
"base" : {
"aiValue" : 7500,
"removable": true,
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VVV", "VAV"]
@@ -40,6 +42,7 @@
"index" :54,
"handler": "monster",
"base" : {
"removable": true,
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VV", "VA"]
@@ -55,6 +58,7 @@
"index" :76,
"handler": "randomResource",
"base" : {
"removable": true,
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VA" ]
@@ -85,6 +89,7 @@
"handler": "resource",
"lastReservedIndex" : 6,
"base" : {
"removable": true,
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VA" ]
@@ -138,6 +143,7 @@
"lastReservedIndex" : 2,
"base" : {
"aiValue" : 0,
"removable": true,
"layer" : "sail",
"onboardAssaultAllowed" : true,
"onboardVisitAllowed" : true,
@@ -175,6 +181,7 @@
"lastReservedIndex" : 7,
"base" : {
"aiValue" : 0,
"removable": true,
"sounds" : {
"visit" : ["CAVEHEAD"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]

View File

@@ -41,6 +41,7 @@
"index" : 22,
"handler": "configurable",
"base" : {
"blockedVisitable" : true,
"sounds" : {
"visit" : ["MYSTERY"]
}
@@ -54,9 +55,7 @@
"rarity" : 100
},
"compatibilityIdentifiers" : [ "object" ],
"onVisitedMessage" : 38,
"blockedVisitable" : true,
"visitMode" : "once",
"selectMode" : "selectFirst",
"rewards" : [

View File

@@ -5,6 +5,8 @@
"index" : 12,
"handler": "configurable",
"base" : {
"blockedVisitable" : true,
"removable": true,
"sounds" : {
"ambient" : ["LOOPCAMP"],
"visit" : ["EXPERNCE"],
@@ -20,8 +22,6 @@
"rarity" : 500
},
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
@@ -48,6 +48,8 @@
"index" : 29,
"handler": "configurable",
"base" : {
"blockedVisitable" : true,
"removable": true,
"sounds" : {
"visit" : ["GENIE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
@@ -62,15 +64,13 @@
"rarity" : 100
},
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
{
"message" : 51,
"appearChance" : { "max" : 25 },
"removeObject" : true,
"removeObject" : true
},
{
"message" : 52,
@@ -106,6 +106,8 @@
"index" : 82,
"handler": "configurable",
"base" : {
"blockedVisitable" : true,
"removable": true,
"sounds" : {
"visit" : ["CHEST"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
@@ -120,8 +122,6 @@
"rarity" : 500
},
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
@@ -157,6 +157,8 @@
"index" : 86,
"handler": "configurable",
"base" : {
"blockedVisitable" : true,
"removable": true,
"sounds" : {
"visit" : ["TREASURE"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
@@ -171,8 +173,6 @@
"rarity" : 50
},
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
"visitMode" : "unlimited",
"selectMode" : "selectFirst",
"rewards" : [
@@ -208,6 +208,7 @@
"index" : 101,
"handler": "configurable",
"base" : {
"removable": true,
"sounds" : {
"visit" : ["CHEST"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]

View File

@@ -96,6 +96,10 @@ void AObjectTypeHandler::init(const JsonNode & input)
else
aiValue = static_cast<std::optional<si32>>(input["aiValue"].Integer());
// TODO: Define properties, move them to actual object instance
blockVisit = input["blockVisit"].Bool();
removable = input["removable"].Bool();
battlefield = BattleField::NONE;
if(!input["battleground"].isNull())
@@ -120,6 +124,8 @@ void AObjectTypeHandler::preInitObject(CGObjectInstance * obj) const
obj->subID = subtype;
obj->typeName = typeName;
obj->subTypeName = subTypeName;
obj->blockVisit = blockVisit;
obj->removable = removable;
}
void AObjectTypeHandler::initTypeData(const JsonNode & input)

View File

@@ -43,6 +43,9 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
si32 type;
si32 subtype;
bool blockVisit;
bool removable;
protected:
void preInitObject(CGObjectInstance * obj) const;
virtual bool objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const;

View File

@@ -33,7 +33,8 @@ CGObjectInstance::CGObjectInstance():
ID(Obj::NO_OBJ),
subID(-1),
tempOwner(PlayerColor::UNFLAGGABLE),
blockVisit(false)
blockVisit(false),
removable(false)
{
}
@@ -173,12 +174,7 @@ void CGObjectInstance::pickRandomObject(CRandomGenerator & rand)
void CGObjectInstance::initObj(CRandomGenerator & rand)
{
switch(ID.toEnum())
{
case Obj::TAVERN:
blockVisit = true;
break;
}
// no-op
}
void CGObjectInstance::setProperty( ObjProperty what, ObjPropertyID identifier )
@@ -191,6 +187,7 @@ void CGObjectInstance::setProperty( ObjProperty what, ObjPropertyID identifier )
tempOwner = identifier.as<PlayerColor>();
break;
case ObjProperty::BLOCKVIS:
// Never actually used in code, but possible in ERM
blockVisit = identifier.getNum();
break;
case ObjProperty::ID:
@@ -327,9 +324,16 @@ bool CGObjectInstance::isVisitable() const
bool CGObjectInstance::isBlockedVisitable() const
{
// TODO: Read from json
return blockVisit;
}
bool CGObjectInstance::isRemovable() const
{
// TODO: Read from json
return removable;
}
bool CGObjectInstance::isCoastVisitable() const
{
return false;

View File

@@ -54,6 +54,7 @@ public:
int3 getSightCenter() const;
/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
bool blockVisit;
bool removable;
PlayerColor getOwner() const override
{
@@ -85,6 +86,9 @@ public:
/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
virtual bool isBlockedVisitable() const;
// If true, can be possibly removed from the map
virtual bool isRemovable() const;
/// If true this object can be visited by hero standing on the coast
virtual bool isCoastVisitable() const;
@@ -144,6 +148,7 @@ public:
h & id;
h & tempOwner;
h & blockVisit;
h & removable;
h & appearance;
//definfo is handled by map serializer
}

View File

@@ -40,7 +40,9 @@ const Area & Object::Instance::getBlockedArea() const
{
dBlockedAreaCache.assign(dObject.getBlockedPos());
if(dObject.isVisitable() || dBlockedAreaCache.empty())
dBlockedAreaCache.add(dObject.visitablePos());
if (!dObject.isBlockedVisitable())
// Do no assume blocked tile is accessible
dBlockedAreaCache.add(dObject.visitablePos());
}
return dBlockedAreaCache;
}
@@ -85,9 +87,7 @@ void Object::Instance::setPosition(const int3 & position)
dBlockedAreaCache.clear();
dAccessibleAreaCache.clear();
dParent.dAccessibleAreaCache.clear();
dParent.dAccessibleAreaFullCache.clear();
dParent.dFullAreaCache.clear();
dParent.clearCachedArea();
}
void Object::Instance::setPositionRaw(const int3 & position)
@@ -97,9 +97,7 @@ void Object::Instance::setPositionRaw(const int3 & position)
dObject.pos = dPosition + dParent.getPosition();
dBlockedAreaCache.clear();
dAccessibleAreaCache.clear();
dParent.dAccessibleAreaCache.clear();
dParent.dAccessibleAreaFullCache.clear();
dParent.dFullAreaCache.clear();
dParent.clearCachedArea();
}
auto shift = position + dParent.getPosition() - dObject.pos;
@@ -141,9 +139,7 @@ void Object::Instance::clear()
delete &dObject;
dBlockedAreaCache.clear();
dAccessibleAreaCache.clear();
dParent.dAccessibleAreaCache.clear();
dParent.dAccessibleAreaFullCache.clear();
dParent.dFullAreaCache.clear();
dParent.clearCachedArea();
}
bool Object::Instance::isVisitableFrom(const int3 & position) const
@@ -152,6 +148,16 @@ bool Object::Instance::isVisitableFrom(const int3 & position) const
return dObject.appearance->isVisitableFrom(relPosition.x, relPosition.y);
}
bool Object::Instance::isBlockedVisitable() const
{
return dObject.isBlockedVisitable();
}
bool Object::Instance::isRemovable() const
{
return dObject.isRemovable();
}
CGObjectInstance & Object::Instance::object()
{
return dObject;
@@ -205,9 +211,7 @@ void Object::addInstance(Instance & object)
setGuardedIfMonster(object);
dInstances.push_back(object);
dFullAreaCache.clear();
dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear();
clearCachedArea();
}
Object::Instance & Object::addInstance(CGObjectInstance & object)
@@ -215,9 +219,7 @@ Object::Instance & Object::addInstance(CGObjectInstance & object)
dInstances.emplace_back(*this, object);
setGuardedIfMonster(dInstances.back());
dFullAreaCache.clear();
dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear();
clearCachedArea();
return dInstances.back();
}
@@ -226,9 +228,7 @@ Object::Instance & Object::addInstance(CGObjectInstance & object, const int3 & p
dInstances.emplace_back(*this, object, position);
setGuardedIfMonster(dInstances.back());
dFullAreaCache.clear();
dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear();
clearCachedArea();
return dInstances.back();
}
@@ -270,10 +270,56 @@ const rmg::Area & Object::getAccessibleArea(bool exceptLast) const
return dAccessibleAreaFullCache;
}
const rmg::Area & Object::getBlockVisitableArea() const
{
if(dInstances.empty())
return dBlockVisitableCache;
for(const auto & i : dInstances)
{
// FIXME: Account for blockvis objects with multiple visitable tiles
if (i.isBlockedVisitable())
dBlockVisitableCache.add(i.getVisitablePosition());
}
return dBlockVisitableCache;
}
const rmg::Area & Object::getRemovableArea() const
{
if(dInstances.empty())
return dRemovableAreaCache;
for(const auto & i : dInstances)
{
if (i.isRemovable())
dRemovableAreaCache.unite(i.getBlockedArea());
}
return dRemovableAreaCache;
}
const rmg::Area Object::getEntrableArea() const
{
// Calculate Area that hero can freely pass
// Do not use blockVisitTiles, unless they belong to removable objects (resources etc.)
// area = accessibleArea - (blockVisitableArea - removableArea)
rmg::Area entrableArea = getAccessibleArea();
rmg::Area blockVisitableArea = getBlockVisitableArea();
blockVisitableArea.subtract(getRemovableArea());
entrableArea.subtract(blockVisitableArea);
return entrableArea;
}
void Object::setPosition(const int3 & position)
{
dAccessibleAreaCache.translate(position - dPosition);
dAccessibleAreaFullCache.translate(position - dPosition);
dBlockVisitableCache.translate(position - dPosition);
dRemovableAreaCache.translate(position - dPosition);
dFullAreaCache.translate(position - dPosition);
dPosition = position;
@@ -374,14 +420,22 @@ void Object::finalize(RmgMap & map, CRandomGenerator & rng)
}
}
void Object::clearCachedArea() const
{
dFullAreaCache.clear();
dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear();
dBlockVisitableCache.clear();
dRemovableAreaCache.clear();
}
void Object::clear()
{
for(auto & instance : dInstances)
instance.clear();
dInstances.clear();
dFullAreaCache.clear();
dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear();
clearCachedArea();
}

View File

@@ -35,6 +35,8 @@ public:
int3 getVisitablePosition() const;
bool isVisitableFrom(const int3 & tile) const;
bool isBlockedVisitable() const;
bool isRemovable() const;
const Area & getAccessibleArea() const;
void setTemplate(TerrainId terrain, CRandomGenerator &); //cache invalidation
void setAnyTemplate(CRandomGenerator &); //cache invalidation
@@ -71,6 +73,9 @@ public:
int3 getVisitablePosition() const;
const Area & getAccessibleArea(bool exceptLast = false) const;
const Area & getBlockVisitableArea() const;
const Area & getRemovableArea() const;
const Area getEntrableArea() const;
const int3 & getPosition() const;
void setPosition(const int3 & position);
@@ -83,12 +88,15 @@ public:
void setGuardedIfMonster(const Instance & object);
void finalize(RmgMap & map, CRandomGenerator &);
void clearCachedArea() const;
void clear();
private:
std::list<Instance> dInstances;
mutable Area dFullAreaCache;
mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache;
mutable Area dBlockVisitableCache;
mutable Area dRemovableAreaCache;
int3 dPosition;
ui32 dStrength;
bool guarded;

View File

@@ -42,8 +42,6 @@ void ObjectDistributor::init()
void ObjectDistributor::distributeLimitedObjects()
{
//FIXME: Must be called after TerrainPainter::process()
ObjectInfo oi;
auto zones = map.getZones();
@@ -77,6 +75,8 @@ void ObjectDistributor::distributeLimitedObjects()
auto rmgInfo = handler->getRMGInfo();
// FIXME: Random order of distribution
RandomGeneratorUtil::randomShuffle(matchingZones, zone.getRand());
for (auto& zone : matchingZones)
{
oi.generateObject = [primaryID, secondaryID]() -> CGObjectInstance *

View File

@@ -546,8 +546,12 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
objects.push_back(&instance->object());
if(auto * m = zone.getModificator<RoadPlacer>())
{
//FIXME: Objects that can be removed, can be trespassed. Does not include Corpse
if(instance->object().appearance->isVisitableFromTop())
if (instance->object().blockVisit && !instance->object().removable)
{
//Cannot be trespassed (Corpse)
continue;
}
else if(instance->object().appearance->isVisitableFromTop())
m->areaForRoads().add(instance->getVisitablePosition());
else
{
@@ -664,7 +668,19 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
if(!guard)
return false;
rmg::Area visitablePos({object.getVisitablePosition()});
// Prefer non-blocking tiles, if any
auto entrableTiles = object.getEntrableArea().getTiles();
int3 entrableTile(-1, -1, -1);
if (entrableTiles.empty())
{
entrableTile = object.getVisitablePosition();
}
else
{
entrableTile = *RandomGeneratorUtil::nextItem(entrableTiles, zone.getRand());
}
rmg::Area visitablePos({entrableTile});
visitablePos.unite(visitablePos.getBorderOutside());
auto accessibleArea = object.getAccessibleArea();

View File

@@ -85,7 +85,7 @@ bool RoadPlacer::createRoad(const int3 & dst)
{
if(areaIsolated().contains(dst) || areaIsolated().contains(src))
{
return 1e30;
return 1e12;
}
}
else

View File

@@ -625,20 +625,31 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
for(const auto & oi : treasureInfos)
{
auto blockedArea = rmgObject.getArea();
auto accessibleArea = rmgObject.getAccessibleArea();
auto entrableArea = rmgObject.getEntrableArea();
if(rmgObject.instances().empty())
accessibleArea.add(int3());
entrableArea.add(int3());
auto * object = oi->generateObject();
if(oi->templates.empty())
continue;
object->appearance = *RandomGeneratorUtil::nextItem(oi->templates, zone.getRand());
auto blockingIssue = object->isBlockedVisitable() && !object->isRemovable();
if (blockingIssue)
{
// Do not place next to another such object (Corpse issue)
// Calculate this before instance is added to rmgObject
auto blockVisitProximity = rmgObject.getBlockVisitableArea().getBorderOutside();
entrableArea.subtract(blockVisitProximity);
}
auto & instance = rmgObject.addInstance(*object);
do
{
if(accessibleArea.empty())
if(entrableArea.empty())
{
//fail - fallback
rmgObject.clear();
@@ -649,12 +660,14 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
if(densePlacement)
{
int bestPositionsWeight = std::numeric_limits<int>::max();
for(const auto & t : accessibleArea.getTilesVector())
for(const auto & t : entrableArea.getTilesVector())
{
instance.setPosition(t);
int w = rmgObject.getAccessibleArea().getTilesVector().size();
if(w < bestPositionsWeight)
int w = rmgObject.getEntrableArea().getTilesVector().size();
if(w && w < bestPositionsWeight)
{
// Minimum 1 position must be entrable
bestPositions.clear();
bestPositions.push_back(t);
bestPositionsWeight = w;
@@ -664,10 +677,12 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
bestPositions.push_back(t);
}
}
}
else
if (bestPositions.empty())
{
bestPositions = accessibleArea.getTilesVector();
bestPositions = entrableArea.getTilesVector();
}
int3 nextPos = *RandomGeneratorUtil::nextItem(bestPositions, zone.getRand());
@@ -676,20 +691,19 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
auto instanceAccessibleArea = instance.getAccessibleArea();
if(instance.getBlockedArea().getTilesVector().size() == 1)
{
if(instance.object().appearance->isVisitableFromTop() && instance.object().ID != Obj::CORPSE)
if(instance.object().appearance->isVisitableFromTop() && !instance.object().isBlockedVisitable())
instanceAccessibleArea.add(instance.getVisitablePosition());
}
//first object is good
if(rmgObject.instances().size() == 1)
break;
//condition for good position
if(!blockedArea.overlap(instance.getBlockedArea()) && accessibleArea.overlap(instanceAccessibleArea))
if(!blockedArea.overlap(instance.getBlockedArea()) && entrableArea.overlap(instanceAccessibleArea))
break;
//fail - new position
accessibleArea.erase(nextPos);
entrableArea.erase(nextPos);
} while(true);
}
return rmgObject;
@@ -822,13 +836,18 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
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++)
const ui32 maxPileGenerationAttemps = 2;
for (ui32 attempt = 0; attempt <= maxPileGenerationAttemps; attempt++)
{
auto rmgObject = constructTreasurePile(treasurePileInfos, attempt == maxAttempts);
if (rmgObject.instances().empty()) //handle incorrect placement
if (rmgObject.instances().empty())
{
restoreZoneLimits(treasurePileInfos);
// Restore once if all attemps failed
if (attempt == (maxPileGenerationAttemps - 1))
{
restoreZoneLimits(treasurePileInfos);
}
continue;
}