2
0
Tomasz Zieliński 2 жил өмнө
parent
commit
219e897db0

+ 216 - 0
lib/mapping/ObstacleProxy.cpp

@@ -0,0 +1,216 @@
+/*
+ * ObstacleProxy.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "ObstacleProxy.h"
+#include "../mapObjects/CObjectClassesHandler.h"
+#include "../mapObjects/CObjectHandler.h"
+#include "../mapping/CMap.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+void ObstacleProxy::collectPossibleObstacles(TerrainId terrain)
+{
+	//get all possible obstacles for this terrain
+	for(auto primaryID : VLC->objtypeh->knownObjects())
+	{
+		for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
+		{
+			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
+			if(handler->isStaticObject())
+			{
+				for(const auto & temp : handler->getTemplates())
+				{
+					if(temp->canBePlacedAt(terrain) && temp->getBlockMapOffset().valid())
+						obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp);
+				}
+			}
+		}
+	}
+	for(const auto & o : obstaclesBySize)
+	{
+		possibleObstacles.emplace_back(o);
+	}
+	boost::sort(possibleObstacles, [](const ObstaclePair &p1, const ObstaclePair &p2) -> bool
+	{
+		return p1.first > p2.first; //bigger obstacles first
+	});
+}
+
+void ObstacleProxy::addBlockedTile(const int3& tile)
+{
+	blockedArea.add(tile);
+}
+
+void ObstacleProxy::setBlockedArea(const rmg::Area& area)
+{
+	blockedArea = area;
+}
+
+void ObstacleProxy::clearBlockedArea()
+{
+	blockedArea.clear();
+}
+
+bool ObstacleProxy::isProhibited(const rmg::Area& objArea) const
+{
+	return false;
+};
+
+int ObstacleProxy::getWeightedObjects(const int3 & tile, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects)
+{
+	int maxWeight = std::numeric_limits<int>::min();
+	for(auto & possibleObstacle : possibleObstacles)
+	{
+		if(!possibleObstacle.first)
+			continue;
+
+		auto shuffledObstacles = possibleObstacle.second;
+		RandomGeneratorUtil::randomShuffle(shuffledObstacles, rand);
+
+		for(const auto & temp : shuffledObstacles)
+		{
+			auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid);
+			auto * obj = handler->create(temp);
+			allObjects.emplace_back(*obj);
+			rmg::Object * rmgObject = &allObjects.back();
+			for(const auto & offset : obj->getBlockedOffsets())
+			{
+				rmgObject->setPosition(tile - offset);
+
+				if(!isInTheMap(rmgObject->getPosition()))
+					continue;
+
+				if(!rmgObject->getArea().getSubarea([this](const int3 & t)
+				{
+					return !isInTheMap(t);
+				}).empty())
+					continue;
+
+				if(isProhibited(rmgObject->getArea()))
+					continue;
+
+				int coverageBlocked = 0;
+				int coveragePossible = 0;
+				//do not use area intersection in optimization purposes
+				for(const auto & t : rmgObject->getArea().getTilesVector())
+				{
+					auto coverage = verifyCoverage(t);
+					if(coverage.first)
+						++coverageBlocked;
+					if(coverage.second)
+						++coveragePossible;
+				}
+
+				int coverageOverlap = possibleObstacle.first - coverageBlocked - coveragePossible;
+				int weight = possibleObstacle.first + coverageBlocked - coverageOverlap * possibleObstacle.first;
+				assert(coverageOverlap >= 0);
+
+				if(weight > maxWeight)
+				{
+					weightedObjects.clear();
+					maxWeight = weight;
+					weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
+					if(weight > 0)
+						break;
+				}
+				else if(weight == maxWeight)
+					weightedObjects.emplace_back(rmgObject, rmgObject->getPosition());
+
+			}
+		}
+
+		if(maxWeight > 0)
+			break;
+	}
+
+	return maxWeight;
+}
+
+std::set<CGObjectInstance*> ObstacleProxy::createObstacles(CRandomGenerator & rand)
+{
+	//reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left
+	auto blockedTiles = blockedArea.getTilesVector();
+	int tilePos = 0;
+	std::set<CGObjectInstance*> objs;
+
+	while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size())
+	{
+		auto tile = blockedArea.getTilesVector()[tilePos];
+
+		std::list<rmg::Object> allObjects;
+		std::vector<std::pair<rmg::Object*, int3>> weightedObjects;
+		int maxWeight = getWeightedObjects(tile, rand, allObjects, weightedObjects);
+
+		if(weightedObjects.empty())
+		{
+			tilePos += 1;
+			continue;
+		}
+
+		auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, rand);
+		objIter->first->setPosition(objIter->second);
+		placeObject(*objIter->first, objs);
+
+		blockedArea.subtract(objIter->first->getArea());
+		tilePos = 0;
+
+		postProcess(*objIter->first);
+
+		if(maxWeight < 0)
+			logGlobal->warn("Placed obstacle with negative weight at %s", objIter->second.toString());
+
+		for(auto & o : allObjects)
+		{
+			if(&o != objIter->first)
+				o.clear();
+		}
+	}
+
+	return objs;
+}
+
+//FIXME: Only editor placer obstacles directly
+
+void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances)
+{
+	manager->insertObjects(instances); //insert as one operation - for undo purposes
+}
+
+std::pair<bool, bool> ObstacleProxy::verifyCoverage(const int3 & t) const
+{
+	return {blockedArea.contains(t), false};
+}
+
+void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
+{
+	for (auto * instance : object.instances())
+	{
+		instances.insert(&instance->object());
+	}
+}
+
+EditorObstaclePlacer::EditorObstaclePlacer(CMap* map):
+	map(map)
+{
+}
+
+bool EditorObstaclePlacer::isInTheMap(const int3& tile)
+{
+	return map->isInTheMap(tile);
+}
+
+void EditorObstaclePlacer::placeObstacles(CRandomGenerator & rand)
+{
+	finalInsertion(map->getEditManager(), createObstacles(rand));
+}
+
+VCMI_LIB_NAMESPACE_END

+ 79 - 0
lib/mapping/ObstacleProxy.h

@@ -0,0 +1,79 @@
+/*
+ * ObstacleProxy.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "StdInc.h"
+#include "../rmg/RmgObject.h"
+#include "CMapEditManager.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class CMapEditManager;
+class CGObjectInstance;
+class ObjectTemplate;
+class CRandomGenerator;
+
+class DLL_LINKAGE ObstacleProxy
+{
+    //Base class generating random obstacles for RMG and map editor
+public:
+	ObstacleProxy() = default;
+	virtual ~ObstacleProxy() = default;
+
+	void collectPossibleObstacles(TerrainId terrain);
+
+	void addBlockedTile(const int3 & tile);
+
+	void setBlockedArea(const rmg::Area & area);
+
+	void clearBlockedArea();
+
+	virtual bool isProhibited(const rmg::Area& objArea) const;
+
+	virtual std::pair<bool, bool> verifyCoverage(const int3 & t) const;
+
+	virtual void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances);
+
+	virtual std::set<CGObjectInstance*> ObstacleProxy::createObstacles(CRandomGenerator & rand);
+
+	virtual bool isInTheMap(const int3& tile) = 0;
+	
+	void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances);
+
+	void placeObstacles( CRandomGenerator & rand);
+
+	virtual void postProcess(const rmg::Object& object) {};
+
+protected:
+	int getWeightedObjects(const int3& tile, CRandomGenerator& rand, std::list<rmg::Object>& allObjects, std::vector<std::pair<rmg::Object*, int3>>& weightedObjects);
+
+	rmg::Area blockedArea;
+
+	using ObstacleVector = std::vector<std::shared_ptr<const ObjectTemplate>>;
+	std::map<int, ObstacleVector> obstaclesBySize;
+	using ObstaclePair = std::pair<int, ObstacleVector>;
+	std::vector<ObstaclePair> possibleObstacles;
+};
+
+class DLL_LINKAGE EditorObstaclePlacer : public ObstacleProxy
+{
+public:
+	EditorObstaclePlacer(CMap* map);
+
+	bool isInTheMap(const int3& tile) override;
+
+	void EditorObstaclePlacer::placeObstacles(CRandomGenerator& rand);
+
+private:
+	CMap* map;
+};
+
+VCMI_LIB_NAMESPACE_END

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

@@ -231,7 +231,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info)
 	if(sailingBoatTypes.empty())
 		return false;
 	
-	auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, generator.rand))->create());
+	auto * boat = dynamic_cast<CGBoat *>(VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(sailingBoatTypes, zone.getRand()))->create());
 
 	rmg::Object rmgObject(*boat);
 	rmgObject.setTemplate(zone.getTerrainType());