Browse Source

Rebase on develop

nordsoft 3 years ago
parent
commit
1b6f4a5cf3

+ 79 - 12
launcher/modManager/cmodlist.cpp

@@ -12,11 +12,59 @@
 
 #include "../../lib/JsonNode.h"
 #include "../../lib/filesystem/CFileInputStream.h"
+#include "../../lib/GameConstants.h"
 
-bool CModEntry::compareVersions(QString lesser, QString greater)
+const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch
+
+bool isCompatible(const QString & verMin, const QString & verMax)
 {
-	static const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch
+	QList<int> vcmiVersionList = {GameConstants::VCMI_VERSION_MAJOR,
+								  GameConstants::VCMI_VERSION_MINOR,
+								  GameConstants::VCMI_VERSION_PATCH};
+
+	if(!verMin.isEmpty())
+	{
+		QStringList verMinList = verMin.split(".");
+		assert(verMinList.size() == maxSections);
+		bool compatibleMin = true;
+		for(int i = 0; i < maxSections; i++)
+		{
+			if(verMinList[i].toInt() < vcmiVersionList[i])
+			{
+				break;
+			}
+			if(verMinList[i].toInt() > vcmiVersionList[i])
+			{
+				compatibleMin = false;
+				break;
+			}
+		}
 
+		if(!compatibleMin)
+			return false;
+	}
+
+	if(!verMax.isEmpty())
+	{
+		QStringList verMaxList = verMax.split(".");
+		assert(verMaxList.size() == maxSections);
+		for(int i = 0; i < maxSections; i++)
+		{
+			if(verMaxList[i].toInt() > vcmiVersionList[i])
+			{
+				return true;
+			}
+			if(verMaxList[i].toInt() < vcmiVersionList[i])
+			{
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+bool CModEntry::compareVersions(QString lesser, QString greater)
+{
 	QStringList lesserList = lesser.split(".");
 	QStringList greaterList = greater.split(".");
 
@@ -92,6 +140,15 @@ bool CModEntry::isUpdateable() const
 	return false;
 }
 
+bool CModEntry::isCompatible() const
+{
+	if(!isInstalled())
+		return false;
+
+	auto compatibility = localData["compatibility"].toMap();
+	return ::isCompatible(compatibility["min"].toString(), compatibility["max"].toString());
+}
+
 bool CModEntry::isEssential() const
 {
 	return getValue("storedLocaly").toBool();
@@ -102,6 +159,11 @@ bool CModEntry::isInstalled() const
 	return !localData.isEmpty();
 }
 
+bool CModEntry::isValid() const
+{
+	return !localData.isEmpty() || !repository.isEmpty();
+}
+
 int CModEntry::getModStatus() const
 {
 	int status = 0;
@@ -193,7 +255,11 @@ static QVariant getValue(QVariant input, QString path)
 		QString remainder = "/" + path.section('/', 2, -1);
 
 		entryName.remove(0, 1);
-		return getValue(input.toMap().value(entryName), remainder);
+		QMap<QString, QString> keyNormalize;
+		for(auto & key : input.toMap().keys())
+			keyNormalize[key.toLower()] = key;
+
+		return getValue(input.toMap().value(keyNormalize[entryName]), remainder);
 	}
 	else
 	{
@@ -203,6 +269,7 @@ static QVariant getValue(QVariant input, QString path)
 
 CModEntry CModList::getMod(QString modname) const
 {
+	modname = modname.toLower();
 	QVariantMap repo;
 	QVariantMap local = localModList[modname].toMap();
 	QVariantMap settings;
@@ -246,14 +313,14 @@ CModEntry CModList::getMod(QString modname) const
 		QVariant repoVal = getValue(entry, path);
 		if(repoVal.isValid())
 		{
-			if(repo.empty())
-			{
-				repo = repoVal.toMap();
-			}
-			else
+			auto repoValMap = repoVal.toMap();
+			auto compatibility = repoValMap["compatibility"].toMap();
+			if(isCompatible(compatibility["min"].toString(), compatibility["max"].toString()))
 			{
-				if(CModEntry::compareVersions(repo["version"].toString(), repoVal.toMap()["version"].toString()))
-					repo = repoVal.toMap();
+				if(repo.empty() || CModEntry::compareVersions(repo["version"].toString(), repoValMap["version"].toString()))
+				{
+					repo = repoValMap;
+				}
 			}
 		}
 	}
@@ -297,12 +364,12 @@ QVector<QString> CModList::getModList() const
 	{
 		for(auto it = repo.begin(); it != repo.end(); it++)
 		{
-			knownMods.insert(it.key());
+			knownMods.insert(it.key().toLower());
 		}
 	}
 	for(auto it = localModList.begin(); it != localModList.end(); it++)
 	{
-		knownMods.insert(it.key());
+		knownMods.insert(it.key().toLower());
 	}
 
 	for(auto entry : knownMods)

+ 4 - 0
launcher/modManager/cmodlist.h

@@ -51,6 +51,10 @@ public:
 	bool isInstalled() const;
 	// vcmi essential files
 	bool isEssential() const;
+	// checks if verison is compatible with vcmi
+	bool isCompatible() const;
+	// returns if has any data
+	bool isValid() const;
 
 	// see ModStatus enum
 	int getModStatus() const;

+ 1 - 0
launcher/modManager/cmodlistmodel_moc.cpp

@@ -245,6 +245,7 @@ bool CModFilterModel::filterMatchesThis(const QModelIndex & source) const
 {
 	CModEntry mod = base->getMod(source.data(ModRoles::ModNameRole).toString());
 	return (mod.getModStatus() & filterMask) == filteredType &&
+			mod.isValid() &&
 	       QSortFilterProxyModel::filterAcceptsRow(source.row(), source.parent());
 }
 

+ 4 - 0
launcher/modManager/cmodmanager.cpp

@@ -169,6 +169,10 @@ bool CModManager::canEnableMod(QString modname)
 	if(!mod.isInstalled())
 		return addError(modname, "Mod must be installed first");
 
+	//check for compatibility
+	if(!mod.isCompatible())
+		return addError(modname, "Mod is not compatible, please update VCMI and checkout latest mod revisions");
+
 	for(auto modEntry : mod.getValue("depends").toStringList())
 	{
 		if(!modList->hasMod(modEntry)) // required mod is not available

+ 58 - 0
lib/CModHandler.cpp

@@ -532,6 +532,51 @@ JsonNode addMeta(JsonNode config, std::string meta)
 	return config;
 }
 
+CModInfo::Version CModInfo::Version::GameVersion()
+{
+	return Version(GameConstants::VCMI_VERSION_MAJOR, GameConstants::VCMI_VERSION_MINOR, GameConstants::VCMI_VERSION_PATCH);
+}
+
+CModInfo::Version CModInfo::Version::fromString(std::string from)
+{
+	int major = 0, minor = 0, patch = 0;
+	try
+	{
+		auto pointPos = from.find('.');
+		major = std::stoi(from.substr(0, pointPos));
+		if(pointPos != std::string::npos)
+		{
+			from = from.substr(pointPos + 1);
+			pointPos = from.find('.');
+			minor = std::stoi(from.substr(0, pointPos));
+			if(pointPos != std::string::npos)
+				patch = std::stoi(from.substr(pointPos + 1));
+		}
+	}
+	catch(const std::invalid_argument & e)
+	{
+		return Version();
+	}
+	return Version(major, minor, patch);
+}
+
+std::string CModInfo::Version::toString() const
+{
+	return std::to_string(major) + '.' + std::to_string(minor) + '.' + std::to_string(patch);
+}
+
+bool CModInfo::Version::compatible(const Version & other, bool checkMinor, bool checkPatch) const
+{
+	return  (major == other.major &&
+			(!checkMinor || minor >= other.minor) &&
+			(!checkPatch || minor > other.minor || (minor == other.minor && patch >= other.patch)));
+}
+
+bool CModInfo::Version::isNull() const
+{
+	return major == 0 && minor == 0 && patch == 0;
+}
+
 CModInfo::CModInfo():
 	checksum(0),
 	enabled(false),
@@ -551,6 +596,12 @@ CModInfo::CModInfo(std::string identifier,const JsonNode & local, const JsonNode
 	validation(PENDING),
 	config(addMeta(config, identifier))
 {
+	version = Version::fromString(config["version"].String());
+	if(!config["compatibility"].isNull())
+	{
+		vcmiCompatibleMin = Version::fromString(config["compatibility"]["min"].String());
+		vcmiCompatibleMax = Version::fromString(config["compatibility"]["max"].String());
+	}
 	loadLocalData(local);
 }
 
@@ -601,6 +652,13 @@ void CModInfo::loadLocalData(const JsonNode & data)
 		validated = data["validated"].Bool();
 		checksum  = strtol(data["checksum"].String().c_str(), nullptr, 16);
 	}
+	
+	//check compatibility
+	bool wasEnabled = enabled;
+	enabled &= vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin);
+	enabled &= vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion());
+	if(wasEnabled && !enabled)
+		logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", name);
 
 	if (enabled)
 		validation = validated ? PASSED : PENDING;

+ 66 - 14
lib/CModHandler.h

@@ -177,6 +177,30 @@ public:
 		FAILED,
 		PASSED
 	};
+	
+	struct Version
+	{
+		int major = 0;
+		int minor = 0;
+		int patch = 0;
+		
+		Version() = default;
+		Version(int mj, int mi, int p): major(mj), minor(mi), patch(p) {}
+		
+		static Version GameVersion();
+		static Version fromString(std::string from);
+		std::string toString() const;
+		
+		bool compatible(const Version & other, bool checkMinor = false, bool checkPatch = false) const;
+		bool isNull() const;
+		
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & major;
+			h & minor;
+			h & patch;
+		}
+	};
 
 	/// identifier, identical to name of folder with mod
 	std::string identifier;
@@ -184,6 +208,12 @@ public:
 	/// human-readable strings
 	std::string name;
 	std::string description;
+	
+	/// version of the mod
+	Version version;
+	
+	///The  vcmi versions compatible with the mod
+	Version vcmiCompatibleMin, vcmiCompatibleMax;
 
 	/// list of mods that should be loaded before this one
 	std::set <TModID> dependencies;
@@ -210,18 +240,6 @@ public:
 	static std::string getModDir(std::string name);
 	static std::string getModFile(std::string name);
 
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & identifier;
-		h & description;
-		h & name;
-		h & dependencies;
-		h & conflicts;
-		h & config;
-		h & checksum;
-		h & validation;
-		h & enabled;
-	}
 private:
 	void loadLocalData(const JsonNode & data);
 };
