From 1b6f4a5cf32f787aa7bd6fc1d331d80be99c0cd3 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 17 Sep 2022 17:43:59 +0400 Subject: [PATCH 1/4] Rebase on develop --- launcher/modManager/cmodlist.cpp | 91 ++++++- launcher/modManager/cmodlist.h | 4 + launcher/modManager/cmodlistmodel_moc.cpp | 1 + launcher/modManager/cmodmanager.cpp | 4 + lib/CModHandler.cpp | 58 +++++ lib/CModHandler.h | 80 ++++-- lib/GameConstants.cpp | 12 +- lib/GameConstants.h | 3 + lib/VCMI_Lib.h | 3 - lib/rmg/ObstaclePlacer.cpp | 294 ++++++++++++++-------- lib/rmg/ObstaclePlacer.h | 52 +++- 11 files changed, 463 insertions(+), 139 deletions(-) diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index c0d617442..3cca86d1e 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -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 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 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 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) diff --git a/launcher/modManager/cmodlist.h b/launcher/modManager/cmodlist.h index a5de09622..2bce79c6d 100644 --- a/launcher/modManager/cmodlist.h +++ b/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; diff --git a/launcher/modManager/cmodlistmodel_moc.cpp b/launcher/modManager/cmodlistmodel_moc.cpp index 2e94b8f81..fb2d3f1e0 100644 --- a/launcher/modManager/cmodlistmodel_moc.cpp +++ b/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()); } diff --git a/launcher/modManager/cmodmanager.cpp b/launcher/modManager/cmodmanager.cpp index 3bc854bb3..eea55af17 100644 --- a/launcher/modManager/cmodmanager.cpp +++ b/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 diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 1c30b1208..485a8304b 100644 --- a/lib/CModHandler.cpp +++ b/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; diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 5df3dfc38..03028dd5f 100644 --- a/lib/CModHandler.h +++ b/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 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 dependencies; @@ -210,18 +240,6 @@ public: static std::string getModDir(std::string name); static std::string getModFile(std::string name); - template 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 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 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; diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index ae543bcf5..ec368869e 100644 --- a/lib/GameConstants.cpp +++ b/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 } diff --git a/lib/GameConstants.h b/lib/GameConstants.h index d86c760ce..54d098e12 100644 --- a/lib/GameConstants.h +++ b/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; diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index 774db316d..f0c60eae4 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -131,9 +131,6 @@ public: callWhenDeserializing(); } } - -private: - void update800(); }; extern DLL_LINKAGE LibClasses * VLC; diff --git a/lib/rmg/ObstaclePlacer.cpp b/lib/rmg/ObstaclePlacer.cpp index a0c051fdb..1e7874075 100644 --- a/lib/rmg/ObstaclePlacer.cpp +++ b/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(); - if(!manager) - return; - - auto * riverManager = zone.getModificator(); - - typedef std::vector> ObstacleVector; - //obstacleVector possibleObstacles; - - std::map obstaclesBySize; - typedef std::pair ObstaclePair; - std::vector 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 & allObjects, std::vector> & weightedObjects) +{ + int maxWeight = std::numeric_limits::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 objs; + while(!blockedArea.empty() && tilePos < blockedArea.getTilesVector().size()) { auto tile = blockedArea.getTilesVector()[tilePos]; - + std::list allObjects; - std::vector> weightedObjects; //obj + position - int maxWeight = std::numeric_limits::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> 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 & instances) +{ + manager->insertObjects(instances); //insert as one operation - for undo purposes +} + +std::pair ObstacleProxy::verifyCoverage(const int3 & t) const +{ + std::pair result(false, false); + if(blockedArea.contains(t)) + result.first = true; + return result; +} + +void ObstacleProxy::placeObject(rmg::Object & object, std::set & 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(); + if(!manager) + return; + + riverManager = zone.getModificator(); + + 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 ObstaclePlacer::verifyCoverage(const int3 & t) const +{ + std::pair 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 &) +{ + 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 &) +{ +} diff --git a/lib/rmg/ObstaclePlacer.h b/lib/rmg/ObstaclePlacer.h index cb1242048..b98d042aa 100644 --- a/lib/rmg/ObstaclePlacer.h +++ b/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 verifyCoverage(const int3 & t) const; + + virtual void placeObject(rmg::Object & object, std::set & instances); + + virtual void postProcess(const rmg::Object & object); + + virtual bool isProhibited(const rmg::Area & objArea) const; + + virtual void finalInsertion(CMapEditManager * manager, std::set & instances); + +protected: + int getWeightedObjects(const int3 & tile, const CMap * map, CRandomGenerator & rand, std::list & allObjects, std::vector> & weightedObjects); + + typedef std::vector> ObstacleVector; + std::map obstaclesBySize; + typedef std::pair ObstaclePair; + std::vector possibleObstacles; +}; + +class ObstaclePlacer: public Modificator, public ObstacleProxy { public: MODIFICATOR(ObstaclePlacer); void process() override; void init() override; + + std::pair verifyCoverage(const int3 & t) const override; + + void placeObject(rmg::Object & object, std::set & instances) override; + + void postProcess(const rmg::Object & object) override; + + bool isProhibited(const rmg::Area & objArea) const override; + + void finalInsertion(CMapEditManager * manager, std::set & instances) override; + +private: + rmg::Area prohibitedArea; + RiverPlacer * riverManager; + ObjectManager * manager; }; From 60264aae2985442acd4cee0865f37b816873625e Mon Sep 17 00:00:00 2001 From: Nordsoft91 Date: Mon, 19 Sep 2022 01:18:17 +0400 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Andrey Filipenkov --- lib/CModHandler.cpp | 5 +++-- lib/CModHandler.h | 6 ++++-- lib/rmg/ObstaclePlacer.cpp | 17 +++++------------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 485a8304b..a652d03ef 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -655,8 +655,9 @@ void CModInfo::loadLocalData(const JsonNode & data) //check compatibility bool wasEnabled = enabled; - enabled &= vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin); - enabled &= vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion()); + enabled = enabled && (vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin)); + enabled = 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); diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 03028dd5f..de0f4144d 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -212,7 +212,8 @@ public: /// version of the mod Version version; - ///The vcmi versions compatible with the mod + /// vcmi versions compatible with the mod + Version vcmiCompatibleMin, vcmiCompatibleMax; /// list of mods that should be loaded before this one @@ -364,7 +365,8 @@ public: if(h.saving) { h & activeMods; - for(auto & m : activeMods) + for(const auto & m : activeMods) + h & allMods[m].version; } else diff --git a/lib/rmg/ObstaclePlacer.cpp b/lib/rmg/ObstaclePlacer.cpp index 1e7874075..29fba34ab 100644 --- a/lib/rmg/ObstaclePlacer.cpp +++ b/lib/rmg/ObstaclePlacer.cpp @@ -171,10 +171,7 @@ void ObstacleProxy::finalInsertion(CMapEditManager * manager, std::set ObstacleProxy::verifyCoverage(const int3 & t) const { - std::pair result(false, false); - if(blockedArea.contains(t)) - result.first = true; - return result; + return {blockedArea.contains(t), false}; } void ObstacleProxy::placeObject(rmg::Object & object, std::set & instances) @@ -230,12 +227,7 @@ void ObstaclePlacer::init() std::pair ObstaclePlacer::verifyCoverage(const int3 & t) const { - std::pair result(false, false); - if(map.shouldBeBlocked(t)) - result.first = true; - if(zone.areaPossible().contains(t)) - result.second = true; - return result; + return {map.shouldBeBlocked(t), zone.areaPossible().contains(t)}; } void ObstaclePlacer::placeObject(rmg::Object & object, std::set &) @@ -248,9 +240,10 @@ void ObstaclePlacer::postProcess(const rmg::Object & object) //river processing if(riverManager) { - if(object.instances().front()->object().typeName == "mountain") + const auto objTypeName = object.instances().front()->object().typeName; + if(objTypeName == "mountain") riverManager->riverSource().unite(object.getArea()); - if(object.instances().front()->object().typeName == "lake") + else if(objTypeName == "lake") riverManager->riverSink().unite(object.getArea()); } } From dcfafac2665769993d04d412a8b8ce98dbf7dc37 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 19 Sep 2022 03:52:08 +0400 Subject: [PATCH 3/4] Reuse QVersionNumber --- launcher/modManager/cmodlist.cpp | 84 ++++++++++++-------------------- 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index 3cca86d1e..27ee144f2 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -14,75 +14,51 @@ #include "../../lib/filesystem/CFileInputStream.h" #include "../../lib/GameConstants.h" -const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch - +namespace +{ bool isCompatible(const QString & verMin, const QString & verMax) { - QList vcmiVersionList = {GameConstants::VCMI_VERSION_MAJOR, - GameConstants::VCMI_VERSION_MINOR, - GameConstants::VCMI_VERSION_PATCH}; - - if(!verMin.isEmpty()) + const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch + QVersionNumber vcmiVersion(GameConstants::VCMI_VERSION_MAJOR, + GameConstants::VCMI_VERSION_MINOR, + GameConstants::VCMI_VERSION_PATCH); + + auto versionMin = QVersionNumber::fromString(verMin); + auto versionMax = QVersionNumber::fromString(verMax); + + auto buildVersion = [](QVersionNumber & ver) { - QStringList verMinList = verMin.split("."); - assert(verMinList.size() == maxSections); - bool compatibleMin = true; - for(int i = 0; i < maxSections; i++) + if(ver.segmentCount() < maxSections) { - if(verMinList[i].toInt() < vcmiVersionList[i]) - { - break; - } - if(verMinList[i].toInt() > vcmiVersionList[i]) - { - compatibleMin = false; - break; - } + auto segments = ver.segments(); + for(int i = segments.size() - 1; i < maxSections; ++i) + segments.append(0); + ver = QVersionNumber(segments); } + }; - if(!compatibleMin) + if(!versionMin.isNull()) + { + buildVersion(versionMin); + if(vcmiVersion < versionMin) return false; } - - if(!verMax.isEmpty()) + + if(!versionMax.isNull()) { - 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; - } - } + buildVersion(versionMax); + if(vcmiVersion > versionMax) + return false; } return true; } +} bool CModEntry::compareVersions(QString lesser, QString greater) { - QStringList lesserList = lesser.split("."); - QStringList greaterList = greater.split("."); - - assert(lesserList.size() <= maxSections); - assert(greaterList.size() <= maxSections); - - for(int i = 0; i < maxSections; i++) - { - if(greaterList.size() <= i) // 1.1.1 > 1.1 - return false; - - if(lesserList.size() <= i) // 1.1 < 1.1.1 - return true; - - if(lesserList[i].toInt() != greaterList[i].toInt()) - return lesserList[i].toInt() < greaterList[i].toInt(); // 1.1 < 1.2 - } - return false; + auto versionLesser = QVersionNumber::fromString(lesser); + auto versionGreater = QVersionNumber::fromString(greater); + return versionLesser < versionGreater; } QString CModEntry::sizeToString(double size) From fd10d8a88721a6d96248e48d244ffd71c49f04d0 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 19 Sep 2022 12:34:18 +0400 Subject: [PATCH 4/4] Fix compilation on msvc --- launcher/modManager/cmodlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index 27ee144f2..65ad05988 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -26,7 +26,7 @@ bool isCompatible(const QString & verMin, const QString & verMax) auto versionMin = QVersionNumber::fromString(verMin); auto versionMax = QVersionNumber::fromString(verMax); - auto buildVersion = [](QVersionNumber & ver) + auto buildVersion = [maxSections](QVersionNumber & ver) { if(ver.segmentCount() < maxSections) {