1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Working fir for Corpse issue:

- Do not place guard next to blockVis object, if possible
- Do not place two blockVis objects next to each other
This commit is contained in:
Tomasz Zieliński 2023-12-06 20:49:28 +01:00
parent 03fa75c51e
commit 6cd19b81dd
11 changed files with 149 additions and 28 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

@ -19,6 +19,7 @@
"value" : 2000,
"rarity" : 500
},
"removable": true,
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
@ -61,6 +62,7 @@
"value" : 2000,
"rarity" : 100
},
"removable": true,
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
@ -119,6 +121,7 @@
"value" : 1500,
"rarity" : 500
},
"removable": true,
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
@ -170,6 +173,7 @@
"value" : 1500,
"rarity" : 50
},
"removable": true,
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,
@ -221,6 +225,7 @@
"value" : 1500,
"rarity" : 1000
},
"removable": true,
"compatibilityIdentifiers" : [ "object" ],
"blockedVisitable" : true,

View File

@ -96,6 +96,18 @@ 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
if(input["blockVisit"].isNull())
blockVisit = false;
else
blockVisit = input["blockVisit"].Bool();
if(input["removable"].isNull())
removable = false;
else
removable = input["removable"].Bool();
battlefield = BattleField::NONE;
if(!input["battleground"].isNull())
@ -120,6 +132,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

@ -153,6 +153,11 @@ bool Object::Instance::isBlockedVisitable() const
return dObject.isBlockedVisitable();
}
bool Object::Instance::isRemovable() const
{
return dObject.isRemovable();
}
CGObjectInstance & Object::Instance::object()
{
return dObject;
@ -269,21 +274,52 @@ const rmg::Area & Object::getBlockVisitableArea() const
{
if(dInstances.empty())
return dBlockVisitableCache;
for(auto i = dInstances.begin(); i != std::prev(dInstances.end()); ++i)
for(const auto i : dInstances)
{
// FIXME: Account for blockvis objects with multiple visitable tiles
if (i->isBlockedVisitable())
dAccessibleAreaCache.add(i->getVisitablePosition());
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;
@ -390,6 +426,7 @@ void Object::clearCachedArea() const
dAccessibleAreaCache.clear();
dAccessibleAreaFullCache.clear();
dBlockVisitableCache.clear();
dRemovableAreaCache.clear();
}
void Object::clear()

View File

@ -36,6 +36,7 @@ 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
@ -73,6 +74,8 @@ 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);
@ -93,6 +96,7 @@ private:
mutable Area dFullAreaCache;
mutable Area dAccessibleAreaCache, dAccessibleAreaFullCache;
mutable Area dBlockVisitableCache;
mutable Area dRemovableAreaCache;
int3 dPosition;
ui32 dStrength;
bool guarded;

View File

@ -664,7 +664,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;
if (entrableTiles.empty())
{
entrableTile = object.getVisitablePosition();
}
else
{
*RandomGeneratorUtil::nextItem(entrableTiles, zone.getRand());
}
rmg::Area visitablePos({entrableTile});
visitablePos.unite(visitablePos.getBorderOutside());
auto accessibleArea = object.getAccessibleArea();

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,7 +691,6 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
auto instanceAccessibleArea = instance.getAccessibleArea();
if(instance.getBlockedArea().getTilesVector().size() == 1)
{
//TOOD: Move hardcoded option to template
if(instance.object().appearance->isVisitableFromTop() && !instance.object().isBlockedVisitable())
instanceAccessibleArea.add(instance.getVisitablePosition());
}
@ -684,16 +698,12 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
//first object is good
if(rmgObject.instances().size() == 1)
break;
// TODO: Do not place blockvis objects so that they block access to existing accessible area
// Either object must be removable, or both must be accessible independently
//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;