mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Rebase on develop
This commit is contained in:
		| @@ -12,11 +12,59 @@ | ||||
|  | ||||
| #include "../../lib/JsonNode.h" | ||||
| #include "../../lib/filesystem/CFileInputStream.h" | ||||
| #include "../../lib/GameConstants.h" | ||||
|  | ||||
| const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch | ||||
|  | ||||
| bool isCompatible(const QString & verMin, const QString & verMax) | ||||
| { | ||||
| 	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) | ||||
| { | ||||
| 	static const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch | ||||
|  | ||||
| 	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()) | ||||
| 			auto repoValMap = repoVal.toMap(); | ||||
| 			auto compatibility = repoValMap["compatibility"].toMap(); | ||||
| 			if(isCompatible(compatibility["min"].toString(), compatibility["max"].toString())) | ||||
| 			{ | ||||
| 				repo = repoVal.toMap(); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				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) | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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()); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
| @@ -602,6 +653,13 @@ void CModInfo::loadLocalData(const JsonNode & data) | ||||
| 		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; | ||||
| 	else | ||||
|   | ||||
| @@ -178,6 +178,30 @@ public: | ||||
| 		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; | ||||
|  | ||||
| @@ -185,6 +209,12 @@ public: | ||||
| 	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); | ||||
| }; | ||||
| @@ -257,6 +275,13 @@ class DLL_LINKAGE CModHandler | ||||
| 	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; | ||||
|  | ||||
| 	std::shared_ptr<CContentHandler> content; //(!)Do not serialize | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -131,9 +131,6 @@ public: | ||||
| 			callWhenDeserializing(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	void update800(); | ||||
| }; | ||||
|  | ||||
| extern DLL_LINKAGE LibClasses * VLC; | ||||
|   | ||||
| @@ -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,91 +50,91 @@ 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); | ||||
| 		if(!possibleObstacles[i].first) | ||||
| 			continue; | ||||
|  | ||||
| 		auto shuffledObstacles = possibleObstacles[i].second; | ||||
| 		RandomGeneratorUtil::randomShuffle(shuffledObstacles, rand); | ||||
|  | ||||
| 	auto prohibitedArea = zone.freePaths() + zone.areaUsed() + manager->getVisitableArea(); | ||||
| 		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()) | ||||
| 		{ | ||||
| @@ -154,20 +142,14 @@ void ObstaclePlacer::process() | ||||
| 			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()); | ||||
| @@ -178,6 +160,62 @@ void ObstaclePlacer::process() | ||||
| 				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*> &) | ||||
| { | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user