@@ -256,6 +274,13 @@ class DLL_LINKAGE CModHandler
 	void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods);
 	void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods);
 public:
+	
+	class Incompatibility: public std::logic_error
+	{
+	public:
+		Incompatibility(const std::string & w): std::logic_error(w)
+		{}
+	};
 
 	CIdentifierStorage identifiers;
 
@@ -336,8 +361,35 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & allMods;
-		h & activeMods;
+		if(h.saving)
+		{
+			h & activeMods;
+			for(auto & m : activeMods)
+				h & allMods[m].version;
+		}
+		else
+		{
+			std::vector<TModID> newActiveMods;
+			h & newActiveMods;
+			for(auto & m : newActiveMods)
+			{
+				if(!allMods.count(m))
+					throw Incompatibility(m + " unkown mod");
+				
+				CModInfo::Version mver;
+				h & mver;
+				if(!allMods[m].version.isNull() && !mver.isNull() && !allMods[m].version.compatible(mver))
+				{
+					std::string err = allMods[m].name +
+					": version needed " + mver.toString() +
+					"but you have installed " + allMods[m].version.toString();
+					throw Incompatibility(err);
+				}
+				allMods[m].enabled = true;
+			}
+			std::swap(activeMods, newActiveMods);
+		}
+				
 		h & settings;
 		h & modules;
 		h & identifiers;

