From 1b6f4a5cf32f787aa7bd6fc1d331d80be99c0cd3 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Sat, 17 Sep 2022 17:43:59 +0400 Subject: [PATCH 01/37] 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 a0832c39f9b35d6f088443711936a3a32577a9e3 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 17 Sep 2022 15:41:56 +0300 Subject: [PATCH 02/37] show message instead of the Quick Recruit dialog when there're no creatures in town --- client/windows/CCastleInterface.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index c37c8e2af..0ce1f55d9 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -842,7 +842,12 @@ void CCastleBuildings::enterDwelling(int level) void CCastleBuildings::enterToTheQuickRecruitmentWindow() { - GH.pushIntT(town, pos); + const auto hasSomeoneToRecruit = std::any_of(town->creatures.cbegin(), town->creatures.cend(), + [](const auto & creatureInfo) { return creatureInfo.first > 0; }); + if(hasSomeoneToRecruit) + GH.pushIntT(town, pos); + else + CInfoWindow::showInfoDialog("There are no creatures to recruit", {}); } void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID::EBuildingID upgrades) From 90d355dd05c27c8f0bbacc673e614af7574c7bf5 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sun, 18 Sep 2022 12:38:50 +0300 Subject: [PATCH 03/37] check availability only of town creatures QRD shows only town creatures ignoring Summoning Portal --- client/windows/CCastleInterface.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 0ce1f55d9..a1797f35e 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -842,7 +842,11 @@ void CCastleBuildings::enterDwelling(int level) void CCastleBuildings::enterToTheQuickRecruitmentWindow() { - const auto hasSomeoneToRecruit = std::any_of(town->creatures.cbegin(), town->creatures.cend(), + const auto beginIt = town->creatures.cbegin(); + const auto afterLastIt = town->creatures.size() > GameConstants::CREATURES_PER_TOWN + ? std::next(beginIt, GameConstants::CREATURES_PER_TOWN) + : town->creatures.cend(); + const auto hasSomeoneToRecruit = std::any_of(beginIt, afterLastIt, [](const auto & creatureInfo) { return creatureInfo.first > 0; }); if(hasSomeoneToRecruit) GH.pushIntT(town, pos); From 0163190ac5716294abdb7116bd3f18e75bbc93c9 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sun, 18 Sep 2022 15:39:08 +0300 Subject: [PATCH 04/37] move hardcoded strings to JSON file --- client/windows/CAdvmapInterface.cpp | 6 +++--- client/windows/CCastleInterface.cpp | 2 +- client/windows/CSpellWindow.cpp | 2 +- config/translate.json | 5 +++++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index fbf5e9c76..0574ee6b0 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -1217,7 +1217,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key) if(itr != LOCPLINT->towns.end()) LOCPLINT->showThievesGuildWindow(*itr); else - LOCPLINT->showInfoDialog("No available town with tavern!"); + LOCPLINT->showInfoDialog(CGI->generaltexth->localizedTexts["adventureMap"]["noTownWithTavern"].String()); } return; case SDLK_i: @@ -1249,7 +1249,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key) case SDLK_r: if(isActive() && LOCPLINT->ctrlPressed()) { - LOCPLINT->showYesNoDialog("Are you sure you want to restart game?", + LOCPLINT->showYesNoDialog(CGI->generaltexth->localizedTexts["adventureMap"]["confirmRestartGame"].String(), [](){ LOCPLINT->sendCustomEvent(EUserEvent::RESTART_GAME); }, nullptr); } return; @@ -1308,7 +1308,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key) if(townWithMarket) //if any town has marketplace, open window GH.pushIntT(townWithMarket); else //if not - complain - LOCPLINT->showInfoDialog("No available marketplace!"); + LOCPLINT->showInfoDialog(CGI->generaltexth->localizedTexts["adventureMap"]["noTownWithMarket"].String()); } else if(isActive()) //no ctrl, advmapint is on the top => switch to town { diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index a1797f35e..4de14e4b0 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -851,7 +851,7 @@ void CCastleBuildings::enterToTheQuickRecruitmentWindow() if(hasSomeoneToRecruit) GH.pushIntT(town, pos); else - CInfoWindow::showInfoDialog("There are no creatures to recruit", {}); + CInfoWindow::showInfoDialog(CGI->generaltexth->localizedTexts["townHall"]["noCreaturesToRecruit"].String(), {}); } void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID::EBuildingID upgrades) diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index 32a68dd07..ad48699e2 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -562,7 +562,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState) if(!texts.empty()) owner->myInt->showInfoDialog(texts.front()); else - owner->myInt->showInfoDialog("Unknown problem with this spell, no more information available."); + owner->myInt->showInfoDialog(CGI->generaltexth->localizedTexts["adventureMap"]["spellUnknownProblem"].String()); } } else //adventure spell diff --git a/config/translate.json b/config/translate.json index 0071d4971..2deb170aa 100644 --- a/config/translate.json +++ b/config/translate.json @@ -21,6 +21,10 @@ "Impossible" ] }, + "confirmRestartGame" : "Are you sure you want to restart game?", + "noTownWithMarket": "No available marketplace!", + "noTownWithTavern": "No available town with tavern!", + "spellUnknownProblem": "Unknown problem with this spell, no more information available.", "playerAttacked" : "Player has been attacked: %s" }, "systemOptions" : @@ -44,6 +48,7 @@ "townHall" : { "missingBase" : "Base building %s must be built first", + "noCreaturesToRecruit" : "There are no creatures to recruit!", "greetingManaVortex" : "As you near the %s your body is filled with new energy. You have doubled your normal spell points.", "greetingKnowledge" : "You study the glyphs on the %s and gain insight into the workings of various magics (+1 Knowledge).", "greetingSpellPower" : "The %s teaches you new ways to focus your magical powers (+1 Power).", From a320af837daefb0423b7ce5395ae361ae7aa3011 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sun, 18 Sep 2022 15:47:49 +0300 Subject: [PATCH 05/37] fix includes in client subdirectories --- client/gui/CAnimation.cpp | 5 +++-- client/mainmenu/CCampaignScreen.cpp | 3 ++- client/mainmenu/CCampaignScreen.h | 2 ++ client/mainmenu/CreditsScreen.cpp | 5 +++-- client/widgets/AdventureMapClasses.cpp | 3 +-- client/widgets/CComponent.cpp | 5 +++-- client/widgets/CGarrisonInt.cpp | 5 +++-- client/windows/CAdvmapInterface.cpp | 4 ++-- client/windows/CCastleInterface.cpp | 2 +- client/windows/CKingdomInterface.cpp | 2 +- client/windows/CWindowObject.cpp | 5 +++-- client/windows/GUIClasses.cpp | 2 +- client/windows/GUIClasses.h | 2 +- client/windows/InfoWindows.cpp | 3 ++- lib/filesystem/Filesystem.cpp | 2 +- 15 files changed, 29 insertions(+), 21 deletions(-) diff --git a/client/gui/CAnimation.cpp b/client/gui/CAnimation.cpp index 3828056fe..be4c357a2 100644 --- a/client/gui/CAnimation.cpp +++ b/client/gui/CAnimation.cpp @@ -10,10 +10,11 @@ #include "StdInc.h" #include "CAnimation.h" +#include "SDL_Extensions.h" +#include "SDL_Pixels.h" + #include "../CBitmapHandler.h" #include "../Graphics.h" -#include "../gui/SDL_Extensions.h" -#include "../gui/SDL_Pixels.h" #include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/ISimpleResourceLoader.h" diff --git a/client/mainmenu/CCampaignScreen.cpp b/client/mainmenu/CCampaignScreen.cpp index ea79cd775..2fb189ced 100644 --- a/client/mainmenu/CCampaignScreen.cpp +++ b/client/mainmenu/CCampaignScreen.cpp @@ -9,9 +9,10 @@ */ #include "StdInc.h" -#include "../mainmenu/CMainMenu.h" #include "CCampaignScreen.h" +#include "CMainMenu.h" + #include "../CGameInfo.h" #include "../CMessage.h" #include "../CBitmapHandler.h" diff --git a/client/mainmenu/CCampaignScreen.h b/client/mainmenu/CCampaignScreen.h index d990f51bd..b0a1acb85 100644 --- a/client/mainmenu/CCampaignScreen.h +++ b/client/mainmenu/CCampaignScreen.h @@ -9,6 +9,8 @@ */ #pragma once +#include "../windows/CWindowObject.h" + class CLabel; class CPicture; class CButton; diff --git a/client/mainmenu/CreditsScreen.cpp b/client/mainmenu/CreditsScreen.cpp index beda44054..9e754668b 100644 --- a/client/mainmenu/CreditsScreen.cpp +++ b/client/mainmenu/CreditsScreen.cpp @@ -9,9 +9,10 @@ */ #include "StdInc.h" - #include "CreditsScreen.h" -#include "../mainmenu/CMainMenu.h" + +#include "CMainMenu.h" + #include "../gui/CGuiHandler.h" #include "../widgets/TextControls.h" #include "../widgets/ObjectLists.h" diff --git a/client/widgets/AdventureMapClasses.cpp b/client/widgets/AdventureMapClasses.cpp index eac733371..4aead3f7a 100644 --- a/client/widgets/AdventureMapClasses.cpp +++ b/client/widgets/AdventureMapClasses.cpp @@ -14,6 +14,7 @@ #include "MiscWidgets.h" #include "CComponent.h" +#include "Images.h" #include "../CGameInfo.h" #include "../CMusicHandler.h" @@ -26,8 +27,6 @@ #include "../gui/SDL_Pixels.h" #include "../gui/SDL_Compat.h" -#include "../widgets/Images.h" - #include "../windows/InfoWindows.h" #include "../windows/CAdvmapInterface.h" #include "../windows/GUIClasses.h" diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index e5850d6ca..48b7af3b4 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -10,6 +10,9 @@ #include "StdInc.h" #include "CComponent.h" +#include "CArtifactHolder.h" +#include "Images.h" + #include #include @@ -18,8 +21,6 @@ #include "../CMessage.h" #include "../CGameInfo.h" -#include "../widgets/Images.h" -#include "../widgets/CArtifactHolder.h" #include "../windows/CAdvmapInterface.h" #include "../../lib/CArtHandler.h" diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index 2078429b8..0012f7b17 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -10,12 +10,13 @@ #include "StdInc.h" #include "CGarrisonInt.h" +#include "Buttons.h" +#include "TextControls.h" + #include "../gui/CGuiHandler.h" #include "../CGameInfo.h" #include "../CPlayerInterface.h" -#include "../widgets/Buttons.h" -#include "../widgets/TextControls.h" #include "../windows/CCreatureWindow.h" #include "../windows/GUIClasses.h" diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 0574ee6b0..59c104719 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -14,8 +14,9 @@ #include "CHeroWindow.h" #include "CKingdomInterface.h" #include "CSpellWindow.h" -#include "GUIClasses.h" #include "CTradeWindow.h" +#include "GUIClasses.h" +#include "InfoWindows.h" #include "../CBitmapHandler.h" #include "../CGameInfo.h" @@ -35,7 +36,6 @@ #include "../gui/CGuiHandler.h" #include "../gui/SDL_Extensions.h" #include "../widgets/MiscWidgets.h" -#include "../windows/InfoWindows.h" #include "../../CCallback.h" diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 4de14e4b0..3d1550bfa 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -13,6 +13,7 @@ #include "CAdvmapInterface.h" #include "CHeroWindow.h" #include "CTradeWindow.h" +#include "InfoWindows.h" #include "GUIClasses.h" #include "QuickRecruitmentWindow.h" @@ -24,7 +25,6 @@ #include "../Graphics.h" #include "../gui/CGuiHandler.h" #include "../gui/SDL_Extensions.h" -#include "../windows/InfoWindows.h" #include "../widgets/MiscWidgets.h" #include "../widgets/CComponent.h" diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 0957aab22..163cb3966 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -12,6 +12,7 @@ #include "CAdvmapInterface.h" #include "CCastleInterface.h" +#include "InfoWindows.h" #include "../CGameInfo.h" #include "../CMT.h" @@ -19,7 +20,6 @@ #include "../gui/CGuiHandler.h" #include "../widgets/CComponent.h" #include "../widgets/MiscWidgets.h" -#include "../windows/InfoWindows.h" #include "../../CCallback.h" diff --git a/client/windows/CWindowObject.cpp b/client/windows/CWindowObject.cpp index d3e78564b..ee6f0f0b4 100644 --- a/client/windows/CWindowObject.cpp +++ b/client/windows/CWindowObject.cpp @@ -10,6 +10,8 @@ #include "StdInc.h" #include "CWindowObject.h" +#include "CAdvmapInterface.h" + #include "../widgets/MiscWidgets.h" #include "../gui/SDL_Pixels.h" @@ -26,7 +28,6 @@ #include "../CPlayerInterface.h" #include "../CMessage.h" #include "../CMusicHandler.h" -#include "../windows/CAdvmapInterface.h" #include "../../CCallback.h" @@ -251,4 +252,4 @@ void CStatusbarWindow::activate() { CIntObject::activate(); GH.statusbar = statusbar; -} \ No newline at end of file +} diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 537e454fb..39348eb83 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -15,6 +15,7 @@ #include "CCreatureWindow.h" #include "CHeroWindow.h" #include "CreatureCostBox.h" +#include "InfoWindows.h" #include "../CBitmapHandler.h" #include "../CGameInfo.h" @@ -36,7 +37,6 @@ #include "../widgets/CComponent.h" #include "../widgets/MiscWidgets.h" -#include "../windows/InfoWindows.h" #include "../lobby/CSavingScreen.h" diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index a87ff932f..dc6bc9980 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -9,13 +9,13 @@ */ #pragma once +#include "CWindowObject.h" #include "../lib/GameConstants.h" #include "../lib/ResourceSet.h" #include "../lib/CConfigHandler.h" #include "../widgets/CArtifactHolder.h" #include "../widgets/CGarrisonInt.h" #include "../widgets/Images.h" -#include "../windows/CWindowObject.h" class CGDwelling; class CreatureCostBox; diff --git a/client/windows/InfoWindows.cpp b/client/windows/InfoWindows.cpp index 3a68d72bf..7f3ee26d3 100644 --- a/client/windows/InfoWindows.cpp +++ b/client/windows/InfoWindows.cpp @@ -10,6 +10,8 @@ #include "StdInc.h" #include "InfoWindows.h" +#include "CAdvmapInterface.h" + #include "../CBitmapHandler.h" #include "../Graphics.h" #include "../CGameInfo.h" @@ -17,7 +19,6 @@ #include "../CMessage.h" #include "../CMusicHandler.h" -#include "../windows/CAdvmapInterface.h" #include "../widgets/CComponent.h" #include "../widgets/MiscWidgets.h" diff --git a/lib/filesystem/Filesystem.cpp b/lib/filesystem/Filesystem.cpp index a96f3ec87..d478d136e 100644 --- a/lib/filesystem/Filesystem.cpp +++ b/lib/filesystem/Filesystem.cpp @@ -156,7 +156,7 @@ ISimpleResourceLoader * CResourceHandler::createInitial() void CResourceHandler::initialize() { - // Create tree-loke structure that looks like this: + // Create tree-like structure that looks like this: // root // | // |- initial From 81dec1405ff9a77b456c2208e44e8e8818b345e5 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 17 Sep 2022 15:44:13 +0300 Subject: [PATCH 06/37] fix typo: purhase -> purchase --- client/windows/CCastleInterface.cpp | 6 +++--- client/windows/CCastleInterface.h | 2 +- client/windows/CreaturePurchaseCard.cpp | 4 ++-- client/windows/CreaturePurchaseCard.h | 2 +- client/windows/QuickRecruitmentWindow.cpp | 8 ++++---- client/windows/QuickRecruitmentWindow.h | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 3d1550bfa..042dce13a 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -1244,9 +1244,9 @@ void CCastleInterface::recreateIcons() hall = std::make_shared(80, 413, town, true); fort = std::make_shared(122, 413, town, false); - fastArmyPurhase = std::make_shared(Point(122, 413), "itmcl.def", CButton::tooltip(), [&](){builds->enterToTheQuickRecruitmentWindow();}); - fastArmyPurhase->setImageOrder(town->fortLevel()-1, town->fortLevel()-1, town->fortLevel()-1, town->fortLevel()-1); - fastArmyPurhase->setAnimateLonelyFrame(true); + fastArmyPurchase = std::make_shared(Point(122, 413), "itmcl.def", CButton::tooltip(), [&](){builds->enterToTheQuickRecruitmentWindow();}); + fastArmyPurchase->setImageOrder(town->fortLevel()-1, town->fortLevel()-1, town->fortLevel()-1, town->fortLevel()-1); + fastArmyPurchase->setAnimateLonelyFrame(true); creainfo.clear(); diff --git a/client/windows/CCastleInterface.h b/client/windows/CCastleInterface.h index 07dfe2d86..088bb63bf 100644 --- a/client/windows/CCastleInterface.h +++ b/client/windows/CCastleInterface.h @@ -209,7 +209,7 @@ class CCastleInterface : public CStatusbarWindow, public CGarrisonHolder std::shared_ptr exit; std::shared_ptr split; - std::shared_ptr fastArmyPurhase; + std::shared_ptr fastArmyPurchase; std::vector> creainfo;//small icons of creatures (bottom-left corner); diff --git a/client/windows/CreaturePurchaseCard.cpp b/client/windows/CreaturePurchaseCard.cpp index 98083799a..074e86288 100644 --- a/client/windows/CreaturePurchaseCard.cpp +++ b/client/windows/CreaturePurchaseCard.cpp @@ -54,14 +54,14 @@ void CreaturePurchaseCard::switchCreatureLevel() void CreaturePurchaseCard::initAmountInfo() { availableAmount = std::make_shared(pos.x + 25, pos.y + 146, FONT_SMALL, CENTER, Colors::YELLOW); - purhaseAmount = std::make_shared(pos.x + 76, pos.y + 146, FONT_SMALL, CENTER, Colors::WHITE); + purchaseAmount = std::make_shared(pos.x + 76, pos.y + 146, FONT_SMALL, CENTER, Colors::WHITE); updateAmountInfo(0); } void CreaturePurchaseCard::updateAmountInfo(int value) { availableAmount->setText(boost::lexical_cast(maxAmount-value)); - purhaseAmount->setText(boost::lexical_cast(value)); + purchaseAmount->setText(boost::lexical_cast(value)); } void CreaturePurchaseCard::initSlider() diff --git a/client/windows/CreaturePurchaseCard.h b/client/windows/CreaturePurchaseCard.h index 91ce2b4bb..d2405388f 100644 --- a/client/windows/CreaturePurchaseCard.h +++ b/client/windows/CreaturePurchaseCard.h @@ -43,7 +43,7 @@ private: void initCostBox(); std::shared_ptr maxButton, minButton, creatureSwitcher; - std::shared_ptr availableAmount, purhaseAmount; + std::shared_ptr availableAmount, purchaseAmount; std::shared_ptr picture; std::shared_ptr cost; std::vector upgradesID; diff --git a/client/windows/QuickRecruitmentWindow.cpp b/client/windows/QuickRecruitmentWindow.cpp index a93e63a6d..0564b727a 100644 --- a/client/windows/QuickRecruitmentWindow.cpp +++ b/client/windows/QuickRecruitmentWindow.cpp @@ -35,7 +35,7 @@ void QuickRecruitmentWindow::setCancelButton() void QuickRecruitmentWindow::setBuyButton() { - buyButton = std::make_shared(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purhaseUnits(); }, SDLK_RETURN); + buyButton = std::make_shared(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purchaseUnits(); }, SDLK_RETURN); cancelButton->assignedKeys.insert(SDLK_ESCAPE); buyButton->setImageOrder(0, 1, 2, 3); } @@ -46,7 +46,7 @@ void QuickRecruitmentWindow::setMaxButton() maxButton->setImageOrder(0, 1, 2, 3); } -void QuickRecruitmentWindow::setCreaturePurhaseCards() +void QuickRecruitmentWindow::setCreaturePurchaseCards() { int availableAmount = getAvailableCreatures(); Point position = Point((pos.w - 100*availableAmount - 8*(availableAmount-1))/2,64); @@ -99,7 +99,7 @@ void QuickRecruitmentWindow::maxAllCards(std::vector> cards); void maxAllSlidersAmount(std::vector> cards); - void purhaseUnits(); + void purchaseUnits(); const CGTownInstance * town; std::shared_ptr maxButton, buyButton, cancelButton; From beeef8527fa6250c28ea83da8ab05f5afb37e4af Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 17 Sep 2022 22:22:09 +0300 Subject: [PATCH 07/37] Apply suggestions from code review Co-authored-by: Nordsoft91 --- client/windows/CCastleInterface.cpp | 4 ++-- client/windows/QuickRecruitmentWindow.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 042dce13a..9cfd8f991 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -1244,8 +1244,8 @@ void CCastleInterface::recreateIcons() hall = std::make_shared(80, 413, town, true); fort = std::make_shared(122, 413, town, false); - fastArmyPurchase = std::make_shared(Point(122, 413), "itmcl.def", CButton::tooltip(), [&](){builds->enterToTheQuickRecruitmentWindow();}); - fastArmyPurchase->setImageOrder(town->fortLevel()-1, town->fortLevel()-1, town->fortLevel()-1, town->fortLevel()-1); + fastArmyPurchase = std::make_shared(Point(122, 413), "itmcl.def", CButton::tooltip(), [&](){ builds->enterToTheQuickRecruitmentWindow(); }); + fastArmyPurchase->setImageOrder(town->fortLevel() - 1, town->fortLevel() - 1, town->fortLevel() - 1, town->fortLevel() - 1); fastArmyPurchase->setAnimateLonelyFrame(true); creainfo.clear(); diff --git a/client/windows/QuickRecruitmentWindow.cpp b/client/windows/QuickRecruitmentWindow.cpp index 0564b727a..e6ffb7492 100644 --- a/client/windows/QuickRecruitmentWindow.cpp +++ b/client/windows/QuickRecruitmentWindow.cpp @@ -35,7 +35,7 @@ void QuickRecruitmentWindow::setCancelButton() void QuickRecruitmentWindow::setBuyButton() { - buyButton = std::make_shared(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purchaseUnits(); }, SDLK_RETURN); + buyButton = std::make_shared(Point((pos.w / 2) - 32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purchaseUnits(); }, SDLK_RETURN); cancelButton->assignedKeys.insert(SDLK_ESCAPE); buyButton->setImageOrder(0, 1, 2, 3); } From 60264aae2985442acd4cee0865f37b816873625e Mon Sep 17 00:00:00 2001 From: Nordsoft91 Date: Mon, 19 Sep 2022 01:18:17 +0400 Subject: [PATCH 08/37] 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 dfdaf57803de6aa9de8d73e9c726755ad0c40c71 Mon Sep 17 00:00:00 2001 From: Agoston Szepessy Date: Sun, 18 Sep 2022 16:00:54 -0700 Subject: [PATCH 09/37] Set position for CreaturePurchaseCard This will make it pick up on right clicks. It was 0 before, so it wasn't doing it. --- client/windows/CreaturePurchaseCard.cpp | 15 ++++++++++++++- client/windows/CreaturePurchaseCard.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/client/windows/CreaturePurchaseCard.cpp b/client/windows/CreaturePurchaseCard.cpp index 98083799a..efbef024a 100644 --- a/client/windows/CreaturePurchaseCard.cpp +++ b/client/windows/CreaturePurchaseCard.cpp @@ -17,6 +17,7 @@ #include "QuickRecruitmentWindow.h" #include "../gui/CGuiHandler.h" #include "../../lib/CCreatureHandler.h" +#include "CCreatureWindow.h" void CreaturePurchaseCard::initButtons() { @@ -82,14 +83,26 @@ void CreaturePurchaseCard::sliderMoved(int to) parent->updateAllSliders(); } +void CreaturePurchaseCard::clickRight(tribool down, bool previousState) +{ + if (down) + GH.pushIntT(creatureOnTheCard, true); +} + CreaturePurchaseCard::CreaturePurchaseCard(const std::vector & creaturesID, Point position, int creaturesMaxAmount, QuickRecruitmentWindow * parents) - : upgradesID(creaturesID), + : CIntObject(RCLICK), + upgradesID(creaturesID), parent(parents), maxAmount(creaturesMaxAmount) { creatureOnTheCard = upgradesID.back().toCreature(); moveTo(Point(position.x, position.y)); initView(); + + // Card's position needs to be set to the animation's width/height + // otherwise the clicks won't register + pos.w = picture->pos.w; + pos.h = picture->pos.h; } void CreaturePurchaseCard::initView() diff --git a/client/windows/CreaturePurchaseCard.h b/client/windows/CreaturePurchaseCard.h index 91ce2b4bb..6929fff4c 100644 --- a/client/windows/CreaturePurchaseCard.h +++ b/client/windows/CreaturePurchaseCard.h @@ -25,6 +25,8 @@ public: QuickRecruitmentWindow * parent; int maxAmount; void sliderMoved(int to); + void clickRight(tribool down, bool previousState) override; + CreaturePurchaseCard(const std::vector & creaturesID, Point position, int creaturesMaxAmount, QuickRecruitmentWindow * parents); private: void initView(); From dcfafac2665769993d04d412a8b8ce98dbf7dc37 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 19 Sep 2022 03:52:08 +0400 Subject: [PATCH 10/37] 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 e423e415c15d982930cdb6c254671b6b6151c0bf Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sun, 18 Sep 2022 18:02:49 +0300 Subject: [PATCH 11/37] move Uninstall button to the left side of layout --- launcher/modManager/cmodlistview_moc.ui | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/launcher/modManager/cmodlistview_moc.ui b/launcher/modManager/cmodlistview_moc.ui index c936008cf..50542e75d 100644 --- a/launcher/modManager/cmodlistview_moc.ui +++ b/launcher/modManager/cmodlistview_moc.ui @@ -253,7 +253,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p></body></html> @@ -385,6 +385,31 @@ p, li { white-space: pre-wrap; } + + + + + 0 + 0 + + + + + 51 + 0 + + + + + 140 + 16777215 + + + + Uninstall + + + @@ -460,31 +485,6 @@ p, li { white-space: pre-wrap; } - - - - - 0 - 0 - - - - - 51 - 0 - - - - - 140 - 16777215 - - - - Uninstall - - - From 411038195cd68327ef0b920935bf69dcee3f4a4e Mon Sep 17 00:00:00 2001 From: Agoston Szepessy Date: Sun, 18 Sep 2022 22:45:30 -0700 Subject: [PATCH 12/37] Add clickeable area on top of creature picture A class that wraps an instance of `CIntObject` creates an area that users can right click to view information about the creature they're buying. The constants were based off the existing ones in the code and were refined by guessing and checking where clicking falls inside/outside the border of the rectangle. --- client/gui/CIntObject.h | 1 + client/windows/CreaturePurchaseCard.cpp | 36 +++++++++++++++---------- client/windows/CreaturePurchaseCard.h | 19 ++++++++++++- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index 67517c192..6eac35e19 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -140,6 +140,7 @@ public: //double click virtual void onDoubleClick(){} + // These are the arguments that can be used to determine what kind of input the CIntObject will receive enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, MCLICK=1024, ALL=0xffff}; const ui16 & active; void addUsedEvents(ui16 newActions); diff --git a/client/windows/CreaturePurchaseCard.cpp b/client/windows/CreaturePurchaseCard.cpp index efbef024a..b0d22f0d0 100644 --- a/client/windows/CreaturePurchaseCard.cpp +++ b/client/windows/CreaturePurchaseCard.cpp @@ -47,6 +47,7 @@ void CreaturePurchaseCard::switchCreatureLevel() auto index = vstd::find_pos(upgradesID, creatureOnTheCard->idNumber); auto nextCreatureId = vstd::circularAt(upgradesID, ++index); creatureOnTheCard = nextCreatureId.toCreature(); + creatureClickArea = std::make_shared(Point(pos.x + CCreatureClickArea::CREATURE_X_POS, pos.y + CCreatureClickArea::CREATURE_Y_POS), picture, creatureOnTheCard); picture = std::make_shared(parent->pos.x, parent->pos.y, creatureOnTheCard); parent->updateAllSliders(); cost->set(creatureOnTheCard->cost * slider->getValue()); @@ -83,34 +84,41 @@ void CreaturePurchaseCard::sliderMoved(int to) parent->updateAllSliders(); } -void CreaturePurchaseCard::clickRight(tribool down, bool previousState) -{ - if (down) - GH.pushIntT(creatureOnTheCard, true); -} - CreaturePurchaseCard::CreaturePurchaseCard(const std::vector & creaturesID, Point position, int creaturesMaxAmount, QuickRecruitmentWindow * parents) - : CIntObject(RCLICK), - upgradesID(creaturesID), + : upgradesID(creaturesID), parent(parents), maxAmount(creaturesMaxAmount) { creatureOnTheCard = upgradesID.back().toCreature(); moveTo(Point(position.x, position.y)); initView(); - - // Card's position needs to be set to the animation's width/height - // otherwise the clicks won't register - pos.w = picture->pos.w; - pos.h = picture->pos.h; } void CreaturePurchaseCard::initView() { picture = std::make_shared(pos.x, pos.y, creatureOnTheCard); background = std::make_shared("QuickRecruitmentWindow/CreaturePurchaseCard.png", pos.x-4, pos.y-50); + initButtons(); + + creatureClickArea = std::make_shared(Point(pos.x + CCreatureClickArea::CREATURE_X_POS, pos.y + CCreatureClickArea::CREATURE_Y_POS), picture, creatureOnTheCard); + initAmountInfo(); initSlider(); - initButtons(); initCostBox(); } + +CreaturePurchaseCard::CCreatureClickArea::CCreatureClickArea(const Point & position, const std::shared_ptr creaturePic, const CCreature * creatureOnTheCard) + : CIntObject(RCLICK), + creatureOnTheCard(creatureOnTheCard) +{ + pos.x = position.x; + pos.y = position.y; + pos.w = CREATURE_WIDTH; + pos.h = CREATURE_HEIGHT; +} + +void CreaturePurchaseCard::CCreatureClickArea::clickRight(tribool down, bool previousState) +{ + if (down) + GH.pushIntT(creatureOnTheCard, true); +} diff --git a/client/windows/CreaturePurchaseCard.h b/client/windows/CreaturePurchaseCard.h index 6929fff4c..d652985a5 100644 --- a/client/windows/CreaturePurchaseCard.h +++ b/client/windows/CreaturePurchaseCard.h @@ -25,7 +25,6 @@ public: QuickRecruitmentWindow * parent; int maxAmount; void sliderMoved(int to); - void clickRight(tribool down, bool previousState) override; CreaturePurchaseCard(const std::vector & creaturesID, Point position, int creaturesMaxAmount, QuickRecruitmentWindow * parents); private: @@ -44,10 +43,28 @@ private: void initCostBox(); + // This just wraps a clickeable area. There's a weird layout scheme in the file and + // it's easier to just add a separate invisble box on top + class CCreatureClickArea : public CIntObject + { + public: + CCreatureClickArea(const Point & pos, const std::shared_ptr creaturePic, const CCreature * creatureOnTheCard); + void clickRight(tribool down, bool previousState) override; + const CCreature * creatureOnTheCard; + + // These are obtained by guessing and checking. I'm not sure how the other numbers + // used to set positions were obtained; commit messages don't document it + static constexpr int CREATURE_WIDTH = 110; + static constexpr int CREATURE_HEIGHT = 132; + static constexpr int CREATURE_X_POS = 15; + static constexpr int CREATURE_Y_POS = 44; + }; + std::shared_ptr maxButton, minButton, creatureSwitcher; std::shared_ptr availableAmount, purhaseAmount; std::shared_ptr picture; std::shared_ptr cost; std::vector upgradesID; std::shared_ptr background; + std::shared_ptr creatureClickArea; }; From 63409dd7005ce465cd865abce31159fec3adc2fa Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Mon, 19 Sep 2022 11:31:48 +0300 Subject: [PATCH 13/37] fix indentation --- .github/workflows/github.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/github.yml b/.github/workflows/github.yml index 2c3114e25..2f528e4c9 100644 --- a/.github/workflows/github.yml +++ b/.github/workflows/github.yml @@ -1,11 +1,11 @@ name: VCMI on: - push: - branches: - - features/* - - develop - pull_request: + push: + branches: + - features/* + - develop + pull_request: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release From fd10d8a88721a6d96248e48d244ffd71c49f04d0 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 19 Sep 2022 12:34:18 +0400 Subject: [PATCH 14/37] 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) { From 753e7905b3acdd73bdffda3d13b3c04cce94a0d7 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Tue, 20 Sep 2022 12:04:00 +0300 Subject: [PATCH 15/37] [CI] perform nightly develop builds instead of on each push --- .github/workflows/github.yml | 52 +++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github.yml b/.github/workflows/github.yml index 2f528e4c9..91ab2da54 100644 --- a/.github/workflows/github.yml +++ b/.github/workflows/github.yml @@ -4,14 +4,64 @@ on: push: branches: - features/* - - develop pull_request: + schedule: + - cron: '0 2 * * *' + env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: + check_last_build: + if: github.event.schedule != '' + runs-on: ubuntu-latest + outputs: + skip_build: ${{ steps.check_if_built.outputs.skip_build }} + defaults: + run: + shell: bash + steps: + - name: Get repo name + id: get_repo_name + run: echo "::set-output name=value::${GITHUB_REPOSITORY#*/}" + - name: Get last successful build for ${{ github.sha }} + uses: octokit/request-action@v2.1.0 + id: get_last_scheduled_run + with: + route: GET /repos/{owner}/{repo}/actions/runs + owner: ${{ github.repository_owner }} + repo: ${{ steps.get_repo_name.outputs.value }} + status: success + per_page: 1 + head_sha: ${{ github.sha }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check if successful build of the current commit exists + id: check_if_built + run: | + if [ ${{ fromJson(steps.get_last_scheduled_run.outputs.data).total_count }} -gt 0 ]; then + echo '::set-output name=skip_build::1' + else + echo '::set-output name=skip_build::0' + fi + - name: Cancel current run + if: steps.check_if_built.outputs.skip_build == 1 + uses: octokit/request-action@v2.1.0 + with: + route: POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel + owner: ${{ github.repository_owner }} + repo: ${{ steps.get_repo_name.outputs.value }} + run_id: ${{ github.run_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Wait for the run to be cancelled + if: steps.check_if_built.outputs.skip_build == 1 + run: sleep 60 + build: + needs: check_last_build + if: always() && needs.check_last_build.skip_build != 1 strategy: matrix: include: From 861df6fe71f726b633672895fa8420176aa23db8 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Tue, 20 Sep 2022 12:04:15 +0300 Subject: [PATCH 16/37] [CI] add ability to run workflow manually --- .github/workflows/github.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github.yml b/.github/workflows/github.yml index 91ab2da54..2220e982e 100644 --- a/.github/workflows/github.yml +++ b/.github/workflows/github.yml @@ -7,6 +7,7 @@ on: pull_request: schedule: - cron: '0 2 * * *' + workflow_dispatch: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) From 1313da191bf23aa96f72379ef017ddbbb41d8de8 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Mon, 19 Sep 2022 17:45:48 +0300 Subject: [PATCH 17/37] trigger Drain Life effects after the attack - animation and sound are played after the attack - amount of drained life appears after damage dealt in the battle log --- server/CGameHandler.cpp | 67 +++++++++++++++++++++++------------------ server/CGameHandler.h | 3 +- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5dcd2dcea..218b365e1 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1048,17 +1048,19 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG; } } + + int64_t drainedLife = 0; + // only primary target if(defender->alive()) - applyBattleEffects(bat, blm, attackerState, fireShield, defender, distance, false); + drainedLife += applyBattleEffects(bat, attackerState, fireShield, defender, distance, false); //multiple-hex normal attack std::set attackedCreatures = gs->curB->getAttackedCreatures(attacker, targetHex, bat.shot()); //creatures other than primary target - for(const CStack * stack : attackedCreatures) { if(stack != defender && stack->alive()) //do not hit same stack twice - applyBattleEffects(bat, blm, attackerState, fireShield, stack, distance, true); + drainedLife += applyBattleEffects(bat, attackerState, fireShield, stack, distance, true); } std::shared_ptr bonus = attacker->getBonusLocalFirst(Selector::type()(Bonus::SPELL_LIKE_ATTACK)); @@ -1086,7 +1088,7 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, { if(stack != defender && stack->alive()) //do not hit same stack twice { - applyBattleEffects(bat, blm, attackerState, fireShield, stack, distance, true); + drainedLife += applyBattleEffects(bat, attackerState, fireShield, stack, distance, true); } } @@ -1134,6 +1136,29 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, addGenericKilledLog(blm, defender, totalKills, multipleTargets); } + + // drain life effect (as well as log entry) must be applied after the attack + if(drainedLife > 0) + { + BattleAttack bat; + bat.stackAttacking = attacker->unitId(); + { + CustomEffectInfo customEffect; + customEffect.sound = soundBase::DRAINLIF; + customEffect.effect = 52; + customEffect.stack = attackerState->unitId(); + bat.customEffects.push_back(std::move(customEffect)); + } + sendAndApply(&bat); + + MetaString text; + attackerState->addText(text, MetaString::GENERAL_TXT, 361); + attackerState->addNameReplacement(text, false); + text.addReplacement(drainedLife); + defender->addNameReplacement(text, true); + blm.lines.push_back(std::move(text)); + } + sendAndApply(&blm); if(!fireShield.empty()) @@ -1179,7 +1204,8 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, handleAfterAttackCasting(ranged, attacker, defender); } -void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm, std::shared_ptr attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary) + +int64_t CGameHandler::applyBattleEffects(BattleAttack & bat, std::shared_ptr attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary) { BattleStackAttacked bsa; if(secondary) @@ -1208,34 +1234,14 @@ void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm CStack::prepareAttacked(bsa, getRandomGenerator(), bai.defender->acquireState()); //calculate casualties } - auto addLifeDrain = [&](int64_t & toHeal, EHealLevel level, EHealPower power) - { - attackerState->heal(toHeal, level, power); - - if(toHeal > 0) - { - CustomEffectInfo customEffect; - customEffect.sound = soundBase::DRAINLIF; - customEffect.effect = 52; - customEffect.stack = attackerState->unitId(); - bat.customEffects.push_back(customEffect); - - MetaString text; - attackerState->addText(text, MetaString::GENERAL_TXT, 361); - attackerState->addNameReplacement(text, false); - text.addReplacement((int)toHeal); - def->addNameReplacement(text, true); - blm.lines.push_back(text); - } - }; + int64_t drainedLife = 0; //life drain handling if(attackerState->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving()) { int64_t toHeal = bsa.damageAmount * attackerState->valOfBonuses(Bonus::LIFE_DRAIN) / 100; - - if(toHeal > 0) - addLifeDrain(toHeal, EHealLevel::RESURRECT, EHealPower::PERMANENT); + attackerState->heal(toHeal, EHealLevel::RESURRECT, EHealPower::PERMANENT); + drainedLife += toHeal; } //soul steal handling @@ -1248,7 +1254,8 @@ void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm if(attackerState->hasBonusOfType(Bonus::SOUL_STEAL, subtype)) { int64_t toHeal = bsa.killedAmount * attackerState->valOfBonuses(Bonus::SOUL_STEAL, subtype) * attackerState->MaxHealth(); - addLifeDrain(toHeal, EHealLevel::OVERHEAL, ((subtype == 0) ? EHealPower::ONE_BATTLE : EHealPower::PERMANENT)); + attackerState->heal(toHeal, EHealLevel::OVERHEAL, ((subtype == 0) ? EHealPower::ONE_BATTLE : EHealPower::PERMANENT)); + drainedLife += toHeal; break; } } @@ -1263,6 +1270,8 @@ void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm auto fireShieldDamage = (std::min(def->getAvailableHealth(), bsa.damageAmount) * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100; fireShield.push_back(std::make_pair(def, fireShieldDamage)); } + + return drainedLife; } void CGameHandler::sendGenericKilledLog(const CStack * defender, int32_t killed, bool multiple) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 2c90edf6e..e80b2d21c 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -126,7 +126,8 @@ public: void makeAttack(const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter); - void applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm, std::shared_ptr attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary); //damage, drain life & fire shield + // damage, drain life & fire shield; returns amount of drained life + int64_t applyBattleEffects(BattleAttack & bat, std::shared_ptr attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary); void sendGenericKilledLog(const CStack * defender, int32_t killed, bool multiple); void addGenericKilledLog(BattleLogMessage & blm, const CStack * defender, int32_t killed, bool multiple); From b95841dabbd80bb3db466b02995768e449fa5a11 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Wed, 21 Sep 2022 10:38:42 +0300 Subject: [PATCH 18/37] fix creating "creatures perished" battle log entry --- server/CGameHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 218b365e1..7c3d97dcf 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1306,8 +1306,8 @@ void CGameHandler::addGenericKilledLog(BattleLogMessage & blm, const CStack * de txt % (multiple ? VLC->generaltexth->allTexts[42] : defender->getCreature()->nameSing); // creature perishes } MetaString line; - line.addReplacement(txt.str()); - blm.lines.push_back(line); + line << txt.str(); + blm.lines.push_back(std::move(line)); } } From 87a88da3a97bc31af0844a3d09934c6d9a8dff0d Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Wed, 21 Sep 2022 10:42:42 +0300 Subject: [PATCH 19/37] add Fire Shield damage to the battle log --- server/CGameHandler.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7c3d97dcf..9c5cd027f 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1159,8 +1159,6 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, blm.lines.push_back(std::move(text)); } - sendAndApply(&blm); - if(!fireShield.empty()) { //todo: this should be "virtual" spell instead, we only need fire spell school bonus here @@ -1199,9 +1197,20 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender, StacksInjured pack; pack.stacks.push_back(bsa); sendAndApply(&pack); - sendGenericKilledLog(attacker, bsa.killedAmount, false); + + // TODO: this is already implemented in Damage::describeEffect() + { + MetaString text; + text.addTxt(MetaString::GENERAL_TXT, 376); + text.addReplacement(MetaString::SPELL_NAME, SpellID::FIRE_SHIELD); + text.addReplacement(totalDamage); + blm.lines.push_back(std::move(text)); + } + addGenericKilledLog(blm, attacker, bsa.killedAmount, false); } + sendAndApply(&blm); + handleAfterAttackCasting(ranged, attacker, defender); } From 6cd810e065f4445d910cbe86b00be4a763b69fc9 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Wed, 21 Sep 2022 15:19:04 +0300 Subject: [PATCH 20/37] fix showing "visited" message for Warrior's Tomb --- lib/mapObjects/CRewardableObject.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 134f54b0c..34b57072d 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -850,17 +850,16 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand) case Obj::WARRIORS_TOMB: { onSelect.addTxt(MetaString::ADVOB_TXT, 161); + onVisited.addTxt(MetaString::ADVOB_TXT, 163); - info.resize(2); + info.resize(1); loadRandomArtifact(rand, info[0], 30, 50, 25, 5); Bonus bonus(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::OBJECT, -3, ID); info[0].reward.bonuses.push_back(bonus); - info[1].reward.bonuses.push_back(bonus); info[0].limiter.numOfGrants = 1; info[0].message.addTxt(MetaString::ADVOB_TXT, 162); info[0].message.addReplacement(VLC->arth->objects[info[0].reward.artifacts.back()]->getName()); - info[1].message.addTxt(MetaString::ADVOB_TXT, 163); } break; case Obj::WAGON: From f54f8022e25b4e0758e35118771c48978766d72e Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Wed, 21 Sep 2022 17:47:57 +0300 Subject: [PATCH 21/37] disable scripting modules by default --- CMakeLists.txt | 4 ++-- CMakePresets.json | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb6cf8e6a..4f1b8c7d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,8 @@ set(VCMI_VERSION_MAJOR 1) set(VCMI_VERSION_MINOR 0) set(VCMI_VERSION_PATCH 0) -option(ENABLE_ERM "Enable compilation of ERM scripting module" ON) -option(ENABLE_LUA "Enable compilation of LUA scripting module" ON) +option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF) +option(ENABLE_LUA "Enable compilation of LUA scripting module" OFF) option(ENABLE_LAUNCHER "Enable compilation of launcher" ON) option(ENABLE_TEST "Enable compilation of unit tests" ON) if(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0") diff --git a/CMakePresets.json b/CMakePresets.json index e1735cbfd..20a5a23ed 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -79,11 +79,7 @@ "name": "macos-arm-conan-ninja-release", "displayName": "Ninja+Conan arm64 release", "description": "VCMI MacOS-arm64 Ninja using Conan", - "inherits": "macos-conan-ninja-release", - "cacheVariables": { - "ENABLE_ERM": "OFF", - "ENABLE_LUA": "OFF" - } + "inherits": "macos-conan-ninja-release" }, { "name": "macos-xcode-release", From 4058c2c18abc13532d5f119575856ead5ed30b82 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Wed, 21 Sep 2022 19:07:49 +0300 Subject: [PATCH 22/37] model implicit ERM -> LUA dependency --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f1b8c7d2..e206ba0ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,11 @@ option(ENABLE_MONOLITHIC_INSTALL "Install everything in single directory on Linu set(PACKAGE_NAME_SUFFIX "" CACHE STRING "Suffix for CPack package name") set(PACKAGE_FILE_NAME "" CACHE STRING "Override for CPack package filename") +# ERM depends on LUA implicitly +if(ENABLE_ERM AND NOT ENABLE_LUA) + set(ENABLE_LUA ON) +endif() + ############################################ # Miscellaneous options # ############################################ From 7e6ed0583caa328c60b23db7c94e2f64c550ac78 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Wed, 21 Sep 2022 19:31:14 +0300 Subject: [PATCH 23/37] disable all scripting code when configuring without scripting modules --- AI/BattleAI/StackWithBonuses.cpp | 6 ++++++ AI/BattleAI/StackWithBonuses.h | 4 ++++ CCallback.cpp | 2 ++ CCallback.h | 2 ++ CMakeLists.txt | 4 ++++ client/CGameInfo.cpp | 2 ++ client/CGameInfo.h | 2 ++ client/CMT.cpp | 2 ++ client/Client.cpp | 10 ++++++++++ client/Client.h | 7 +++++++ include/vcmi/Services.h | 4 ++++ include/vcmi/scripting/Service.h | 2 ++ lib/CGameInterface.cpp | 2 ++ lib/CGameInterface.h | 6 ++++++ lib/CModHandler.cpp | 4 ++++ lib/CScriptingModule.cpp | 2 ++ lib/CScriptingModule.h | 2 ++ lib/IGameCallback.h | 7 +++++-- lib/ScriptHandler.cpp | 2 ++ lib/ScriptHandler.h | 2 ++ lib/VCMI_Lib.cpp | 10 ++++++++++ lib/VCMI_Lib.h | 11 +++++++++++ lib/battle/BattleInfo.cpp | 2 ++ lib/battle/BattleInfo.h | 2 ++ lib/battle/CBattleInfoCallback.h | 7 ------- lib/battle/IBattleInfoCallback.h | 4 ++++ lib/spells/ISpellMechanics.cpp | 2 ++ lib/spells/ISpellMechanics.h | 6 ++++++ server/CGameHandler.cpp | 8 +++++++- server/CGameHandler.h | 8 ++++++++ 30 files changed, 124 insertions(+), 10 deletions(-) diff --git a/AI/BattleAI/StackWithBonuses.cpp b/AI/BattleAI/StackWithBonuses.cpp index 2b3679569..fc93ffc03 100644 --- a/AI/BattleAI/StackWithBonuses.cpp +++ b/AI/BattleAI/StackWithBonuses.cpp @@ -16,7 +16,9 @@ #include "../../lib/CStack.h" #include "../../lib/ScriptHandler.h" +#if SCRIPTING_ENABLED using scripting::Pool; +#endif void actualizeEffect(TBonusListPtr target, const Bonus & ef) { @@ -217,7 +219,9 @@ HypotheticBattle::HypotheticBattle(const Environment * ENV, Subject realBattle) localEnvironment.reset(new HypotheticEnvironment(this, env)); serverCallback.reset(new HypotheticServerCallback(this)); +#if SCRIPTING_ENABLED pool.reset(new scripting::PoolImpl(localEnvironment.get(), serverCallback.get())); +#endif } bool HypotheticBattle::unitHasAmmoCart(const battle::Unit * unit) const @@ -420,10 +424,12 @@ int64_t HypotheticBattle::getTreeVersion() const return getBattleNode()->getTreeVersion() + bonusTreeVersion; } +#if SCRIPTING_ENABLED Pool * HypotheticBattle::getContextPool() const { return pool.get(); } +#endif ServerCallback * HypotheticBattle::getServerCallback() { diff --git a/AI/BattleAI/StackWithBonuses.h b/AI/BattleAI/StackWithBonuses.h index dc2ac3ce7..eb8d57db5 100644 --- a/AI/BattleAI/StackWithBonuses.h +++ b/AI/BattleAI/StackWithBonuses.h @@ -136,7 +136,9 @@ public: int64_t getTreeVersion() const; +#if SCRIPTING_ENABLED scripting::Pool * getContextPool() const override; +#endif ServerCallback * getServerCallback(); @@ -189,6 +191,8 @@ private: std::unique_ptr serverCallback; std::unique_ptr localEnvironment; +#if SCRIPTING_ENABLED mutable std::shared_ptr pool; +#endif mutable std::shared_ptr eventBus; }; diff --git a/CCallback.cpp b/CCallback.cpp index cc1714849..a4c2de397 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -366,10 +366,12 @@ void CCallback::unregisterBattleInterface(std::shared_ptr cl->additionalBattleInts[*player] -= battleEvents; } +#if SCRIPTING_ENABLED scripting::Pool * CBattleCallback::getContextPool() const { return cl->getGlobalContextPool(); } +#endif CBattleCallback::CBattleCallback(boost::optional Player, CClient *C ) { diff --git a/CCallback.h b/CCallback.h index 76f4413d0..7fe4583d3 100644 --- a/CCallback.h +++ b/CCallback.h @@ -99,7 +99,9 @@ public: int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions +#if SCRIPTING_ENABLED scripting::Pool * getContextPool() const override; +#endif friend class CCallback; friend class CClient; diff --git a/CMakeLists.txt b/CMakeLists.txt index e206ba0ed..98eaa413f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,6 +219,10 @@ if(NOT WIN32) endif() endif() +if(ENABLE_LUA) + add_compile_definitions(SCRIPTING_ENABLED=1) +endif() + ############################################ # Finding packages # ############################################ diff --git a/client/CGameInfo.cpp b/client/CGameInfo.cpp index d8c58296a..b93f149db 100644 --- a/client/CGameInfo.cpp +++ b/client/CGameInfo.cpp @@ -71,10 +71,12 @@ const HeroTypeService * CGameInfo::heroTypes() const return globalServices->heroTypes(); } +#if SCRIPTING_ENABLED const scripting::Service * CGameInfo::scripts() const { return globalServices->scripts(); } +#endif const spells::Service * CGameInfo::spells() const { diff --git a/client/CGameInfo.h b/client/CGameInfo.h index 70b56905c..442be449d 100644 --- a/client/CGameInfo.h +++ b/client/CGameInfo.h @@ -59,7 +59,9 @@ public: const FactionService * factions() const override; const HeroClassService * heroClasses() const override; const HeroTypeService * heroTypes() const override; +#if SCRIPTING_ENABLED const scripting::Service * scripts() const override; +#endif const spells::Service * spells() const override; const SkillService * skills() const override; const BattleFieldService * battlefields() const override; diff --git a/client/CMT.cpp b/client/CMT.cpp index ebc5274f4..5754814b8 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -686,6 +686,7 @@ void processCommand(const std::string &message) std::cout << "\rExtracting done :)\n"; std::cout << " Extracted files can be found in " << outPath << " directory\n"; } +#if SCRIPTING_ENABLED else if(message=="get scripts") { std::cout << "Command accepted.\t"; @@ -708,6 +709,7 @@ void processCommand(const std::string &message) std::cout << "\rExtracting done :)\n"; std::cout << " Extracted files can be found in " << outPath << " directory\n"; } +#endif else if(message=="get txt") { std::cout << "Command accepted.\t"; diff --git a/client/Client.cpp b/client/Client.cpp index a200c2044..e145c1b12 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -263,12 +263,14 @@ void CClient::serialize(BinarySerializer & h, const int version) i->second->saveGame(h, version); } +#if SCRIPTING_ENABLED if(version >= 800) { JsonNode scriptsState; clientScripts->serializeState(h.saving, scriptsState); h & scriptsState; } +#endif } void CClient::serialize(BinaryDeserializer & h, const int version) @@ -329,11 +331,13 @@ void CClient::serialize(BinaryDeserializer & h, const int version) LOCPLINT = prevInt; } +#if SCRIPTING_ENABLED { JsonNode scriptsState; h & scriptsState; clientScripts->serializeState(h.saving, scriptsState); } +#endif logNetwork->trace("Loaded client part of save %d ms", CSH->th->getDiff()); } @@ -352,7 +356,9 @@ void CClient::save(const std::string & fname) void CClient::endGame() { +#if SCRIPTING_ENABLED clientScripts.reset(); +#endif //suggest interfaces to finish their stuff (AI should interrupt any bg working threads) for(auto & i : playerint) @@ -732,6 +738,7 @@ PlayerColor CClient::getLocalPlayer() const return getCurrentPlayer(); } +#if SCRIPTING_ENABLED scripting::Pool * CClient::getGlobalContextPool() const { return clientScripts.get(); @@ -741,11 +748,14 @@ scripting::Pool * CClient::getContextPool() const { return clientScripts.get(); } +#endif void CClient::reinitScripting() { clientEventBus = make_unique(); +#if SCRIPTING_ENABLED clientScripts.reset(new scripting::PoolImpl(this)); +#endif } diff --git a/client/Client.h b/client/Client.h index 7f93aa3fe..38a2b4551 100644 --- a/client/Client.h +++ b/client/Client.h @@ -39,10 +39,12 @@ namespace boost { class thread; } template class CApplier; class CBaseForCLApply; +#if SCRIPTING_ENABLED namespace scripting { class PoolImpl; } +#endif namespace events { @@ -233,13 +235,18 @@ public: void showInfoDialog(InfoWindow * iw) override {}; void showInfoDialog(const std::string & msg, PlayerColor player) override {}; +#if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; scripting::Pool * getContextPool() const override; +#endif + private: std::map> battleCallbacks; //callbacks given to player interfaces std::map> playerEnvironments; +#if SCRIPTING_ENABLED std::shared_ptr clientScripts; +#endif std::unique_ptr clientEventBus; std::shared_ptr> applier; diff --git a/include/vcmi/Services.h b/include/vcmi/Services.h index 7bd2a7bb0..1bc33a1dd 100644 --- a/include/vcmi/Services.h +++ b/include/vcmi/Services.h @@ -32,10 +32,12 @@ namespace spells } } +#if SCRIPTING_ENABLED namespace scripting { class Service; } +#endif class DLL_LINKAGE Services { @@ -47,7 +49,9 @@ public: virtual const FactionService * factions() const = 0; virtual const HeroClassService * heroClasses() const = 0; virtual const HeroTypeService * heroTypes() const = 0; +#if SCRIPTING_ENABLED virtual const scripting::Service * scripts() const = 0; +#endif virtual const spells::Service * spells() const = 0; virtual const SkillService * skills() const = 0; virtual const BattleFieldService * battlefields() const = 0; diff --git a/include/vcmi/scripting/Service.h b/include/vcmi/scripting/Service.h index d09882dfa..e94300598 100644 --- a/include/vcmi/scripting/Service.h +++ b/include/vcmi/scripting/Service.h @@ -10,6 +10,7 @@ #pragma once +#if SCRIPTING_ENABLED #include class Services; @@ -78,3 +79,4 @@ public: } +#endif diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index fc4731688..84b9e9745 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -126,10 +126,12 @@ std::shared_ptr CDynLibHandler::getNewBattleAI(std::string return createAnyAI(dllname, "GetNewBattleAI"); } +#if SCRIPTING_ENABLED std::shared_ptr CDynLibHandler::getNewScriptingModule(const boost::filesystem::path & dllname) { return createAny(dllname, "GetNewModule"); } +#endif BattleAction CGlobalAI::activeStack(const CStack * stack) { diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index 35b8671fe..c4ab18378 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -56,10 +56,14 @@ class CSaveFile; class BinaryDeserializer; class BinarySerializer; struct ArtifactLocation; + +#if SCRIPTING_ENABLED namespace scripting { class Module; } +#endif + class DLL_LINKAGE CBattleGameInterface : public IBattleEventsReceiver { @@ -110,7 +114,9 @@ class DLL_LINKAGE CDynLibHandler public: static std::shared_ptr getNewAI(std::string dllname); static std::shared_ptr getNewBattleAI(std::string dllname); +#if SCRIPTING_ENABLED static std::shared_ptr getNewScriptingModule(const boost::filesystem::path & dllname); +#endif }; class DLL_LINKAGE CGlobalAI : public CGameInterface // AI class (to derivate) diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 1c30b1208..8158c8621 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -434,7 +434,9 @@ void CContentHandler::init() handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell"))); handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill"))); handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template"))); +#if SCRIPTING_ENABLED handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script"))); +#endif handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield"))); handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle"))); //TODO: any other types of moddables? @@ -986,7 +988,9 @@ void CModHandler::load() for(const TModID & modName : activeMods) content->load(allMods[modName]); +#if SCRIPTING_ENABLED VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load +#endif content->loadCustom(); diff --git a/lib/CScriptingModule.cpp b/lib/CScriptingModule.cpp index e85e3c72e..c6da26f01 100644 --- a/lib/CScriptingModule.cpp +++ b/lib/CScriptingModule.cpp @@ -11,6 +11,7 @@ #include "CScriptingModule.h" +#if SCRIPTING_ENABLED namespace scripting { @@ -30,3 +31,4 @@ Module::Module() Module::~Module() = default; } +#endif diff --git a/lib/CScriptingModule.h b/lib/CScriptingModule.h index eacf0c599..77b59249f 100644 --- a/lib/CScriptingModule.h +++ b/lib/CScriptingModule.h @@ -9,6 +9,7 @@ */ #pragma once +#if SCRIPTING_ENABLED #include namespace spells @@ -45,3 +46,4 @@ public: }; } +#endif diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 532532dac..0f4ad0940 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -27,12 +27,13 @@ class CStackBasicDescriptor; class CGCreature; struct ShashInt3; +#if SCRIPTING_ENABLED namespace scripting { - class Context; class Pool; - class Script; } +#endif + class DLL_LINKAGE CPrivilegedInfoCallback : public CGameInfoCallback { @@ -132,7 +133,9 @@ class DLL_LINKAGE IGameCallback : public CPrivilegedInfoCallback, public IGameEv public: virtual ~IGameCallback(){}; +#if SCRIPTING_ENABLED virtual scripting::Pool * getGlobalContextPool() const = 0; +#endif //get info virtual bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero); diff --git a/lib/ScriptHandler.cpp b/lib/ScriptHandler.cpp index ebca5ce26..955ca2852 100644 --- a/lib/ScriptHandler.cpp +++ b/lib/ScriptHandler.cpp @@ -11,6 +11,7 @@ #include "ScriptHandler.h" +#if SCRIPTING_ENABLED #include #include @@ -311,3 +312,4 @@ void ScriptHandler::saveState(JsonNode & state) } +#endif diff --git a/lib/ScriptHandler.h b/lib/ScriptHandler.h index 37da9e975..53d91110c 100644 --- a/lib/ScriptHandler.h +++ b/lib/ScriptHandler.h @@ -10,6 +10,7 @@ #pragma once +#if SCRIPTING_ENABLED #include #include "IHandlerBase.h" #include "JsonNode.h" @@ -131,3 +132,4 @@ private: }; } +#endif diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index 81fd2bd99..d79a9003a 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -82,10 +82,12 @@ const HeroTypeService * LibClasses::heroTypes() const return heroh; } +#if SCRIPTING_ENABLED const scripting::Service * LibClasses::scripts() const { return scriptHandler; } +#endif const spells::Service * LibClasses::spells() const { @@ -215,7 +217,9 @@ void LibClasses::init(bool onlyEssential) createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?) +#if SCRIPTING_ENABLED createHandler(scriptHandler, "Script", pomtime); +#endif createHandler(battlefieldsHandler, "Battlefields", pomtime); @@ -246,7 +250,9 @@ void LibClasses::clear() delete bth; delete tplh; delete terviewh; +#if SCRIPTING_ENABLED delete scriptHandler; +#endif delete battlefieldsHandler; makeNull(); } @@ -266,7 +272,9 @@ void LibClasses::makeNull() bth = nullptr; tplh = nullptr; terviewh = nullptr; +#if SCRIPTING_ENABLED scriptHandler = nullptr; +#endif battlefieldsHandler = nullptr; } @@ -287,10 +295,12 @@ void LibClasses::callWhenDeserializing() //modh->loadConfigFromFile ("defaultMods"); //TODO: remember last saved config } +#if SCRIPTING_ENABLED void LibClasses::scriptsLoaded() { scriptHandler->performRegistration(this); } +#endif LibClasses::~LibClasses() { diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index 774db316d..8a8a472ee 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -32,10 +32,13 @@ class CTerrainViewPatternConfig; class CRmgTemplateStorage; class IHandlerBase; +#if SCRIPTING_ENABLED namespace scripting { class ScriptHandler; } +#endif + /// Loads and constructs several handlers class DLL_LINKAGE LibClasses : public Services @@ -55,7 +58,9 @@ public: const FactionService * factions() const override; const HeroClassService * heroClasses() const override; const HeroTypeService * heroTypes() const override; +#if SCRIPTING_ENABLED const scripting::Service * scripts() const override; +#endif const spells::Service * spells() const override; const SkillService * skills() const override; const BattleFieldService * battlefields() const override; @@ -82,7 +87,9 @@ public: CRmgTemplateStorage * tplh; BattleFieldHandler * battlefieldsHandler; ObstacleHandler * obstacleHandler; +#if SCRIPTING_ENABLED scripting::ScriptHandler * scriptHandler; +#endif LibClasses(); //c-tor, loads .lods and NULLs handlers ~LibClasses(); @@ -92,15 +99,19 @@ public: void loadFilesystem(bool onlyEssential);// basic initialization. should be called before init() +#if SCRIPTING_ENABLED void scriptsLoaded(); +#endif template void serialize(Handler &h, const int version) { +#if SCRIPTING_ENABLED h & scriptHandler;//must be first (or second after modh), it can modify factories other handlers depends on if(!h.saving) { scriptsLoaded(); } +#endif h & heroh; h & arth; diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index f49d1e9a6..3ffa5d742 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -965,12 +965,14 @@ CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const return const_cast(CBattleInfoEssentials::battleGetFightingHero(side)); } +#if SCRIPTING_ENABLED scripting::Pool * BattleInfo::getContextPool() const { //this is real battle, use global scripting context pool //TODO: make this line not ugly return IObjectInterface::cb->getGlobalContextPool(); } +#endif bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b) { diff --git a/lib/battle/BattleInfo.h b/lib/battle/BattleInfo.h index 1d0c128cc..73a4ad0cc 100644 --- a/lib/battle/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -144,7 +144,9 @@ public: ui8 whatSide(PlayerColor player) const; protected: +#if SCRIPTING_ENABLED scripting::Pool * getContextPool() const override; +#endif }; diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index 146cf038d..b5cd714c3 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -23,13 +23,6 @@ struct CObstacleInstance; class IBonusBearer; class CRandomGenerator; -namespace scripting -{ - class Context; - class Pool; - class Script; -} - namespace spells { class Caster; diff --git a/lib/battle/IBattleInfoCallback.h b/lib/battle/IBattleInfoCallback.h index ad3d0e6e4..fcce0c7f2 100644 --- a/lib/battle/IBattleInfoCallback.h +++ b/lib/battle/IBattleInfoCallback.h @@ -24,15 +24,19 @@ namespace battle using UnitFilter = std::function; } +#if SCRIPTING_ENABLED namespace scripting { class Pool; } +#endif class DLL_LINKAGE IBattleInfoCallback { public: +#if SCRIPTING_ENABLED virtual scripting::Pool * getContextPool() const = 0; +#endif virtual Terrain battleTerrainType() const = 0; virtual BattleField battleGetBattlefieldType() const = 0; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index a80475041..29578ff56 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -720,10 +720,12 @@ const CreatureService * BaseMechanics::creatures() const return VLC->creatures(); //todo: redirect } +#if SCRIPTING_ENABLED const scripting::Service * BaseMechanics::scripts() const { return VLC->scripts(); //todo: redirect } +#endif const Service * BaseMechanics::spells() const { diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index de9546757..927a5a308 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -35,10 +35,12 @@ namespace vstd class RNG; } +#if SCRIPTING_ENABLED namespace scripting { class Service; } +#endif ///callback to be provided by server @@ -238,7 +240,9 @@ public: //Global environment facade virtual const CreatureService * creatures() const = 0; +#if SCRIPTING_ENABLED virtual const scripting::Service * scripts() const = 0; +#endif virtual const Service * spells() const = 0; virtual const IGameInfoCallback * game() const = 0; @@ -296,7 +300,9 @@ public: std::vector getTargetTypes() const override; const CreatureService * creatures() const override; +#if SCRIPTING_ENABLED const scripting::Service * scripts() const override; +#endif const Service * spells() const override; const IGameInfoCallback * game() const override; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5dcd2dcea..764051636 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1652,7 +1652,9 @@ CGameHandler::~CGameHandler() void CGameHandler::reinitScripting() { serverEventBus = make_unique(); +#if SCRIPTING_ENABLED serverScripts.reset(new scripting::PoolImpl(this, spellEnv)); +#endif } void CGameHandler::init(StartInfo *si) @@ -2112,7 +2114,9 @@ void CGameHandler::run(bool resume) logGlobal->info(sbuffer.str()); } +#if SCRIPTING_ENABLED services()->scripts()->run(serverScripts); +#endif if(resume) events::GameResumed::defaultExecute(serverEventBus.get()); @@ -7319,15 +7323,17 @@ CRandomGenerator & CGameHandler::getRandomGenerator() return CRandomGenerator::getDefault(); } +#if SCRIPTING_ENABLED scripting::Pool * CGameHandler::getGlobalContextPool() const { return serverScripts.get(); } -scripting::Pool * CGameHandler::getContextPool() const +scripting::Pool * CGameHandler::getContextPool() const { return serverScripts.get(); } +#endif const ObjectInstanceID CGameHandler::putNewObject(Obj ID, int subID, int3 pos) { diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 2c90edf6e..56726028b 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -34,10 +34,12 @@ class IMarket; class SpellCastEnvironment; +#if SCRIPTING_ENABLED namespace scripting { class PoolImpl; } +#endif template class CApplier; @@ -274,12 +276,14 @@ public: h & finishingBattle; h & getRandomGenerator(); +#if SCRIPTING_ENABLED JsonNode scriptsState; if(h.saving) serverScripts->serializeState(h.saving, scriptsState); h & scriptsState; if(!h.saving) serverScripts->serializeState(h.saving, scriptsState); +#endif } void sendMessageToAll(const std::string &message); @@ -326,13 +330,17 @@ public: CRandomGenerator & getRandomGenerator(); +#if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; scripting::Pool * getContextPool() const override; +#endif friend class CVCMIServer; private: std::unique_ptr serverEventBus; +#if SCRIPTING_ENABLED std::shared_ptr serverScripts; +#endif void reinitScripting(); From 667d97d300640e455929d3ecfda54f50bef960e6 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 22 Sep 2022 05:06:49 +0400 Subject: [PATCH 24/37] Add simple healthcheck --- client/CServerHandler.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index e0193efdc..dd3d0ebbc 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -161,6 +161,18 @@ void CServerHandler::startLocalServerAndConnect() threadRunLocalServer->join(); th->update(); + + try + { + CConnection testConnection(settings["server"]["server"].String(), getDefaultPort(), NAME, uuid); + logNetwork->error("Port is busy, kill other vcmiserver processes"); + return; + } + catch(...) + { + //no connection means that port is not busy and we can start local server + } + #ifdef VCMI_ANDROID { CAndroidVMHelper envHelper; From a1cf120ea66a7fdb1b682e612ca9b681d470713d Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:02:16 +0300 Subject: [PATCH 25/37] add missing virtual destructors --- AI/Nullkiller/Analyzers/ArmyManager.h | 1 + AI/Nullkiller/Analyzers/HeroManager.h | 4 +++- AI/Nullkiller/Engine/PriorityEvaluator.h | 1 + AI/Nullkiller/Pathfinding/Actions/SpecialAction.h | 4 +++- AI/Nullkiller/Pathfinding/Actors.h | 5 +++-- AI/VCAI/ArmyManager.h | 1 + CCallback.h | 2 ++ client/gui/SDL_Extensions.h | 1 + include/vcmi/ServerCallback.h | 2 ++ lib/CPathfinder.h | 4 ++++ 10 files changed, 21 insertions(+), 4 deletions(-) diff --git a/AI/Nullkiller/Analyzers/ArmyManager.h b/AI/Nullkiller/Analyzers/ArmyManager.h index 5d422aa9e..fddad9fcb 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.h +++ b/AI/Nullkiller/Analyzers/ArmyManager.h @@ -41,6 +41,7 @@ struct ArmyUpgradeInfo class DLL_EXPORT IArmyManager //: public: IAbstractManager { public: + virtual ~IArmyManager() = default; virtual void update() = 0; virtual ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const = 0; virtual ui64 howManyReinforcementsCanBuy( diff --git a/AI/Nullkiller/Analyzers/HeroManager.h b/AI/Nullkiller/Analyzers/HeroManager.h index 5ac745c02..8ade6cf7c 100644 --- a/AI/Nullkiller/Analyzers/HeroManager.h +++ b/AI/Nullkiller/Analyzers/HeroManager.h @@ -20,6 +20,7 @@ class DLL_EXPORT IHeroManager //: public: IAbstractManager { public: + virtual ~IHeroManager() = default; virtual const std::map & getHeroRoles() const = 0; virtual int selectBestSkill(const HeroPtr & hero, const std::vector & skills) const = 0; virtual HeroRole getHeroRole(const HeroPtr & hero) const = 0; @@ -31,6 +32,7 @@ public: class DLL_EXPORT ISecondarySkillRule { public: + virtual ~ISecondarySkillRule() = default; virtual void evaluateScore(const CGHeroInstance * hero, SecondarySkill skill, float & score) const = 0; }; @@ -102,4 +104,4 @@ private: public: void evaluateScore(const CGHeroInstance * hero, SecondarySkill skill, float & score) const override; -}; \ No newline at end of file +}; diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.h b/AI/Nullkiller/Engine/PriorityEvaluator.h index 5ce11de5b..6bcd09c32 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.h +++ b/AI/Nullkiller/Engine/PriorityEvaluator.h @@ -61,6 +61,7 @@ struct DLL_EXPORT EvaluationContext class IEvaluationContextBuilder { public: + virtual ~IEvaluationContextBuilder() = default; virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal goal) const = 0; }; diff --git a/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h b/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h index 6d7cbbbac..51977963b 100644 --- a/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h +++ b/AI/Nullkiller/Pathfinding/Actions/SpecialAction.h @@ -18,6 +18,8 @@ struct AIPathNode; class SpecialAction { public: + virtual ~SpecialAction() = default; + virtual bool canAct(const AIPathNode * source) const { return true; @@ -39,4 +41,4 @@ public: virtual std::string toString() const = 0; virtual const CGObjectInstance * targetObject() const { return nullptr; } -}; \ No newline at end of file +}; diff --git a/AI/Nullkiller/Pathfinding/Actors.h b/AI/Nullkiller/Pathfinding/Actors.h index ff2618df2..2f18df369 100644 --- a/AI/Nullkiller/Pathfinding/Actors.h +++ b/AI/Nullkiller/Pathfinding/Actors.h @@ -75,7 +75,8 @@ public: TResources armyCost; std::shared_ptr tiCache; - ChainActor(){} + ChainActor() = default; + virtual ~ChainActor() = default; virtual std::string toString() const; ExchangeResult tryExchangeNoLock(const ChainActor * other) const { return tryExchangeNoLock(this, other); } @@ -168,4 +169,4 @@ private: public: TownGarrisonActor(const CGTownInstance * town, uint64_t chainMask); virtual std::string toString() const override; -}; \ No newline at end of file +}; diff --git a/AI/VCAI/ArmyManager.h b/AI/VCAI/ArmyManager.h index 7579207da..5e29b41b1 100644 --- a/AI/VCAI/ArmyManager.h +++ b/AI/VCAI/ArmyManager.h @@ -28,6 +28,7 @@ struct SlotInfo class DLL_EXPORT IArmyManager //: public: IAbstractManager { public: + virtual ~IArmyManager() = default; virtual void init(CPlayerSpecificInfoCallback * CB) = 0; virtual void setAI(VCAI * AI) = 0; virtual bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const = 0; diff --git a/CCallback.h b/CCallback.h index 76f4413d0..4a1828917 100644 --- a/CCallback.h +++ b/CCallback.h @@ -35,6 +35,8 @@ struct ArtifactLocation; class IBattleCallback { public: + virtual ~IBattleCallback() = default; + bool waitTillRealize; //if true, request functions will return after they are realized by server bool unlockGsWhenWaiting;//if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback! //battle diff --git a/client/gui/SDL_Extensions.h b/client/gui/SDL_Extensions.h index b88054bfa..bc5401a37 100644 --- a/client/gui/SDL_Extensions.h +++ b/client/gui/SDL_Extensions.h @@ -155,6 +155,7 @@ typedef void (*BlitterWithRotationVal)(SDL_Surface *src,SDL_Rect srcRect, SDL_Su class ColorShifter { public: + virtual ~ColorShifter() = default; virtual SDL_Color shiftColor(SDL_Color clr) const = 0; }; diff --git a/include/vcmi/ServerCallback.h b/include/vcmi/ServerCallback.h index a9a722d4d..af35b4ea5 100644 --- a/include/vcmi/ServerCallback.h +++ b/include/vcmi/ServerCallback.h @@ -27,6 +27,8 @@ struct CatapultAttack; class DLL_LINKAGE ServerCallback { public: + virtual ~ServerCallback() = default; + virtual void complain(const std::string & problem) = 0; virtual bool describeChanges() const = 0; diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index 2bd7e5790..c047701a4 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -386,6 +386,9 @@ class DLL_LINKAGE INodeStorage { public: using ELayer = EPathfindingLayer; + + virtual ~INodeStorage() = default; + virtual std::vector getInitialNodes() = 0; virtual std::vector calculateNeighbours( @@ -448,6 +451,7 @@ public: PathfinderConfig( std::shared_ptr nodeStorage, std::vector> rules); + virtual ~PathfinderConfig() = default; virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) = 0; }; From aae1d4fed547fc1129f34ebc2625b882de8d2549 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:02:36 +0300 Subject: [PATCH 26/37] fix variable type in for-in loops --- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 2 +- AI/VCAI/ResourceManager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 4997fde20..cd4e05035 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -122,7 +122,7 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero { //No free slot, we might discard our weakest stack weakestStackPower = std::numeric_limits().max(); - for (const auto stack : slots) + for (const auto & stack : slots) { vstd::amin(weakestStackPower, stack.second->getPower()); } diff --git a/AI/VCAI/ResourceManager.cpp b/AI/VCAI/ResourceManager.cpp index 027b9e9c2..60289124e 100644 --- a/AI/VCAI/ResourceManager.cpp +++ b/AI/VCAI/ResourceManager.cpp @@ -121,7 +121,7 @@ Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o } float goalPriority = 10; //arbitrary, will be divided - for (const resPair & p : missingResources) + for (const resPair p : missingResources) { if (!income[p.first]) //prioritize resources with 0 income { From 3616827aad803033357b6aab3c204c273f6edb77 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:12:04 +0300 Subject: [PATCH 27/37] use default implementation of special methods --- AI/VCAI/AIhelper.cpp | 6 +----- AI/VCAI/AIhelper.h | 1 - lib/Terrain.cpp | 6 ------ lib/Terrain.h | 1 - 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/AI/VCAI/AIhelper.cpp b/AI/VCAI/AIhelper.cpp index 8e46d50f5..75fca29b2 100644 --- a/AI/VCAI/AIhelper.cpp +++ b/AI/VCAI/AIhelper.cpp @@ -19,10 +19,6 @@ AIhelper::AIhelper() armyManager.reset(new ArmyManager()); } -AIhelper::~AIhelper() -{ -} - bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal) { return resourceManager->notifyGoalCompleted(goal); @@ -182,4 +178,4 @@ std::vector::iterator AIhelper::getWeakestCreature(std::vector AIhelper::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const { return armyManager->getSortedSlots(target, source); -} \ No newline at end of file +} diff --git a/AI/VCAI/AIhelper.h b/AI/VCAI/AIhelper.h index 29d2bc94d..1a33025f0 100644 --- a/AI/VCAI/AIhelper.h +++ b/AI/VCAI/AIhelper.h @@ -36,7 +36,6 @@ class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, pu //TODO: vector public: AIhelper(); - ~AIhelper(); bool canAfford(const TResources & cost) const; TResources reservedResources() const override; diff --git a/lib/Terrain.cpp b/lib/Terrain.cpp index 142355961..7f89447d2 100644 --- a/lib/Terrain.cpp +++ b/lib/Terrain.cpp @@ -201,12 +201,6 @@ Terrain::operator std::string() const Terrain::Terrain(const std::string & _name) : name(_name) {} -Terrain& Terrain::operator=(const Terrain & _name) -{ - name = _name.name; - return *this; -} - Terrain& Terrain::operator=(const std::string & _name) { name = _name; diff --git a/lib/Terrain.h b/lib/Terrain.h index d270090e5..95fa343b0 100644 --- a/lib/Terrain.h +++ b/lib/Terrain.h @@ -74,7 +74,6 @@ public: int id() const; //TODO: has to be completely removed - Terrain& operator=(const Terrain & _type); Terrain& operator=(const std::string & _type); DLL_LINKAGE friend bool operator==(const Terrain & l, const Terrain & r); From 2ce78ba8c91a3d87ea2660a561e5fab6a8427441 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:12:37 +0300 Subject: [PATCH 28/37] fix operator precedence --- client/windows/GUIClasses.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 867a7abf0..3944be31a 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1286,7 +1286,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, int skill = hero->secSkills[g].first, level = hero->secSkills[g].second; // <1, 3> secSkillAreas[b].push_back(std::make_shared()); - secSkillAreas[b][g]->pos = genRect(32, 32, pos.x + 32 + g*36 + b*454 , pos.y + qeLayout ? 83 : 88); + secSkillAreas[b][g]->pos = genRect(32, 32, pos.x + 32 + g*36 + b*454 , pos.y + (qeLayout ? 83 : 88)); secSkillAreas[b][g]->baseType = 1; secSkillAreas[b][g]->type = skill; @@ -1301,12 +1301,12 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, heroAreas[b] = std::make_shared(257 + 228*b, 13, hero); specialtyAreas[b] = std::make_shared(); - specialtyAreas[b]->pos = genRect(32, 32, pos.x + 69 + 490*b, pos.y + qeLayout ? 41 : 45); + specialtyAreas[b]->pos = genRect(32, 32, pos.x + 69 + 490*b, pos.y + (qeLayout ? 41 : 45)); specialtyAreas[b]->hoverText = CGI->generaltexth->heroscrn[27]; specialtyAreas[b]->text = hero->type->specDescr; experienceAreas[b] = std::make_shared(); - experienceAreas[b]->pos = genRect(32, 32, pos.x + 105 + 490*b, pos.y + qeLayout ? 41 : 45); + experienceAreas[b]->pos = genRect(32, 32, pos.x + 105 + 490*b, pos.y + (qeLayout ? 41 : 45)); experienceAreas[b]->hoverText = CGI->generaltexth->heroscrn[9]; experienceAreas[b]->text = CGI->generaltexth->allTexts[2]; boost::algorithm::replace_first(experienceAreas[b]->text, "%d", boost::lexical_cast(hero->level)); @@ -1314,7 +1314,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, boost::algorithm::replace_first(experienceAreas[b]->text, "%d", boost::lexical_cast(hero->exp)); spellPointsAreas[b] = std::make_shared(); - spellPointsAreas[b]->pos = genRect(32, 32, pos.x + 141 + 490*b, pos.y + qeLayout ? 41 : 45); + spellPointsAreas[b]->pos = genRect(32, 32, pos.x + 141 + 490*b, pos.y + (qeLayout ? 41 : 45)); spellPointsAreas[b]->hoverText = CGI->generaltexth->heroscrn[22]; spellPointsAreas[b]->text = CGI->generaltexth->allTexts[205]; boost::algorithm::replace_first(spellPointsAreas[b]->text, "%s", hero->name); From 1d57c407407e57aba8bfffcb1bbd981c70f27677 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:22:14 +0300 Subject: [PATCH 29/37] wrap && conditions in parentheses when near || --- AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp | 8 ++++---- AI/Nullkiller/Analyzers/ObjectClusterizer.cpp | 2 +- AI/Nullkiller/Behaviors/DefenceBehavior.cpp | 12 ++++++------ AI/Nullkiller/Behaviors/StartupBehavior.cpp | 6 +++--- AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 2 +- server/CGameHandler.cpp | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp index ded809623..8192fd1a4 100644 --- a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp @@ -63,7 +63,7 @@ void DangerHitMapAnalyzer::updateHitMap() auto & node = hitMap[pos.x][pos.y][pos.z]; if(tileDanger > node.maximumDanger.danger - || tileDanger == node.maximumDanger.danger && node.maximumDanger.turn > turn) + || (tileDanger == node.maximumDanger.danger && node.maximumDanger.turn > turn)) { node.maximumDanger.danger = tileDanger; node.maximumDanger.turn = turn; @@ -71,7 +71,7 @@ void DangerHitMapAnalyzer::updateHitMap() } if(turn < node.fastestDanger.turn - || turn == node.fastestDanger.turn && node.fastestDanger.danger < tileDanger) + || (turn == node.fastestDanger.turn && node.fastestDanger.danger < tileDanger)) { node.fastestDanger.danger = tileDanger; node.fastestDanger.turn = turn; @@ -101,8 +101,8 @@ uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath & int turn = path.turn(); const HitMapNode & info = hitMap[tile.x][tile.y][tile.z]; - return info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger) - || info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger); + return (info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger)) + || (info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger)); } const HitMapNode & DangerHitMapAnalyzer::getObjectTreat(const CGObjectInstance * obj) const diff --git a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp index bc60f43ba..dacacf393 100644 --- a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp +++ b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp @@ -149,7 +149,7 @@ bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const const int3 pos = obj->visitablePos(); - if(obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->memory->alreadyVisited, obj) + if((obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->memory->alreadyVisited, obj)) || obj->wasVisited(ai->playerID)) { return false; diff --git a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp index 88263ecb1..da108da88 100644 --- a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp +++ b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp @@ -106,10 +106,10 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta { if(path.getHeroStrength() > treat.danger) { - if(path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger) - || path.exchangeCount == 1 && path.turn() < treat.turn + if((path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger)) + || (path.exchangeCount == 1 && path.turn() < treat.turn) || path.turn() < treat.turn - 1 - || path.turn() < treat.turn && treat.turn >= 2) + || (path.turn() < treat.turn && treat.turn >= 2)) { logAi->debug( "Hero %s can eliminate danger for town %s using path %s.", @@ -217,7 +217,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta // dismiss creatures we are not able to pick to be able to hide in garrison if(town->garrisonHero || town->getUpperArmy()->stacksCount() == 0 - || town->getUpperArmy()->getArmyStrength() < 500 && town->fortLevel() >= CGTownInstance::CITADEL) + || (town->getUpperArmy()->getArmyStrength() < 500 && town->fortLevel() >= CGTownInstance::CITADEL)) { tasks.push_back( Goals::sptr(Composition() @@ -228,7 +228,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta continue; } - if(treat.turn == 0 || path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger) + if(treat.turn == 0 || (path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)) { if(ai->nullkiller->arePathHeroesLocked(path)) { @@ -294,4 +294,4 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta } logAi->debug("Found %d tasks", tasks.size()); -} \ No newline at end of file +} diff --git a/AI/Nullkiller/Behaviors/StartupBehavior.cpp b/AI/Nullkiller/Behaviors/StartupBehavior.cpp index 4a1788c67..4c92cc382 100644 --- a/AI/Nullkiller/Behaviors/StartupBehavior.cpp +++ b/AI/Nullkiller/Behaviors/StartupBehavior.cpp @@ -55,7 +55,7 @@ const CGHeroInstance * getNearestHero(const CGTownInstance * town) if(shortestPath.nodes.size() > 1 || shortestPath.turn() != 0 || shortestPath.targetHero->visitablePos().dist2dSQ(town->visitablePos()) > 4 - || town->garrisonHero && shortestPath.targetHero == town->garrisonHero.get()) + || (town->garrisonHero && shortestPath.targetHero == town->garrisonHero.get())) return nullptr; return shortestPath.targetHero; @@ -76,7 +76,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown) for(auto obj : ai->nullkiller->objectClusterizer->getNearbyObjects()) { - if(obj->ID == Obj::RESOURCE && obj->subID == Res::GOLD + if((obj->ID == Obj::RESOURCE && obj->subID == Res::GOLD) || obj->ID == Obj::TREASURE_CHEST || obj->ID == Obj::CAMPFIRE || obj->ID == Obj::WATER_WHEEL) @@ -162,7 +162,7 @@ Goals::TGoalVec StartupBehavior::decompose() const auto garrisonHeroScore = ai->nullkiller->heroManager->evaluateHero(garrisonHero); if(visitingHeroScore > garrisonHeroScore - || ai->nullkiller->heroManager->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->nullkiller->heroManager->getHeroRole(visitingHero) == HeroRole::MAIN) + || (ai->nullkiller->heroManager->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->nullkiller->heroManager->getHeroRole(visitingHero) == HeroRole::MAIN)) { if(canRecruitHero || ai->nullkiller->armyManager->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200) { diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index a5e302557..39049acfb 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -558,7 +558,7 @@ bool AINodeStorage::selectNextActor() for(auto actor = actors.begin(); actor != actors.end(); actor++) { if(actor->get()->armyValue > currentActor->get()->armyValue - || actor->get()->armyValue == currentActor->get()->armyValue && actor <= currentActor) + || (actor->get()->armyValue == currentActor->get()->armyValue && actor <= currentActor)) { continue; } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5dcd2dcea..a901d48a9 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5585,7 +5585,7 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2) auto topArmy = dialog->exchangingArmies.at(0); auto bottomArmy = dialog->exchangingArmies.at(1); - if (topArmy == o1 && bottomArmy == o2 || bottomArmy == o1 && topArmy == o2) + if ((topArmy == o1 && bottomArmy == o2) || (bottomArmy == o1 && topArmy == o2)) return true; } } From 143b0983e65a8072e16ba4233e2939703b786e84 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:22:31 +0300 Subject: [PATCH 30/37] fix enum value --- AI/Nullkiller/Behaviors/StartupBehavior.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AI/Nullkiller/Behaviors/StartupBehavior.cpp b/AI/Nullkiller/Behaviors/StartupBehavior.cpp index 4c92cc382..f5f84187f 100644 --- a/AI/Nullkiller/Behaviors/StartupBehavior.cpp +++ b/AI/Nullkiller/Behaviors/StartupBehavior.cpp @@ -82,7 +82,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown) || obj->ID == Obj::WATER_WHEEL) { auto path = paths->getPathInfo(obj->visitablePos()); - if((path->accessible == CGPathNode::BLOCKVIS || path->accessible == CGPathNode::VISIT) + if((path->accessible == CGPathNode::BLOCKVIS || path->accessible == CGPathNode::VISITABLE) && path->reachable()) { treasureSourcesCount++; From c9c79887a74aca6a0606dc310e100737b0f4fae8 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:49:55 +0300 Subject: [PATCH 31/37] remove unused variables --- AI/Nullkiller/Analyzers/ArmyManager.cpp | 3 +-- AI/Nullkiller/Analyzers/BuildAnalyzer.cpp | 4 +--- AI/Nullkiller/Analyzers/HeroManager.h | 3 +-- AI/Nullkiller/Behaviors/BuildingBehavior.cpp | 2 -- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 10 +--------- AI/Nullkiller/Pathfinding/Actions/BoatActions.h | 5 +---- AI/Nullkiller/Pathfinding/Actors.cpp | 13 +------------ .../Rules/AIMovementAfterDestinationRule.cpp | 3 --- AI/VCAI/ResourceManager.cpp | 9 ++------- client/battle/CBattleInterface.cpp | 3 +-- lib/events/ApplyDamage.cpp | 4 +--- lib/events/ApplyDamage.h | 4 ---- lib/rmg/Zone.cpp | 6 +----- 13 files changed, 11 insertions(+), 58 deletions(-) diff --git a/AI/Nullkiller/Analyzers/ArmyManager.cpp b/AI/Nullkiller/Analyzers/ArmyManager.cpp index fef7fcce6..48e2f3169 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.cpp +++ b/AI/Nullkiller/Analyzers/ArmyManager.cpp @@ -204,7 +204,6 @@ std::shared_ptr ArmyManager::getArmyAvailableToBuyAsCCreatureSet( TResources availableRes) const { std::vector creaturesInDwellings; - int freeHeroSlots = GameConstants::ARMY_SIZE; auto army = std::make_shared(); for(int i = dwelling->creatures.size() - 1; i >= 0; i--) @@ -497,4 +496,4 @@ ArmyUpgradeInfo ArmyManager::calculateCreaturesUpgrade( } return result; -} \ No newline at end of file +} diff --git a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp index 0b168227c..72314e61d 100644 --- a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp @@ -129,8 +129,6 @@ void BuildAnalyzer::update() { logAi->trace("Checking town %s", town->name); - auto townInfo = town->town; - developmentInfos.push_back(TownDevelopmentInfo(town)); TownDevelopmentInfo & developmentInfo = developmentInfos.back(); @@ -399,4 +397,4 @@ std::string BuildingInfo::toString() const + ", creature: " + std::to_string(creatureGrows) + " x " + std::to_string(creatureLevel) + " x " + creatureCost.toString() + ", daily: " + dailyIncome.toString(); -} \ No newline at end of file +} diff --git a/AI/Nullkiller/Analyzers/HeroManager.h b/AI/Nullkiller/Analyzers/HeroManager.h index 8ade6cf7c..8248a2b0b 100644 --- a/AI/Nullkiller/Analyzers/HeroManager.h +++ b/AI/Nullkiller/Analyzers/HeroManager.h @@ -54,11 +54,10 @@ private: static SecondarySkillEvaluator scountSkillsScores; CCallback * cb; //this is enough, but we downcast from CCallback - const Nullkiller * ai; std::map heroRoles; public: - HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB), ai(ai) {} + HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB) {} const std::map & getHeroRoles() const override; HeroRole getHeroRole(const HeroPtr & hero) const override; int selectBestSkill(const HeroPtr & hero, const std::vector & skills) const override; diff --git a/AI/Nullkiller/Behaviors/BuildingBehavior.cpp b/AI/Nullkiller/Behaviors/BuildingBehavior.cpp index 22386683e..5fc50c213 100644 --- a/AI/Nullkiller/Behaviors/BuildingBehavior.cpp +++ b/AI/Nullkiller/Behaviors/BuildingBehavior.cpp @@ -53,8 +53,6 @@ Goals::TGoalVec BuildingBehavior::decompose() const for(auto & developmentInfo : developmentInfos) { - auto town = developmentInfo.town; - for(auto & buildingInfo : developmentInfo.toBuild) { if(goldPreasure < MAX_GOLD_PEASURE || buildingInfo.dailyIncome[Res::GOLD] > 0) diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index cd4e05035..58f97b837 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -645,7 +645,6 @@ public: } auto heroPtr = task->hero; - auto day = ai->cb->getDate(Date::DAY); auto hero = heroPtr.get(ai->cb.get()); bool checkGold = evaluationContext.danger == 0; auto army = path.heroArmy; @@ -670,11 +669,8 @@ public: class ClusterEvaluationContextBuilder : public IEvaluationContextBuilder { -private: - const Nullkiller * ai; - public: - ClusterEvaluationContextBuilder(const Nullkiller * ai) : ai(ai) {} + ClusterEvaluationContextBuilder(const Nullkiller * ai) {} virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override { @@ -699,7 +695,6 @@ public: for(auto objInfo : objects) { auto target = objInfo.first; - auto day = ai->cb->getDate(Date::DAY); bool checkGold = objInfo.second.danger == 0; auto army = hero; @@ -718,9 +713,6 @@ public: if(boost > 8) break; } - - const AIPath & pathToCenter = clusterGoal.getPathToCenter(); - } }; diff --git a/AI/Nullkiller/Pathfinding/Actions/BoatActions.h b/AI/Nullkiller/Pathfinding/Actions/BoatActions.h index 0db5ff781..4b2812b21 100644 --- a/AI/Nullkiller/Pathfinding/Actions/BoatActions.h +++ b/AI/Nullkiller/Pathfinding/Actions/BoatActions.h @@ -24,9 +24,6 @@ namespace AIPathfinding class SummonBoatAction : public VirtualBoatAction { - private: - const CGHeroInstance * hero; - public: virtual void execute(const CGHeroInstance * hero) const override; @@ -71,4 +68,4 @@ namespace AIPathfinding virtual const CGObjectInstance * targetObject() const override; }; -} \ No newline at end of file +} diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp index 68d10fecc..e6806a0b2 100644 --- a/AI/Nullkiller/Pathfinding/Actors.cpp +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -269,8 +269,6 @@ ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other) return result; // already inserted } - auto position = inserted.first; - auto differentMasks = (actor->chainMask & other->chainMask) == 0; if(!differentMasks) return result; @@ -461,15 +459,6 @@ CCreatureSet * DwellingActor::getDwellingCreatures(const CGDwelling * dwelling, continue; auto creature = creatureInfo.second.back().toCreature(); - auto count = creatureInfo.first; - - if(waitForGrowth) - { - const CGTownInstance * town = dynamic_cast(dwelling); - - count += town ? town->creatureGrowth(creature->level) : creature->growth; - } - dwellingCreatures->addToSlot( dwellingCreatures->getSlotFor(creature), creature->idNumber, @@ -487,4 +476,4 @@ TownGarrisonActor::TownGarrisonActor(const CGTownInstance * town, uint64_t chain std::string TownGarrisonActor::toString() const { return town->name; -} \ No newline at end of file +} diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp index 8ffcf1be7..68aab0c35 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp @@ -126,7 +126,6 @@ namespace AIPathfinding const AIPathNode * destinationNode = nodeStorage->getAINode(destination.node); auto questObj = dynamic_cast(destination.nodeObject); auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord); - auto nodeHero = pathfinderHelper->hero; QuestAction questAction(questInfo); if(destination.nodeObject->ID == Obj::QUEST_GUARD && questObj->quest->missionType == CQuest::MISSION_NONE) @@ -157,8 +156,6 @@ namespace AIPathfinding nodeStorage->updateAINode(destination.node, [&](AIPathNode * node) { - auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord); - node->specialAction.reset(new QuestAction(questAction)); }); } diff --git a/AI/VCAI/ResourceManager.cpp b/AI/VCAI/ResourceManager.cpp index 60289124e..d676c4488 100644 --- a/AI/VCAI/ResourceManager.cpp +++ b/AI/VCAI/ResourceManager.cpp @@ -120,14 +120,12 @@ Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o return o.goal; } - float goalPriority = 10; //arbitrary, will be divided for (const resPair p : missingResources) { if (!income[p.first]) //prioritize resources with 0 income { resourceType = p.first; amountToCollect = p.second; - goalPriority /= amountToCollect; //need more resources -> lower priority break; } } @@ -138,7 +136,7 @@ Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o std::map daysToEarn; for (auto it : missingResources) daysToEarn[it.first] = (float)missingResources[it.first] / income[it.first]; - auto incomeComparer = [&income](const timePair & lhs, const timePair & rhs) -> bool + auto incomeComparer = [](const timePair & lhs, const timePair & rhs) -> bool { //theoretically income can be negative, but that falls into this comparison return lhs.second < rhs.second; @@ -146,12 +144,9 @@ Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o resourceType = boost::max_element(daysToEarn, incomeComparer)->first; amountToCollect = missingResources[resourceType]; - goalPriority /= daysToEarn[resourceType]; //more days - lower priority } - if (resourceType == Res::GOLD) - goalPriority *= 1000; - //this is abstract goal and might take soem time to complete + //this is abstract goal and might take some time to complete return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect).setisAbstract(true)); } diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 50d41a8c3..016d161dd 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1083,11 +1083,10 @@ void CBattleInterface::stacksAreAttacked(std::vector attacked std::array killedBySide = {0, 0}; - int targets = 0, damage = 0; + int targets = 0; for(const StackAttackedInfo & attackedInfo : attackedInfos) { ++targets; - damage += (int)attackedInfo.dmg; ui8 side = attackedInfo.defender->side; killedBySide.at(side) += attackedInfo.amountKilled; diff --git a/lib/events/ApplyDamage.cpp b/lib/events/ApplyDamage.cpp index 8ba3de747..93b387899 100644 --- a/lib/events/ApplyDamage.cpp +++ b/lib/events/ApplyDamage.cpp @@ -25,10 +25,8 @@ SubscriptionRegistry * ApplyDamage::getRegistry() } CApplyDamage::CApplyDamage(const Environment * env_, BattleStackAttacked * pack_, std::shared_ptr target_) - : env(env_), - pack(pack_), + : pack(pack_), target(target_) - { initalDamage = pack->damageAmount; } diff --git a/lib/events/ApplyDamage.h b/lib/events/ApplyDamage.h index c56d730fe..178d12224 100644 --- a/lib/events/ApplyDamage.h +++ b/lib/events/ApplyDamage.h @@ -28,12 +28,8 @@ public: private: int64_t initalDamage; - const Environment * env; BattleStackAttacked * pack; std::shared_ptr target; }; } - - - diff --git a/lib/rmg/Zone.cpp b/lib/rmg/Zone.cpp index 7323a27fd..6b2f977f2 100644 --- a/lib/rmg/Zone.cpp +++ b/lib/rmg/Zone.cpp @@ -193,11 +193,7 @@ void Zone::fractalize() rmg::Area clearedTiles(dAreaFree); rmg::Area possibleTiles(dAreaPossible); rmg::Area tilesToIgnore; //will be erased in this iteration - - //the more treasure density, the greater distance between paths. Scaling is experimental. - int totalDensity = 0; - for(auto ti : treasureInfo) - totalDensity += ti.density; + const float minDistance = 10 * 10; //squared if(type != ETemplateZoneType::JUNCTION) From 635d8c78835f8c30131e0bc2f235cd9b73590410 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Thu, 22 Sep 2022 11:53:09 +0300 Subject: [PATCH 32/37] silence varargs warning from fuzzylite --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb6cf8e6a..e123f932d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,6 +192,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR NOT WIN32) #so far all *nix compilers support suc set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing -Wno-switch -Wno-sign-compare -Wno-unused-local-typedefs") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-overloaded-virtual -Wno-type-limits -Wno-unknown-pragmas") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-varargs") # fuzzylite - Operation.h if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-mismatched-tags -Wno-unknown-warning-option -Wno-missing-braces") From c2f5628fc5282d921ea289433948275b424e9184 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 22 Sep 2022 15:39:43 +0400 Subject: [PATCH 33/37] Add system message # Conflicts: # client/CServerHandler.cpp --- client/CServerHandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index dd3d0ebbc..12f2b9f76 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -17,6 +17,7 @@ #include "lobby/CSelectionBase.h" #include "lobby/CLobbyScreen.h" +#include "windows/InfoWindows.h" #include "mainmenu/CMainMenu.h" @@ -166,6 +167,7 @@ void CServerHandler::startLocalServerAndConnect() { CConnection testConnection(settings["server"]["server"].String(), getDefaultPort(), NAME, uuid); logNetwork->error("Port is busy, kill other vcmiserver processes"); + CInfoWindow::showInfoDialog("Port is busy, kill other vcmiserver processes", CInfoWindow::TCompsInfo(), PlayerColor(1)); return; } catch(...) From 145103a32d681cc83c0278efd703069d6232e323 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 22 Sep 2022 16:41:13 +0400 Subject: [PATCH 34/37] Allow enabling of compatible mods only --- launcher/modManager/cmodlist.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index 65ad05988..5bc73743a 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -284,6 +284,15 @@ CModEntry CModList::getMod(QString modname) const } } + if(settings.value("active").toBool()) + { + auto compatibility = local.value("compatibility").toMap(); + if(compatibility["min"].isValid() || compatibility["max"].isValid()) + if(!isCompatible(compatibility["min"].toString(), compatibility["max"].toString())) + settings["active"] = false; + } + + for(auto entry : repositories) { QVariant repoVal = getValue(entry, path); From 2c7198a01a1377967df9919f7babfdc61f53b857 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 22 Sep 2022 16:41:23 +0400 Subject: [PATCH 35/37] Increment vcmi version --- ChangeLog | 20 +++++++++++++++++++- lib/GameConstants.cpp | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 57c7f4e37..d33b1499d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,22 @@ -0.99 -> 1.0 +1.0.0 -> 1.1.0 + +GENERAL: +* Mods and their versions and serialized into save files. Game checks mod compatibility before loading +* Logs are stored in system default logs directory +* LUA/ERM libs are not compiled by default +* FFMpeg dependency is optional now + +MODS: +* Supported rewardable objects customization +* Battleground obstacles are extendable now with VLC mechanism +* Introduced "compatibility" section into mods settings + +LAUNCHER: +* Fixed problem with duplicated mods in the list +* Launcher shows compatible mods only +* Uninstall button was moved to the left of layout + +0.99 -> 1.0.0 GENERAL: * Spectator mode was implemented through command-line options diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index ec368869e..7f6155316 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -52,7 +52,7 @@ 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_MINOR = 1; const int VCMI_VERSION_PATCH = 0; const std::string VCMI_VERSION_STRING = std::to_string(VCMI_VERSION_MAJOR) + "." + From 7de4d94aff076ea647c2fff0e2658ed8e257ab86 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Thu, 22 Sep 2022 20:06:14 +0400 Subject: [PATCH 36/37] Move text string to translations.json --- client/CServerHandler.cpp | 5 +++-- config/translate.json | 31 +++++++++++++++++++------------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 12f2b9f76..91532bbe9 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -163,11 +163,12 @@ void CServerHandler::startLocalServerAndConnect() th->update(); + auto errorMsg = CGI->generaltexth->localizedTexts["server"]["errors"]["existingProcess"].String(); try { CConnection testConnection(settings["server"]["server"].String(), getDefaultPort(), NAME, uuid); - logNetwork->error("Port is busy, kill other vcmiserver processes"); - CInfoWindow::showInfoDialog("Port is busy, kill other vcmiserver processes", CInfoWindow::TCompsInfo(), PlayerColor(1)); + logNetwork->error("Port is busy, check if another instance of vcmiserver is working"); + CInfoWindow::showInfoDialog(errorMsg, {}); return; } catch(...) diff --git a/config/translate.json b/config/translate.json index 0071d4971..757bfb9d0 100644 --- a/config/translate.json +++ b/config/translate.json @@ -23,6 +23,13 @@ }, "playerAttacked" : "Player has been attacked: %s" }, + "server" : + { + "errors" : + { + "existingProcess" : "Another vcmiserver process is running, please terminate it first" + } + }, "systemOptions" : { "fullscreenButton" : @@ -62,18 +69,18 @@ "allOf" : "All of the following:", "noneOf" : "None of the following:" }, - "heroWindow": - { - "openCommander": - { - "label": "Open commander window", - "help": "Displays information about commander of this hero" - } - }, - "commanderWindow": - { - "artifactMessage": "Do you want to give this artifact back to hero?" - }, + "heroWindow" : + { + "openCommander" : + { + "label" : "Open commander window", + "help" : "Displays information about commander of this hero" + } + }, + "commanderWindow": + { + "artifactMessage": "Do you want to give this artifact back to hero?" + }, "creatureWindow" : { "showBonuses" : From 3101a207a0b4a7637fb5b8e35ab6f6cfd26ad050 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 23 Sep 2022 12:34:44 +0400 Subject: [PATCH 37/37] hotfix for broken game loading --- lib/CModHandler.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/CModHandler.h b/lib/CModHandler.h index de0f4144d..1605f59fd 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -371,6 +371,7 @@ public: } else { + loadMods(); std::vector newActiveMods; h & newActiveMods; for(auto & m : newActiveMods)