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:
parent
03fa75c51e
commit
6cd19b81dd
@ -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"]
|
||||
|
@ -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" ]
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user