1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +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; 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 const rmg::Area Object::getEntrableArea() const
{ {
// Calculate Area that hero can freely pass // 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.) // Do not use blockVisitTiles, unless they belong to removable objects (resources etc.)
// area = accessibleArea - (blockVisitableArea - removableArea) // 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(); rmg::Area blockVisitableArea = getBlockVisitableArea();
blockVisitableArea.subtract(getRemovableArea()); blockVisitableArea.subtract(getRemovableArea());
entrableArea.subtract(blockVisitableArea); entrableArea.subtract(blockVisitableArea);
@@ -330,11 +344,14 @@ const rmg::Area Object::getEntrableArea() const
void Object::setPosition(const int3 & position) void Object::setPosition(const int3 & position)
{ {
dAccessibleAreaCache.translate(position - dPosition); auto shift = position - dPosition;
dAccessibleAreaFullCache.translate(position - dPosition);
dBlockVisitableCache.translate(position - dPosition); dAccessibleAreaCache.translate(shift);
dRemovableAreaCache.translate(position - dPosition); dAccessibleAreaFullCache.translate(shift);
dFullAreaCache.translate(position - dPosition); dBlockVisitableCache.translate(shift);
dVisitableCache.translate(shift);
dRemovableAreaCache.translate(shift);
dFullAreaCache.translate(shift);
dPosition = position; dPosition = position;
for(auto& i : dInstances) for(auto& i : dInstances)
@@ -450,6 +467,7 @@ void Object::clearCachedArea() const
dAccessibleAreaCache.clear(); dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear(); dAccessibleAreaFullCache.clear();
dBlockVisitableCache.clear(); dBlockVisitableCache.clear();
dVisitableCache.clear();
dRemovableAreaCache.clear(); dRemovableAreaCache.clear();
} }

View File

@@ -74,6 +74,7 @@ public:
int3 getVisitablePosition() const; int3 getVisitablePosition() const;
const Area & getAccessibleArea(bool exceptLast = false) const; const Area & getAccessibleArea(bool exceptLast = false) const;
const Area & getBlockVisitableArea() const; const Area & getBlockVisitableArea() const;
const Area & getVisitableArea() const;
const Area & getRemovableArea() const; const Area & getRemovableArea() const;
const Area getEntrableArea() const; const Area getEntrableArea() const;
@@ -96,6 +97,7 @@ private:
mutable Area dFullAreaCache; mutable Area dFullAreaCache;
mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache; mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache;
mutable Area dBlockVisitableCache; mutable Area dBlockVisitableCache;
mutable Area dVisitableCache;
mutable Area dRemovableAreaCache; mutable Area dRemovableAreaCache;
int3 dPosition; int3 dPosition;
mutable std::optional<int3> visibleTopOffset; 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) //Cannot be trespassed (Corpse)
continue; continue;
} }
else if(instance->object().appearance->isVisitableFromTop()) else if(!instance->object().appearance->isVisitableFromTop())
m->areaForRoads().add(instance->getVisitablePosition());
else
{ {
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; return false;
// Prefer non-blocking tiles, if any // Prefer non-blocking tiles, if any
const auto & entrableTiles = object.getEntrableArea().getTilesVector(); auto entrableArea = object.getEntrableArea();
int3 entrableTile(-1, -1, -1); if (entrableArea.empty())
if (entrableTiles.empty())
{ {
entrableTile = object.getVisitablePosition(); entrableArea.add(object.getVisitablePosition());
}
else
{
entrableTile = *RandomGeneratorUtil::nextItem(entrableTiles, zone.getRand());
} }
rmg::Area visitablePos({entrableTile}); rmg::Area entrableBorder = entrableArea.getBorderOutside();
visitablePos.unite(visitablePos.getBorderOutside());
auto accessibleArea = object.getAccessibleArea(); auto accessibleArea = object.getAccessibleArea();
accessibleArea.intersect(visitablePos); accessibleArea.erase_if([&](const int3 & tile)
{
return !entrableBorder.contains(tile);
});
if(accessibleArea.empty()) if(accessibleArea.empty())
{ {
delete guard; delete guard;

View File

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