+ 10 - 2
lib/GameConstants.cpp

@@ -51,10 +51,18 @@ const TeamID TeamID::NO_TEAM = TeamID(255);
 
 namespace GameConstants
 {
+	const int VCMI_VERSION_MAJOR = 1;
+	const int VCMI_VERSION_MINOR = 0;
+	const int VCMI_VERSION_PATCH = 0;
+
+	const std::string VCMI_VERSION_STRING = std::to_string(VCMI_VERSION_MAJOR) + "." +
+											std::to_string(VCMI_VERSION_MINOR) + "." +
+											std::to_string(VCMI_VERSION_PATCH);
+
 #ifdef VCMI_NO_EXTRA_VERSION
-	const std::string VCMI_VERSION = std::string("VCMI 1.0.0");
+	const std::string VCMI_VERSION = std::string("VCMI ") + VCMI_VERSION_STRING;
 #else
-	const std::string VCMI_VERSION = std::string("VCMI 1.0.0.") + GIT_SHA1;
+	const std::string VCMI_VERSION = std::string("VCMI ") + VCMI_VERSION_STRING + "." + GIT_SHA1;
 #endif
 }
 

+ 3 - 0
lib/GameConstants.h

@@ -36,6 +36,9 @@ struct IdTag
 
 namespace GameConstants
 {
+	DLL_LINKAGE extern const int VCMI_VERSION_MAJOR;
+	DLL_LINKAGE extern const int VCMI_VERSION_MINOR;
+	DLL_LINKAGE extern const int VCMI_VERSION_PATCH;
 	DLL_LINKAGE extern const std::string VCMI_VERSION;
 
 	const int PUZZLE_MAP_PIECES = 48;

+ 0 - 3
lib/VCMI_Lib.h

@@ -131,9 +131,6 @@ public:
 			callWhenDeserializing();
 		}
 	}
