2
0
Эх сурвалжийг харах

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

Tomasz Zieliński 2 жил өмнө
parent
commit
6cd19b81dd

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

+ 5 - 0
config/objects/rewardablePickable.json

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

+ 14 - 0
lib/mapObjectConstructors/AObjectTypeHandler.cpp

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

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

+ 40 - 3
lib/rmg/RmgObject.cpp

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

+ 4 - 0
lib/rmg/RmgObject.h

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

+ 13 - 1
lib/rmg/modificators/ObjectManager.cpp

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

+ 26 - 16
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,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;