浏览代码

Merge pull request #3259 from vcmi/fix_corpse

Fix corpse
Ivan Savenko 1 年之前
父节点
当前提交
c9de10ea74

+ 21 - 1
config/objects/generic.json

@@ -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"]

+ 7 - 0
config/objects/moddables.json

@@ -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" ]

+ 1 - 2
config/objects/rewardableOnceVisitable.json

@@ -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" : [

+ 10 - 9
config/objects/rewardablePickable.json

@@ -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" ]

+ 6 - 0
lib/mapObjectConstructors/AObjectTypeHandler.cpp

@@ -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)

+ 3 - 0
lib/mapObjectConstructors/AObjectTypeHandler.h

@@ -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;

+ 11 - 7
lib/mapObjects/CGObjectInstance.cpp

@@ -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;

+ 5 - 0
lib/mapObjects/CGObjectInstance.h

@@ -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
 	}

+ 76 - 22
lib/rmg/RmgObject.cpp

@@ -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();
 }
  
 

+ 8 - 0
lib/rmg/RmgObject.h

@@ -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;

+ 2 - 2
lib/rmg/modificators/ObjectDistributor.cpp

@@ -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 *

+ 19 - 3
lib/rmg/modificators/ObjectManager.cpp

@@ -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();

+ 1 - 1
lib/rmg/modificators/RoadPlacer.cpp

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

+ 36 - 17
lib/rmg/modificators/TreasurePlacer.cpp

@@ -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;
 				}