-
-private:
-	void update800();
 };
 
 extern DLL_LINKAGE LibClasses * VLC;

+ 187 - 107
lib/rmg/ObstaclePlacer.cpp

@@ -22,22 +22,10 @@
 #include "CMapGenerator.h"
 #include "../CRandomGenerator.h"
 #include "Functions.h"
+#include "../mapping/CMapEditManager.h"
 
-void ObstaclePlacer::process()
+void ObstacleProxy::collectPossibleObstacles(const Terrain & terrain)
 {
-	auto * manager = zone.getModificator<ObjectManager>();
-	if(!manager)
-		return;
-	
-	auto * riverManager = zone.getModificator<RiverPlacer>();
-	
-	typedef std::vector<std::shared_ptr<const ObjectTemplate>> ObstacleVector;
-	//obstacleVector possibleObstacles;
-	
-	std::map<int, ObstacleVector> obstaclesBySize;
-	typedef std::pair<int, ObstacleVector> ObstaclePair;
-	std::vector<ObstaclePair> possibleObstacles;
-	
 	//get all possible obstacles for this terrain
 	for(auto primaryID : VLC->objtypeh->knownObjects())
 	{
@@ -48,7 +36,7 @@ void ObstaclePlacer::process()
 			{
 				for(auto temp : handler->getTemplates())
 				{
-					if(temp->canBePlacedAt(zone.getTerrainType()) && temp->getBlockMapOffset().valid())
+					if(temp->canBePlacedAt(terrain) && temp->getBlockMapOffset().valid())
 						obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp);
 				}
 			}
@@ -62,122 +50,172 @@ void ObstaclePlacer::process()
 	{
 		return p1.first > p2.first; //bigger obstacles first
 	});
