1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Definitive solution for Corpse

This commit is contained in:
Tomasz Zieliński 2023-12-21 12:29:45 +01:00
parent e22f6dd07e
commit d5f9de5beb
4 changed files with 69 additions and 37 deletions

View File

@ -313,6 +313,19 @@ const rmg::Area & Object::getRemovableArea() const
return dRemovableAreaCache;
}
const rmg::Area & Object::getVisitableArea() const
{
if(dVisitableCache.empty())
{
for(const auto & i : dInstances)
{
// FIXME: Account for bjects with multiple visitable tiles
dVisitableCache.add(i.getVisitablePosition());
}
}
return dVisitableCache;
}
const rmg::Area Object::getEntrableArea() const
{
// Calculate Area that hero can freely pass
@ -320,7 +333,8 @@ const rmg::Area Object::getEntrableArea() const
// Do not use blockVisitTiles, unless they belong to removable objects (resources etc.)
// area = accessibleArea - (blockVisitableArea - removableArea)
rmg::Area entrableArea = getAccessibleArea();
// FIXME: What does it do? AccessibleArea means area AROUND the object
rmg::Area entrableArea = getVisitableArea();
rmg::Area blockVisitableArea = getBlockVisitableArea();
blockVisitableArea.subtract(getRemovableArea());
entrableArea.subtract(blockVisitableArea);
@ -330,11 +344,14 @@ const rmg::Area Object::getEntrableArea() const
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);
auto shift = position - dPosition;
dAccessibleAreaCache.translate(shift);
dAccessibleAreaFullCache.translate(shift);
dBlockVisitableCache.translate(shift);
dVisitableCache.translate(shift);
dRemovableAreaCache.translate(shift);
dFullAreaCache.translate(shift);
dPosition = position;
for(auto& i : dInstances)
@ -450,6 +467,7 @@ void Object::clearCachedArea() const
dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear();
dBlockVisitableCache.clear();
dVisitableCache.clear();
dRemovableAreaCache.clear();
}

View File

@ -74,6 +74,7 @@ public:
int3 getVisitablePosition() const;
const Area & getAccessibleArea(bool exceptLast = false) const;
const Area & getBlockVisitableArea() const;
const Area & getVisitableArea() const;
const Area & getRemovableArea() const;
const Area getEntrableArea() const;
@ -96,6 +97,7 @@ private:
mutable Area dFullAreaCache;
mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache;
mutable Area dBlockVisitableCache;
mutable Area dVisitableCache;
mutable Area dRemovableAreaCache;
int3 dPosition;
mutable std::optional<int3> visibleTopOffset;

View File

@ -600,11 +600,13 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
//Cannot be trespassed (Corpse)
continue;
}
else if(instance->object().appearance->isVisitableFromTop())
m->areaForRoads().add(instance->getVisitablePosition());
else
else if(!instance->object().appearance->isVisitableFromTop())
{
m->areaIsolated().add(instance->getVisitablePosition() + int3(0, -1, 0));
auto abovePos = instance->getVisitablePosition() + int3(0, -1, 0);
if (!instance->object().blockingAt(abovePos))
{
m->areaIsolated().add(abovePos);
}
}
}
@ -718,22 +720,20 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
return false;
// Prefer non-blocking tiles, if any
const auto & entrableTiles = object.getEntrableArea().getTilesVector();
int3 entrableTile(-1, -1, -1);
if (entrableTiles.empty())
auto entrableArea = object.getEntrableArea();
if (entrableArea.empty())
{
entrableTile = object.getVisitablePosition();
}
else
{
entrableTile = *RandomGeneratorUtil::nextItem(entrableTiles, zone.getRand());
entrableArea.add(object.getVisitablePosition());
}
rmg::Area visitablePos({entrableTile});
visitablePos.unite(visitablePos.getBorderOutside());
rmg::Area entrableBorder = entrableArea.getBorderOutside();
auto accessibleArea = object.getAccessibleArea();
accessibleArea.intersect(visitablePos);
accessibleArea.erase_if([&](const int3 & tile)
{
return !entrableBorder.contains(tile);
});
if(accessibleArea.empty())
{
delete guard;

View File

@ -633,9 +633,12 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
{
auto blockedArea = rmgObject.getArea();
auto entrableArea = rmgObject.getEntrableArea();
auto accessibleArea = rmgObject.getAccessibleArea();
if(rmgObject.instances().empty())
entrableArea.add(int3());
{
accessibleArea.add(int3());
}
auto * object = oi->generateObject();
if(oi->templates.empty())
@ -650,20 +653,21 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
object->appearance = *RandomGeneratorUtil::nextItem(templates, zone.getRand());
auto blockingIssue = object->isBlockedVisitable() && !object->isRemovable();
if (blockingIssue)
//Put object in accessible area next to entrable area (excluding blockvis tiles)
if (!entrableArea.empty())
{
// 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 entrableBorder = entrableArea.getBorderOutside();
accessibleArea.erase_if([&](const int3 & tile)
{
return !entrableBorder.count(tile);
});
}
auto & instance = rmgObject.addInstance(*object);
do
{
if(entrableArea.empty())
if(accessibleArea.empty())
{
//fail - fallback
rmgObject.clear();
@ -671,15 +675,24 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
}
std::vector<int3> bestPositions;
if(densePlacement)
if(densePlacement && !entrableArea.empty())
{
// Choose positon which has access to as many entrable tiles as possible
int bestPositionsWeight = std::numeric_limits<int>::max();
for(const auto & t : entrableArea.getTilesVector())
for(const auto & t : accessibleArea.getTilesVector())
{
instance.setPosition(t);
int w = rmgObject.getEntrableArea().getTilesVector().size();
if(w && w < bestPositionsWeight)
auto currentAccessibleArea = rmgObject.getAccessibleArea();
auto currentEntrableBorder = rmgObject.getEntrableArea().getBorderOutside();
currentAccessibleArea.erase_if([&](const int3 & tile)
{
return !currentEntrableBorder.count(tile);
});
size_t w = currentAccessibleArea.getTilesVector().size();
if(w > bestPositionsWeight)
{
// Minimum 1 position must be entrable
bestPositions.clear();
@ -691,12 +704,11 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
bestPositions.push_back(t);
}
}
}
if (bestPositions.empty())
{
bestPositions = entrableArea.getTilesVector();
bestPositions = accessibleArea.getTilesVector();
}
int3 nextPos = *RandomGeneratorUtil::nextItem(bestPositions, zone.getRand());
@ -713,11 +725,11 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
if(rmgObject.instances().size() == 1)
break;
if(!blockedArea.overlap(instance.getBlockedArea()) && entrableArea.overlap(instanceAccessibleArea))
if(!blockedArea.overlap(instance.getBlockedArea()) && accessibleArea.overlap(instanceAccessibleArea))
break;
//fail - new position
entrableArea.erase(nextPos);
accessibleArea.erase(nextPos);
} while(true);
}
return rmgObject;