-	
-	auto blockedArea = zone.area().getSubarea([this](const int3 & t)
+}
+
+int ObstacleProxy::getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects)
+{
+	int maxWeight = std::numeric_limits<int>::min();
+	for(int i = 0; i < possibleObstacles.size(); ++i)
 	{
-		return map.shouldBeBlocked(t);
-	});
-	blockedArea.subtract(zone.areaUsed());
-	zone.areaPossible().subtract(blockedArea);
-	
-	
-	auto prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
-	
+		if(!possibleObstacles[i].first)
+			continue;
+
+		auto shuffledObstacles = possibleObstacles[i].second;
+		RandomGeneratorUtil::randomShuffle(shuffledObstacles, rand);
+
+		for(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(auto & offset : obj->getBlockedOffsets())
+			{
+				rmgObject->setPosition(tile - offset);
+				if(!map->isInTheMap(rmgObject->getPosition()))
+					continue;
+
+				if(!rmgObject->getArea().getSubarea([map](const int3 & t)
+				{
+					return !map->isInTheMap(t);
+				}).empty())
+					continue;
+
+				if(isProhibited(rmgObject->getArea()))
+					continue;
+
+				int coverageBlocked = 0;
+				int coveragePossible = 0;
+				//do not use area intersection in optimization purposes
+				for(auto & t : rmgObject->getArea().getTilesVector())
+				{
+					auto coverage = verifyCoverage(t);
+					if(coverage.first)
+						++coverageBlocked;
+					if(coverage.second)
+						++coveragePossible;
+				}
+
+				int coverageOverlap = possibleObstacles[i].first - coverageBlocked - coveragePossible;
+				int weight = possibleObstacles[i].first + coverageBlocked - coverageOverlap * possibleObstacles[i].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;
+}
+
+void ObstacleProxy::placeObstacles(CMap * map, 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; //obj + position
-		int maxWeight = std::numeric_limits<int>::min();
-		for(int i = 0; i < possibleObstacles.size(); ++i)
-		{
-			if(!possibleObstacles[i].first)
-				continue;
-			
-			auto shuffledObstacles = possibleObstacles[i].second;
-			RandomGeneratorUtil::randomShuffle(shuffledObstacles, generator.rand);
-			
-			for(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(auto & offset : obj->getBlockedOffsets())
-				{
-					rmgObject->setPosition(tile - offset);
-					if(!map.isOnMap(rmgObject->getPosition()))
-						continue;
-					
-					if(!rmgObject->getArea().getSubarea([this](const int3 & t)
-					{
-						return !map.isOnMap(t);
-					}).empty())
-						continue;
-					
-					if(prohibitedArea.overlap(rmgObject->getArea()))
-						continue;
-					
-					if(!zone.area().contains(rmgObject->getArea()))
-						continue;
-					
-					int coverageBlocked = 0;
-					int coveragePossible = 0;
-					//do not use area intersection in optimization purposes
-					for(auto & t : rmgObject->getArea().getTilesVector())
-					{
-						if(map.shouldBeBlocked(t))
-							++coverageBlocked;
-						if(zone.areaPossible().contains(t))
-							++coveragePossible;
-					}
-					
-					int coverageOverlap = possibleObstacles[i].first - coverageBlocked - coveragePossible;
-					int weight = possibleObstacles[i].first + coverageBlocked - coverageOverlap * possibleObstacles[i].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;
-		}
-		
+		std::vector<std::pair<rmg::Object*, int3>> weightedObjects;
+		int maxWeight = getWeightedObjects(tile, map, rand, allObjects, weightedObjects);
+
 		if(weightedObjects.empty())
 		{
 			tilePos += 1;
 			continue;
 		}
-		
-		auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, generator.rand);
+
+		auto objIter = RandomGeneratorUtil::nextItem(weightedObjects, rand);
 		objIter->first->setPosition(objIter->second);
-		manager->placeObject(*objIter->first, false, false);
+		placeObject(*objIter->first, objs);
+
 		blockedArea.subtract(objIter->first->getArea());
 		tilePos = 0;
-		
-		//river processing
-		if(riverManager)
-		{
-			if(objIter->first->instances().front()->object().typeName == "mountain")
-				riverManager->riverSource().unite(objIter->first->getArea());
-			if(objIter->first->instances().front()->object().typeName == "lake")
-				riverManager->riverSink().unite(objIter->first->getArea());
-		}
-		
+
+		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();
 		}
 	}
+
+	finalInsertion(map->getEditManager(), objs);
+}
+
+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
+{
+	std::pair<bool, bool> result(false, false);
+	if(blockedArea.contains(t))
+		result.first = true;
+	return result;
+}
+
+void ObstacleProxy::placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances)
+{
+	for (auto * instance : object.instances())
+	{
+		instances.insert(&instance->object());
+	}
+}
+
+void ObstacleProxy::postProcess(const rmg::Object & object)
+{
+}
+
+bool ObstacleProxy::isProhibited(const rmg::Area & objArea) const
+{
+	return false;
+}
+
+
+
+void ObstaclePlacer::process()
+{
+	manager = zone.getModificator<ObjectManager>();
+	if(!manager)
+		return;
+	
+	riverManager = zone.getModificator<RiverPlacer>();
+	
+	collectPossibleObstacles(zone.getTerrainType());
+	
+	blockedArea = zone.area().getSubarea([this](const int3 & t)
+	{
+		return map.shouldBeBlocked(t);
+	});
+	blockedArea.subtract(zone.areaUsed());
+	zone.areaPossible().subtract(blockedArea);
+	
+	prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea();
+		
+	placeObstacles(&map.map(), generator.rand);
 }
 
 void ObstaclePlacer::init()
@@ -189,3 +227,45 @@ void ObstaclePlacer::init()
 	DEPENDENCY(RoadPlacer);
 	DEPENDENCY_ALL(RockPlacer);
 }
+
+std::pair<bool, bool> ObstaclePlacer::verifyCoverage(const int3 & t) const
+{
+	std::pair<bool, bool> result(false, false);
+	if(map.shouldBeBlocked(t))
+		result.first = true;
+	if(zone.areaPossible().contains(t))
+		result.second = true;
+	return result;
+}
+
+void ObstaclePlacer::placeObject(rmg::Object & object, std::set<CGObjectInstance*> &)
+{
+	manager->placeObject(object, false, false);
+}
+
+void ObstaclePlacer::postProcess(const rmg::Object & object)
+{
+	//river processing
+	if(riverManager)
+	{
+		if(object.instances().front()->object().typeName == "mountain")
+			riverManager->riverSource().unite(object.getArea());
+		if(object.instances().front()->object().typeName == "lake")
+			riverManager->riverSink().unite(object.getArea());
+	}
+}
+
+bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
+{
+	if(prohibitedArea.overlap(objArea))
+		return true;
+	 
+	if(!zone.area().contains(objArea))
+		return true;
+	
+	return false;
+}
+
+void ObstaclePlacer::finalInsertion(CMapEditManager *, std::set<CGObjectInstance*> &)
+{
+}

+ 51 - 1
lib/rmg/ObstaclePlacer.h

@@ -11,11 +11,61 @@
 #pragma once
 #include "Zone.h"
 
-class ObstaclePlacer: public Modificator
+class CMap;
+class CMapEditManager;
+class RiverPlacer;
+class ObjectManager;
+class DLL_LINKAGE ObstacleProxy
+{
+public:
+	ObstacleProxy() = default;
+	virtual ~ObstacleProxy() = default;
+
+	rmg::Area blockedArea;
+
+	void collectPossibleObstacles(const Terrain & terrain);
+
+	void placeObstacles(CMap * map, CRandomGenerator & rand);
+
+	virtual std::pair<bool, bool> verifyCoverage(const int3 & t) const;
+
+	virtual void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances);
+
+	virtual void postProcess(const rmg::Object & object);
+
+	virtual bool isProhibited(const rmg::Area & objArea) const;
+	
+	virtual void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances);
+
+protected:
+	int getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list<rmg::Object> & allObjects, std::vector<std::pair<rmg::Object*, int3>> & weightedObjects);
+
+	typedef std::vector<std::shared_ptr<const ObjectTemplate>> ObstacleVector;
+	std::map<int, ObstacleVector> obstaclesBySize;
+	typedef std::pair<int, ObstacleVector> ObstaclePair;
+	std::vector<ObstaclePair> possibleObstacles;
+};
+
+class ObstaclePlacer: public Modificator, public ObstacleProxy
 {
 public:
 	MODIFICATOR(ObstaclePlacer);
 	
 	void process() override;
 	void init() override;
+	
+	std::pair<bool, bool> verifyCoverage(const int3 & t) const override;
+	
+	void placeObject(rmg::Object & object, std::set<CGObjectInstance*> & instances) override;
+	
+	void postProcess(const rmg::Object & object) override;
+	
+	bool isProhibited(const rmg::Area & objArea) const override;
+	
+	void finalInsertion(CMapEditManager * manager, std::set<CGObjectInstance*> & instances) override;
+	
+private:
+	rmg::Area prohibitedArea;
+	RiverPlacer * riverManager;
+	ObjectManager * manager;
 };