From 0901c8feaf41d48bf69125fe26019eeca020623e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 6 Jun 2023 16:32:26 +0300 Subject: [PATCH 01/12] Use boost::multiarray instead of manual memory management in CMap --- lib/mapping/CMap.cpp | 38 +++++--------------------------- lib/mapping/CMap.h | 52 +++++--------------------------------------- 2 files changed, 11 insertions(+), 79 deletions(-) diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 09eb206f0..dc6e48864 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -129,9 +129,10 @@ bool TerrainTile::isWater() const } CMap::CMap() - : checksum(0), grailPos(-1, -1, -1), grailRadius(0), terrain(nullptr), - guardingCreaturePositions(nullptr), - uidCounter(0) + : checksum(0) + , grailPos(-1, -1, -1) + , grailRadius(0) + , uidCounter(0) { allHeroes.resize(allowedHeroes.size()); allowedAbilities = VLC->skillh->getDefaultAllowed(); @@ -142,22 +143,6 @@ CMap::CMap() CMap::~CMap() { getEditManager()->getUndoManager().clearAll(); - - if(terrain) - { - for(int z = 0; z < levels(); z++) - { - for(int x = 0; x < width; x++) - { - delete[] terrain[z][x]; - delete[] guardingCreaturePositions[z][x]; - } - delete[] terrain[z]; - delete[] guardingCreaturePositions[z]; - } - delete [] terrain; - delete [] guardingCreaturePositions; - } for(auto obj : objects) obj.dellNull(); @@ -572,19 +557,8 @@ void CMap::removeObject(CGObjectInstance * obj) void CMap::initTerrain() { - int level = levels(); - terrain = new TerrainTile**[level]; - guardingCreaturePositions = new int3**[level]; - for(int z = 0; z < level; ++z) - { - terrain[z] = new TerrainTile*[width]; - guardingCreaturePositions[z] = new int3*[width]; - for(int x = 0; x < width; ++x) - { - terrain[z][x] = new TerrainTile[height]; - guardingCreaturePositions[z][x] = new int3[height]; - } - } + terrain.resize(boost::extents[levels()][width][height]); + guardingCreaturePositions.resize(boost::extents[levels()][width][height]); } CMapEditManager * CMap::getEditManager() diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 1638ff3b3..58a3568d2 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -142,14 +142,14 @@ public: std::map questIdentifierToId; std::unique_ptr editManager; - - int3 ***guardingCreaturePositions; + boost::multi_array guardingCreaturePositions; std::map > instanceNames; private: /// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground - TerrainTile*** terrain; + boost::multi_array terrain; + si32 uidCounter; //TODO: initialize when loading an old map public: @@ -170,50 +170,8 @@ public: h & questIdentifierToId; //TODO: viccondetails - const int level = levels(); - if(h.saving) - { - // Save terrain - for(int z = 0; z < level; ++z) - { - for(int x = 0; x < width; ++x) - { - for(int y = 0; y < height; ++y) - { - h & terrain[z][x][y]; - h & guardingCreaturePositions[z][x][y]; - } - } - } - } - else - { - // Load terrain - terrain = new TerrainTile**[level]; - guardingCreaturePositions = new int3**[level]; - for(int z = 0; z < level; ++z) - { - terrain[z] = new TerrainTile*[width]; - guardingCreaturePositions[z] = new int3*[width]; - for(int x = 0; x < width; ++x) - { - terrain[z][x] = new TerrainTile[height]; - guardingCreaturePositions[z][x] = new int3[height]; - } - } - for(int z = 0; z < level; ++z) - { - for(int x = 0; x < width; ++x) - { - for(int y = 0; y < height; ++y) - { - - h & terrain[z][x][y]; - h & guardingCreaturePositions[z][x][y]; - } - } - } - } + h & terrain; + h & guardingCreaturePositions; h & objects; h & heroesOnMap; From 5cfbdd2967b4bd5314d0a6bd681e78ad8d2d02b7 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 6 Jun 2023 18:32:53 +0300 Subject: [PATCH 02/12] Partially split CommonConstructors.cpp into few separate files --- AI/Nullkiller/Engine/FuzzyHelper.cpp | 2 +- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 2 +- AI/VCAI/FuzzyHelper.cpp | 2 +- cmake_modules/VCMI_lib.cmake | 3 + .../CBankInstanceConstructor.cpp | 255 ++++++++++++++++++ .../CBankInstanceConstructor.h | 105 ++++++++ .../CDefaultObjectTypeHandler.h | 52 ++++ .../CObjectClassesHandler.cpp | 1 + .../CommonConstructors.cpp | 239 +--------------- .../CommonConstructors.h | 123 +-------- lib/mapObjects/CBank.cpp | 2 +- lib/registerTypes/RegisterTypes.h | 1 + 12 files changed, 423 insertions(+), 364 deletions(-) create mode 100644 lib/mapObjectConstructors/CBankInstanceConstructor.cpp create mode 100644 lib/mapObjectConstructors/CBankInstanceConstructor.h create mode 100644 lib/mapObjectConstructors/CDefaultObjectTypeHandler.h diff --git a/AI/Nullkiller/Engine/FuzzyHelper.cpp b/AI/Nullkiller/Engine/FuzzyHelper.cpp index 0815f4767..f9ac898dc 100644 --- a/AI/Nullkiller/Engine/FuzzyHelper.cpp +++ b/AI/Nullkiller/Engine/FuzzyHelper.cpp @@ -15,7 +15,7 @@ #include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h" #include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h" -#include "../../../lib/mapObjectConstructors/CommonConstructors.h" +#include "../../../lib/mapObjectConstructors/CBankInstanceConstructor.h" namespace NKAI { diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 902470d97..1e8790f64 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -13,7 +13,7 @@ #include "Nullkiller.h" #include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h" #include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h" -#include "../../../lib/mapObjectConstructors/CommonConstructors.h" +#include "../../../lib/mapObjectConstructors/CBankInstanceConstructor.h" #include "../../../lib/mapObjects/MapObjects.h" #include "../../../lib/CCreatureHandler.h" #include "../../../lib/CPathfinder.h" diff --git a/AI/VCAI/FuzzyHelper.cpp b/AI/VCAI/FuzzyHelper.cpp index 2f2a75d67..1a7d1dc45 100644 --- a/AI/VCAI/FuzzyHelper.cpp +++ b/AI/VCAI/FuzzyHelper.cpp @@ -15,7 +15,7 @@ #include "../../lib/mapObjectConstructors/AObjectTypeHandler.h" #include "../../lib/mapObjectConstructors/CObjectClassesHandler.h" -#include "../../lib/mapObjectConstructors/CommonConstructors.h" +#include "../../lib/mapObjectConstructors/CBankInstanceConstructor.h" #include "../../lib/mapObjects/CBank.h" #include "../../lib/mapObjects/CGDwelling.h" diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index b7b7069be..888e01b17 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -66,6 +66,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/logging/CLogger.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/AObjectTypeHandler.cpp + ${MAIN_LIB_DIR}/mapObjectConstructors/CBankInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.cpp @@ -369,6 +370,8 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/logging/CLogger.h ${MAIN_LIB_DIR}/mapObjectConstructors/AObjectTypeHandler.h + ${MAIN_LIB_DIR}/mapObjectConstructors/CBankInstanceConstructor.h + ${MAIN_LIB_DIR}/mapObjectConstructors/CDefaultObjectTypeHandler.h ${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.h ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.h ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.h diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.cpp b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp new file mode 100644 index 000000000..fb55d43f1 --- /dev/null +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp @@ -0,0 +1,255 @@ +/* +* CBankInstanceConstructor.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#include "StdInc.h" +#include "CBankInstanceConstructor.h" + +#include "../JsonRandom.h" +#include "../CGeneralTextHandler.h" +#include "../IGameCallback.h" + +VCMI_LIB_NAMESPACE_BEGIN + +bool CBankInstanceConstructor::hasNameTextID() const +{ + return true; +} + +void CBankInstanceConstructor::initTypeData(const JsonNode & input) +{ + if (input.Struct().count("name") == 0) + logMod->warn("Bank %s missing name!", getJsonKey()); + + VLC->generaltexth->registerString(input.meta, getNameTextID(), input["name"].String()); + + levels = input["levels"].Vector(); + bankResetDuration = static_cast(input["resetDuration"].Float()); +} + +CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr tmpl) const +{ + return createTyped(tmpl); +} + +BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const +{ + BankConfig bc; + + bc.chance = static_cast(level["chance"].Float()); + + bc.guards = JsonRandom::loadCreatures(level["guards"], rng); + bc.upgradeChance = static_cast(level["upgrade_chance"].Float()); + bc.combatValue = static_cast(level["combat_value"].Float()); + + std::vector spells; + IObjectInterface::cb->getAllowedSpells(spells); + + bc.resources = ResourceSet(level["reward"]["resources"]); + bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng); + bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng); + bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells); + + bc.value = static_cast(level["value"].Float()); + + return bc; +} + +void CBankInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +{ + auto * bank = dynamic_cast(object); + + bank->resetDuration = bankResetDuration; + + si32 totalChance = 0; + for(const auto & node : levels) + totalChance += static_cast(node["chance"].Float()); + + assert(totalChance != 0); + + si32 selectedChance = rng.nextInt(totalChance - 1); + + int cumulativeChance = 0; + for(const auto & node : levels) + { + cumulativeChance += static_cast(node["chance"].Float()); + if(selectedChance < cumulativeChance) + { + bank->setConfig(generateConfig(node, rng)); + break; + } + } +} + +CBankInfo::CBankInfo(const JsonVector & Config) : + config(Config) +{ + assert(!Config.empty()); +} + +static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature * crea, si32 amount) +{ + army.totalStrength += crea->getFightValue() * amount; + + bool walker = true; + if(crea->hasBonusOfType(BonusType::SHOOTER)) + { + army.shootersStrength += crea->getFightValue() * amount; + walker = false; + } + if(crea->hasBonusOfType(BonusType::FLYING)) + { + army.flyersStrength += crea->getFightValue() * amount; + walker = false; + } + if(walker) + army.walkersStrength += crea->getFightValue() * amount; +} + +IObjectInfo::CArmyStructure CBankInfo::minGuards() const +{ + std::vector armies; + for(auto configEntry : config) + { + auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); + IObjectInfo::CArmyStructure army; + for(auto & stack : stacks) + { + assert(!stack.allowedCreatures.empty()); + auto weakest = boost::range::min_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b) + { + return a->getFightValue() < b->getFightValue(); + }); + addStackToArmy(army, *weakest, stack.minAmount); + } + armies.push_back(army); + } + return *boost::range::min_element(armies); +} + +IObjectInfo::CArmyStructure CBankInfo::maxGuards() const +{ + std::vector armies; + for(auto configEntry : config) + { + auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); + IObjectInfo::CArmyStructure army; + for(auto & stack : stacks) + { + assert(!stack.allowedCreatures.empty()); + auto strongest = boost::range::max_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b) + { + return a->getFightValue() < b->getFightValue(); + }); + addStackToArmy(army, *strongest, stack.maxAmount); + } + armies.push_back(army); + } + return *boost::range::max_element(armies); +} + +TPossibleGuards CBankInfo::getPossibleGuards() const +{ + TPossibleGuards out; + + for(const JsonNode & configEntry : config) + { + const JsonNode & guardsInfo = configEntry["guards"]; + auto stacks = JsonRandom::evaluateCreatures(guardsInfo); + IObjectInfo::CArmyStructure army; + + + for(auto stack : stacks) + { + army.totalStrength += stack.allowedCreatures.front()->getAIValue() * (stack.minAmount + stack.maxAmount) / 2; + //TODO: add fields for flyers, walkers etc... + } + + ui8 chance = static_cast(configEntry["chance"].Float()); + out.push_back(std::make_pair(chance, army)); + } + return out; +} + +std::vector> CBankInfo::getPossibleResourcesReward() const +{ + std::vector> result; + + for(const JsonNode & configEntry : config) + { + const JsonNode & resourcesInfo = configEntry["reward"]["resources"]; + + if(!resourcesInfo.isNull()) + { + result.emplace_back(configEntry["chance"].Integer(), TResources(resourcesInfo)); + } + } + + return result; +} + +std::vector> CBankInfo::getPossibleCreaturesReward() const +{ + std::vector> aproximateReward; + + for(const JsonNode & configEntry : config) + { + const JsonNode & guardsInfo = configEntry["reward"]["creatures"]; + auto stacks = JsonRandom::evaluateCreatures(guardsInfo); + + for(auto stack : stacks) + { + const auto * creature = stack.allowedCreatures.front(); + + aproximateReward.emplace_back(configEntry["chance"].Integer(), CStackBasicDescriptor(creature, (stack.minAmount + stack.maxAmount) / 2)); + } + } + + return aproximateReward; +} + +bool CBankInfo::givesResources() const +{ + for(const JsonNode & node : config) + if(!node["reward"]["resources"].isNull()) + return true; + return false; +} + +bool CBankInfo::givesArtifacts() const +{ + for(const JsonNode & node : config) + if(!node["reward"]["artifacts"].isNull()) + return true; + return false; +} + +bool CBankInfo::givesCreatures() const +{ + for(const JsonNode & node : config) + if(!node["reward"]["creatures"].isNull()) + return true; + return false; +} + +bool CBankInfo::givesSpells() const +{ + for(const JsonNode & node : config) + if(!node["reward"]["spells"].isNull()) + return true; + return false; +} + + +std::unique_ptr CBankInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const +{ + return std::unique_ptr(new CBankInfo(levels)); +} + + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.h b/lib/mapObjectConstructors/CBankInstanceConstructor.h new file mode 100644 index 000000000..232e61676 --- /dev/null +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.h @@ -0,0 +1,105 @@ +/* +* CBankInstanceConstructor.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#pragma once + +#include "CDefaultObjectTypeHandler.h" +#include "IObjectInfo.h" + +#include "../CCreatureSet.h" +#include "../ResourceSet.h" +#include "../mapObjects/CBank.h" + +VCMI_LIB_NAMESPACE_BEGIN + +struct BankConfig +{ + ui32 value = 0; //overall value of given things + ui32 chance = 0; //chance for this level being chosen + ui32 upgradeChance = 0; //chance for creatures to be in upgraded versions + ui32 combatValue = 0; //how hard are guards of this level + std::vector guards; //creature ID, amount + ResourceSet resources; //resources given in case of victory + std::vector creatures; //creatures granted in case of victory (creature ID, amount) + std::vector artifacts; //artifacts given in case of victory + std::vector spells; // granted spell(s), for Pyramid + + template void serialize(Handler &h, const int version) + { + h & chance; + h & upgradeChance; + h & guards; + h & combatValue; + h & resources; + h & creatures; + h & artifacts; + h & value; + h & spells; + } +}; + +using TPossibleGuards = std::vector>; + +template +struct DLL_LINKAGE PossibleReward +{ + int chance; + T data; + + PossibleReward(int chance, const T & data) : chance(chance), data(data) {} +}; + +class DLL_LINKAGE CBankInfo : public IObjectInfo +{ + const JsonVector & config; +public: + CBankInfo(const JsonVector & Config); + + TPossibleGuards getPossibleGuards() const; + std::vector> getPossibleResourcesReward() const; + std::vector> getPossibleCreaturesReward() const; + + // These functions should try to evaluate minimal possible/max possible guards to give provide information on possible thread to AI + CArmyStructure minGuards() const override; + CArmyStructure maxGuards() const override; + + bool givesResources() const override; + bool givesArtifacts() const override; + bool givesCreatures() const override; + bool givesSpells() const override; +}; + +class CBankInstanceConstructor : public CDefaultObjectTypeHandler +{ + BankConfig generateConfig(const JsonNode & conf, CRandomGenerator & rng) const; + + JsonVector levels; +protected: + void initTypeData(const JsonNode & input) override; + +public: + // all banks of this type will be reset N days after clearing, + si32 bankResetDuration = 0; + + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + + bool hasNameTextID() const override; + + std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + + template void serialize(Handler &h, const int version) + { + h & levels; + h & bankResetDuration; + h & static_cast&>(*this); + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h new file mode 100644 index 000000000..6b055e288 --- /dev/null +++ b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h @@ -0,0 +1,52 @@ +/* +* CDefaultObjectTypeHandler.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#pragma once + +#include "AObjectTypeHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +/// Class that is used for objects that do not have dedicated handler +template +class CDefaultObjectTypeHandler : public AObjectTypeHandler +{ +protected: + ObjectType * createTyped(std::shared_ptr tmpl /* = nullptr */) const + { + auto obj = new ObjectType(); + preInitObject(obj); + + //Set custom template or leave null + if (tmpl) + { + obj->appearance = tmpl; + } + + return obj; + } +public: + CDefaultObjectTypeHandler() {} + + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override + { + return createTyped(tmpl); + } + + virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override + { + } + + virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override + { + return nullptr; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 063f6b173..57bd7392f 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -23,6 +23,7 @@ #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" +#include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../mapObjects/CQuest.h" #include "../mapObjects/CGPandoraBox.h" #include "../mapObjects/ObjectTemplate.h" diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index 434cf2083..7f7a5464e 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -22,7 +22,7 @@ #include "../mapObjects/CGTownInstance.h" #include "../mapObjects/ObjectTemplate.h" #include "../mapping/CMapDefines.h" -#include "JsonRandom.h" +#include "../JsonRandom.h" VCMI_LIB_NAMESPACE_BEGIN @@ -356,241 +356,4 @@ void MarketInstanceConstructor::configureObject(CGObjectInstance * object, CRand } } - -bool CBankInstanceConstructor::hasNameTextID() const -{ - return true; -} - -void CBankInstanceConstructor::initTypeData(const JsonNode & input) -{ - if (input.Struct().count("name") == 0) - logMod->warn("Bank %s missing name!", getJsonKey()); - - VLC->generaltexth->registerString(input.meta, getNameTextID(), input["name"].String()); - - levels = input["levels"].Vector(); - bankResetDuration = static_cast(input["resetDuration"].Float()); -} - -CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr tmpl) const -{ - return createTyped(tmpl); -} - -BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const -{ - BankConfig bc; - - bc.chance = static_cast(level["chance"].Float()); - - bc.guards = JsonRandom::loadCreatures(level["guards"], rng); - bc.upgradeChance = static_cast(level["upgrade_chance"].Float()); - bc.combatValue = static_cast(level["combat_value"].Float()); - - std::vector spells; - for (size_t i=0; i<6; i++) - IObjectInterface::cb->getAllowedSpells(spells, static_cast(i)); - - bc.resources = ResourceSet(level["reward"]["resources"]); - bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng); - bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng); - bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells); - - bc.value = static_cast(level["value"].Float()); - - return bc; -} - -void CBankInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const -{ - auto * bank = dynamic_cast(object); - - bank->resetDuration = bankResetDuration; - - si32 totalChance = 0; - for(const auto & node : levels) - totalChance += static_cast(node["chance"].Float()); - - assert(totalChance != 0); - - si32 selectedChance = rng.nextInt(totalChance - 1); - - int cumulativeChance = 0; - for(const auto & node : levels) - { - cumulativeChance += static_cast(node["chance"].Float()); - if(selectedChance < cumulativeChance) - { - bank->setConfig(generateConfig(node, rng)); - break; - } - } -} - -CBankInfo::CBankInfo(const JsonVector & Config) : - config(Config) -{ - assert(!Config.empty()); -} - -static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature * crea, si32 amount) -{ - army.totalStrength += crea->getFightValue() * amount; - - bool walker = true; - if(crea->hasBonusOfType(BonusType::SHOOTER)) - { - army.shootersStrength += crea->getFightValue() * amount; - walker = false; - } - if(crea->hasBonusOfType(BonusType::FLYING)) - { - army.flyersStrength += crea->getFightValue() * amount; - walker = false; - } - if(walker) - army.walkersStrength += crea->getFightValue() * amount; -} - -IObjectInfo::CArmyStructure CBankInfo::minGuards() const -{ - std::vector armies; - for(auto configEntry : config) - { - auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); - IObjectInfo::CArmyStructure army; - for(auto & stack : stacks) - { - assert(!stack.allowedCreatures.empty()); - auto weakest = boost::range::min_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b) - { - return a->getFightValue() < b->getFightValue(); - }); - addStackToArmy(army, *weakest, stack.minAmount); - } - armies.push_back(army); - } - return *boost::range::min_element(armies); -} - -IObjectInfo::CArmyStructure CBankInfo::maxGuards() const -{ - std::vector armies; - for(auto configEntry : config) - { - auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); - IObjectInfo::CArmyStructure army; - for(auto & stack : stacks) - { - assert(!stack.allowedCreatures.empty()); - auto strongest = boost::range::max_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b) - { - return a->getFightValue() < b->getFightValue(); - }); - addStackToArmy(army, *strongest, stack.maxAmount); - } - armies.push_back(army); - } - return *boost::range::max_element(armies); -} - -TPossibleGuards CBankInfo::getPossibleGuards() const -{ - TPossibleGuards out; - - for(const JsonNode & configEntry : config) - { - const JsonNode & guardsInfo = configEntry["guards"]; - auto stacks = JsonRandom::evaluateCreatures(guardsInfo); - IObjectInfo::CArmyStructure army; - - - for(auto stack : stacks) - { - army.totalStrength += stack.allowedCreatures.front()->getAIValue() * (stack.minAmount + stack.maxAmount) / 2; - //TODO: add fields for flyers, walkers etc... - } - - ui8 chance = static_cast(configEntry["chance"].Float()); - out.push_back(std::make_pair(chance, army)); - } - return out; -} - -std::vector> CBankInfo::getPossibleResourcesReward() const -{ - std::vector> result; - - for(const JsonNode & configEntry : config) - { - const JsonNode & resourcesInfo = configEntry["reward"]["resources"]; - - if(!resourcesInfo.isNull()) - { - result.emplace_back(configEntry["chance"].Integer(), TResources(resourcesInfo)); - } - } - - return result; -} - -std::vector> CBankInfo::getPossibleCreaturesReward() const -{ - std::vector> aproximateReward; - - for(const JsonNode & configEntry : config) - { - const JsonNode & guardsInfo = configEntry["reward"]["creatures"]; - auto stacks = JsonRandom::evaluateCreatures(guardsInfo); - - for(auto stack : stacks) - { - const auto * creature = stack.allowedCreatures.front(); - - aproximateReward.emplace_back(configEntry["chance"].Integer(), CStackBasicDescriptor(creature, (stack.minAmount + stack.maxAmount) / 2)); - } - } - - return aproximateReward; -} - -bool CBankInfo::givesResources() const -{ - for(const JsonNode & node : config) - if(!node["reward"]["resources"].isNull()) - return true; - return false; -} - -bool CBankInfo::givesArtifacts() const -{ - for(const JsonNode & node : config) - if(!node["reward"]["artifacts"].isNull()) - return true; - return false; -} - -bool CBankInfo::givesCreatures() const -{ - for(const JsonNode & node : config) - if(!node["reward"]["creatures"].isNull()) - return true; - return false; -} - -bool CBankInfo::givesSpells() const -{ - for(const JsonNode & node : config) - if(!node["reward"]["spells"].isNull()) - return true; - return false; -} - - -std::unique_ptr CBankInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const -{ - return std::unique_ptr(new CBankInfo(levels)); -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/CommonConstructors.h b/lib/mapObjectConstructors/CommonConstructors.h index 12745afe0..d50630714 100644 --- a/lib/mapObjectConstructors/CommonConstructors.h +++ b/lib/mapObjectConstructors/CommonConstructors.h @@ -10,14 +10,13 @@ #pragma once #include "AObjectTypeHandler.h" +#include "CDefaultObjectTypeHandler.h" #include "../mapObjects/CGMarket.h" #include "../mapObjects/MiscObjects.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGTownInstance.h" -#include "../mapObjects/CBank.h" #include "../LogicalExpression.h" -#include "IObjectInfo.h" VCMI_LIB_NAMESPACE_BEGIN @@ -30,42 +29,6 @@ class CBank; class CFaction; class CStackBasicDescriptor; -/// Class that is used for objects that do not have dedicated handler -template -class CDefaultObjectTypeHandler : public AObjectTypeHandler -{ -protected: - ObjectType * createTyped(std::shared_ptr tmpl /* = nullptr */) const - { - auto obj = new ObjectType(); - preInitObject(obj); - - //Set custom template or leave null - if (tmpl) - { - obj->appearance = tmpl; - } - - return obj; - } -public: - CDefaultObjectTypeHandler() {} - - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override - { - return createTyped(tmpl); - } - - virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override - { - } - - virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override - { - return nullptr; - } -}; - class CObstacleConstructor : public CDefaultObjectTypeHandler { public: @@ -178,90 +141,6 @@ public: } }; -struct BankConfig -{ - ui32 value = 0; //overall value of given things - ui32 chance = 0; //chance for this level being chosen - ui32 upgradeChance = 0; //chance for creatures to be in upgraded versions - ui32 combatValue = 0; //how hard are guards of this level - std::vector guards; //creature ID, amount - ResourceSet resources; //resources given in case of victory - std::vector creatures; //creatures granted in case of victory (creature ID, amount) - std::vector artifacts; //artifacts given in case of victory - std::vector spells; // granted spell(s), for Pyramid - - template void serialize(Handler &h, const int version) - { - h & chance; - h & upgradeChance; - h & guards; - h & combatValue; - h & resources; - h & creatures; - h & artifacts; - h & value; - h & spells; - } -}; - -using TPossibleGuards = std::vector>; - -template -struct DLL_LINKAGE PossibleReward -{ - int chance; - T data; - - PossibleReward(int chance, const T & data) : chance(chance), data(data) {} -}; - -class DLL_LINKAGE CBankInfo : public IObjectInfo -{ - const JsonVector & config; -public: - CBankInfo(const JsonVector & Config); - - TPossibleGuards getPossibleGuards() const; - std::vector> getPossibleResourcesReward() const; - std::vector> getPossibleCreaturesReward() const; - - // These functions should try to evaluate minimal possible/max possible guards to give provide information on possible thread to AI - CArmyStructure minGuards() const override; - CArmyStructure maxGuards() const override; - - bool givesResources() const override; - bool givesArtifacts() const override; - bool givesCreatures() const override; - bool givesSpells() const override; -}; - -class CBankInstanceConstructor : public CDefaultObjectTypeHandler -{ - BankConfig generateConfig(const JsonNode & conf, CRandomGenerator & rng) const; - - JsonVector levels; -protected: - void initTypeData(const JsonNode & input) override; - -public: - // all banks of this type will be reset N days after clearing, - si32 bankResetDuration = 0; - - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; - - bool hasNameTextID() const override; - - std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; - - template void serialize(Handler &h, const int version) - { - h & levels; - h & bankResetDuration; - h & static_cast&>(*this); - } -}; - class MarketInstanceConstructor : public CDefaultObjectTypeHandler { protected: diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index a5b090bf4..efccca9c7 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -18,7 +18,7 @@ #include "../CGeneralTextHandler.h" #include "../CSoundBase.h" #include "../mapObjectConstructors/CObjectClassesHandler.h" -#include "../mapObjectConstructors/CommonConstructors.h" +#include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../IGameCallback.h" #include "../CGameState.h" diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 741cb0b6f..d38af4b1b 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -20,6 +20,7 @@ #include "../CModHandler.h" //needed? #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" +#include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../mapObjects/MapObjects.h" #include "../mapObjects/CGTownBuilding.h" #include "../mapObjects/ObjectTemplate.h" From fc190b14bbc55b31b87676ef9ffc567ad4301cfe Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 6 Jun 2023 18:34:04 +0300 Subject: [PATCH 03/12] Spell shrines can now be configured in json --- cmake_modules/VCMI_lib.cmake | 2 + config/objects/generic.json | 12 ++++ include/vcmi/spells/Spell.h | 2 + lib/IGameCallback.cpp | 15 +++-- lib/IGameCallback.h | 2 +- lib/JsonRandom.cpp | 31 ++++++--- .../CObjectClassesHandler.cpp | 3 +- .../ShrineInstanceConstructor.cpp | 65 +++++++++++++++++++ .../ShrineInstanceConstructor.h | 34 ++++++++++ lib/mapObjects/MiscObjects.cpp | 24 ++----- lib/mapObjects/MiscObjects.h | 3 + lib/rewardable/Info.cpp | 6 +- lib/spells/CSpellHandler.cpp | 5 ++ lib/spells/CSpellHandler.h | 2 + 14 files changed, 168 insertions(+), 38 deletions(-) create mode 100644 lib/mapObjectConstructors/ShrineInstanceConstructor.cpp create mode 100644 lib/mapObjectConstructors/ShrineInstanceConstructor.h diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 888e01b17..20db1745d 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -70,6 +70,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.cpp + ${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjects/CArmedInstance.cpp ${MAIN_LIB_DIR}/mapObjects/CBank.cpp @@ -377,6 +378,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/IObjectInfo.h ${MAIN_LIB_DIR}/mapObjectConstructors/RandomMapInfo.h + ${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/SObjectSounds.h ${MAIN_LIB_DIR}/mapObjects/CArmedInstance.h diff --git a/config/objects/generic.json b/config/objects/generic.json index 04c781969..c01ac5dc7 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -311,6 +311,10 @@ "value" : 500, "rarity" : 100 } + "visitText" : 127, + "spell" : { + "level" : 1 + } } } }, @@ -330,6 +334,10 @@ "rmg" : { "value" : 2000, "rarity" : 100 + }, + "visitText" : 128, + "spell" : { + "level" : 2 } } } @@ -350,6 +358,10 @@ "rmg" : { "value" : 3000, "rarity" : 100 + }, + "visitText" : 129, + "spell" : { + "level" : 3 } } } diff --git a/include/vcmi/spells/Spell.h b/include/vcmi/spells/Spell.h index 98547a387..ba16ad105 100644 --- a/include/vcmi/spells/Spell.h +++ b/include/vcmi/spells/Spell.h @@ -15,6 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN class SpellID; +enum class ESpellSchool: int8_t; namespace spells { @@ -43,6 +44,7 @@ public: virtual bool isSpecial() const = 0; virtual bool isMagical() const = 0; //Should this spell considered as magical effect or as ability (like dendroid's bind) + virtual bool hasSchool(ESpellSchool school) const = 0; virtual void forEachSchool(const SchoolCallback & cb) const = 0; virtual const std::string & getCastSound() const = 0; virtual int32_t getCost(const int32_t skillLevel) const = 0; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index c05494e4f..2b642314b 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -170,16 +170,19 @@ void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)]); } -void CPrivilegedInfoCallback::getAllowedSpells(std::vector & out, ui16 level) +void CPrivilegedInfoCallback::getAllowedSpells(std::vector & out, std::optional level) { for (ui32 i = 0; i < gs->map->allowedSpell.size(); i++) //spellh size appears to be greater (?) { - const spells::Spell * spell = SpellID(i).toSpell(); - if(isAllowed(0, spell->getIndex()) && spell->getLevel() == level) - { - out.push_back(spell->getId()); - } + + if (!isAllowed(0, spell->getIndex())) + continue; + + if (level.has_value() && spell->getLevel() != level) + continue; + + out.push_back(spell->getId()); } } diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index d6c428188..e349b6daa 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -71,7 +71,7 @@ public: //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant void pickAllowedArtsSet(std::vector & out, CRandomGenerator & rand) const; - void getAllowedSpells(std::vector &out, ui16 level); + void getAllowedSpells(std::vector &out, std::optional level = std::nullopt); template void saveCommonState(Saver &out) const; //stores GS and VLC diff --git a/lib/JsonRandom.cpp b/lib/JsonRandom.cpp index de1433f67..3be169f88 100644 --- a/lib/JsonRandom.cpp +++ b/lib/JsonRandom.cpp @@ -243,21 +243,36 @@ namespace JsonRandom if (value.getType() == JsonNode::JsonType::DATA_STRING) return SpellID(VLC->modh->identifiers.getIdentifier("spell", value).value()); - vstd::erase_if(spells, [=](const SpellID & spell) + if (!value["level"].isNull()) { - return VLC->spellh->getById(spell)->getLevel() != si32(value["level"].Float()); - }); + int32_t spellLevel = value["level"].Float(); + vstd::erase_if(spells, [=](const SpellID & spell) + { + return VLC->spellh->getById(spell)->getLevel() != spellLevel; + }); + } + + if (!value["school"].isNull()) + { + int32_t schoolID = VLC->modh->identifiers.getIdentifier("spellSchool", value["school"]).value(); + + vstd::erase_if(spells, [=](const SpellID & spell) + { + return !VLC->spellh->getById(spell)->hasSchool(ESpellSchool(schoolID)); + }); + } + + if (spells.empty()) + { + logMod->warn("Failed to select suitable random spell!"); + return SpellID::NONE; + } return SpellID(*RandomGeneratorUtil::nextItem(spells, rng)); } std::vector loadSpells(const JsonNode & value, CRandomGenerator & rng, const std::vector & spells) { - // possible extensions: (taken from spell json config) - // "type": "adventure",//"adventure", "combat", "ability" - // "school": {"air":true, "earth":true, "fire":true, "water":true}, - // "level": 1, - std::vector ret; for (const JsonNode & entry : value.Vector()) { diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 57bd7392f..3a92ffc79 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -24,6 +24,7 @@ #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" #include "../mapObjectConstructors/CBankInstanceConstructor.h" +#include "../mapObjectConstructors/ShrineInstanceConstructor.h" #include "../mapObjects/CQuest.h" #include "../mapObjects/CGPandoraBox.h" #include "../mapObjects/ObjectTemplate.h" @@ -44,6 +45,7 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER_CLASS("bank", CBankInstanceConstructor); SET_HANDLER_CLASS("boat", BoatInstanceConstructor); SET_HANDLER_CLASS("market", MarketInstanceConstructor); + SET_HANDLER_CLASS("shrine", ShrineInstanceConstructor); SET_HANDLER_CLASS("static", CObstacleConstructor); SET_HANDLER_CLASS("", CObstacleConstructor); @@ -78,7 +80,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("scholar", CGScholar); SET_HANDLER("seerHut", CGSeerHut); SET_HANDLER("shipyard", CGShipyard); - SET_HANDLER("shrine", CGShrine); SET_HANDLER("sign", CGSignBottle); SET_HANDLER("siren", CGSirens); SET_HANDLER("monolith", CGMonolith); diff --git a/lib/mapObjectConstructors/ShrineInstanceConstructor.cpp b/lib/mapObjectConstructors/ShrineInstanceConstructor.cpp new file mode 100644 index 000000000..1e8c8b72d --- /dev/null +++ b/lib/mapObjectConstructors/ShrineInstanceConstructor.cpp @@ -0,0 +1,65 @@ +/* +* ShrineInstanceConstructor.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#include "StdInc.h" +#include "ShrineInstanceConstructor.h" + +#include "IObjectInfo.h" +#include "../mapObjects/MiscObjects.h" +#include "../JsonRandom.h" +#include "../IGameCallback.h" + +VCMI_LIB_NAMESPACE_BEGIN + +void ShrineInstanceConstructor::initTypeData(const JsonNode & config) +{ + parameters = config; +} + +CGObjectInstance * ShrineInstanceConstructor::create(std::shared_ptr tmpl) const +{ + CGShrine * shrine = new CGShrine; + + preInitObject(shrine); + + if(tmpl) + shrine->appearance = tmpl; + + return shrine; +} + +void ShrineInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +{ + CGShrine * shrine = dynamic_cast(object); + + if (!shrine) + throw std::runtime_error("Unexpected object instance in ShrineInstanceConstructor!"); + + auto visitTextParameter = parameters["visitText"]; + + if (visitTextParameter.isNumber()) + shrine->visitText.addTxt(MetaString::ADVOB_TXT, static_cast(visitTextParameter.Float())); + else + shrine->visitText << visitTextParameter.String(); + + if(shrine->spell == SpellID::NONE) // shrine has no predefined spell + { + std::vector possibilities; + shrine->cb->getAllowedSpells(possibilities); + + shrine->spell =JsonRandom::loadSpell(parameters["spell"], rng, possibilities); + } +} + +std::unique_ptr ShrineInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const +{ + return nullptr; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/ShrineInstanceConstructor.h b/lib/mapObjectConstructors/ShrineInstanceConstructor.h new file mode 100644 index 000000000..1350f92f4 --- /dev/null +++ b/lib/mapObjectConstructors/ShrineInstanceConstructor.h @@ -0,0 +1,34 @@ +/* +* ShrineInstanceConstructor.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#pragma once + +#include "AObjectTypeHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class ShrineInstanceConstructor final : public AObjectTypeHandler +{ + JsonNode parameters; + +protected: + void initTypeData(const JsonNode & config) override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + +public: + template void serialize(Handler &h, const int version) + { + h & static_cast(*this); + h & parameters; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 673a9a39c..5fb0bd92d 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -26,6 +26,8 @@ #include "../CPlayerState.h" #include "../GameSettings.h" #include "../serializer/JsonSerializeFormat.h" +#include "../mapObjectConstructors/AObjectTypeHandler.h" +#include "../mapObjectConstructors/CObjectClassesHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -1530,7 +1532,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const InfoWindow iw; iw.type = EInfoWindowMode::AUTO; iw.player = h->getOwner(); - iw.text.addTxt(MetaString::ADVOB_TXT,127 + ID - 88); + iw.text = visitText; iw.text.addTxt(MetaString::SPELL_NAME,spell); iw.text << "."; @@ -1542,7 +1544,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const { iw.text.addTxt(MetaString::ADVOB_TXT,174); } - else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT && h->maxSpellLevel() < 3) //it's third level spell and hero doesn't have wisdom + else if(spell.toSpell()->getLevel() > h->maxSpellLevel()) //it's third level spell and hero doesn't have wisdom { iw.text.addTxt(MetaString::ADVOB_TXT,130); } @@ -1560,20 +1562,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const void CGShrine::initObj(CRandomGenerator & rand) { - if(spell == SpellID::NONE) //spell not set - { - int level = ID-87; - std::vector possibilities; - cb->getAllowedSpells (possibilities, level); - - if(possibilities.empty()) - { - logGlobal->error("Error: cannot init shrine, no allowed spells!"); - return; - } - - spell = *RandomGeneratorUtil::nextItem(possibilities, rand); - } + VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); } std::string CGShrine::getHoverText(PlayerColor player) const @@ -1693,8 +1682,7 @@ void CGScholar::initObj(CRandomGenerator & rand) break; case SPELL: std::vector possibilities; - for (int i = 1; i < 6; ++i) - cb->getAllowedSpells (possibilities, i); + cb->getAllowedSpells (possibilities); bonusID = *RandomGeneratorUtil::nextItem(possibilities, rand); break; } diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 2a50ca0df..d921a4ac1 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -246,7 +246,9 @@ protected: class DLL_LINKAGE CGShrine : public CTeamVisited { public: + MetaString visitText; SpellID spell; //id of spell or NONE if random + void onHeroVisit(const CGHeroInstance * h) const override; void initObj(CRandomGenerator & rand) override; std::string getHoverText(PlayerColor player) const override; @@ -256,6 +258,7 @@ public: { h & static_cast(*this);; h & spell; + h & visitText; } protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index 68fced7d7..2d5292718 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -74,8 +74,7 @@ Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Conf void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const { std::vector spells; - for (size_t i=0; i<6; i++) - IObjectInterface::cb->getAllowedSpells(spells, static_cast(i)); + IObjectInterface::cb->getAllowedSpells(spells); limiter.dayOfWeek = JsonRandom::loadValue(source["dayOfWeek"], rng); @@ -124,8 +123,7 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand reward.secondary = JsonRandom::loadSecondary(source["secondary"], rng); std::vector spells; - for (size_t i=0; i<6; i++) - IObjectInterface::cb->getAllowedSpells(spells, static_cast(i)); + IObjectInterface::cb->getAllowedSpells(spells); reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng); reward.spells = JsonRandom::loadSpells(source["spells"], rng, spells); diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 75da3ad99..aee7303b4 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -127,6 +127,11 @@ int64_t CSpell::calculateDamage(const spells::Caster * caster) const return caster->getSpellBonus(this, rawDamage, nullptr); } +bool CSpell::hasSchool(ESpellSchool which) const +{ + return school.count(which) && school.at(which); +} + bool CSpell::canBeCast(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster) const { //if caller do not interested in description just discard it and do not pollute even debug log diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index 90966df1f..884017a60 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -209,6 +209,8 @@ public: int64_t calculateDamage(const spells::Caster * caster) const override; + bool hasSchool(ESpellSchool school) const override; + /** * Calls cb for each school this spell belongs to * From 2e7c382612faf295cd3654a7e9cf6fc7504314ab Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 6 Jun 2023 19:19:30 +0300 Subject: [PATCH 04/12] Moved creature upgrade logic to CGObjectInstance inheritors --- config/objects/generic.json | 2 +- lib/CGameState.cpp | 58 +++++-------------- .../CObjectClassesHandler.cpp | 1 + lib/mapObjects/CGHeroInstance.cpp | 14 +++++ lib/mapObjects/CGHeroInstance.h | 4 +- lib/mapObjects/CGObjectInstance.cpp | 5 -- lib/mapObjects/CGTownInstance.cpp | 17 ++++++ lib/mapObjects/CGTownInstance.h | 4 +- lib/mapObjects/IObjectInterface.h | 9 +++ lib/mapObjects/MiscObjects.cpp | 17 ++++++ lib/mapObjects/MiscObjects.h | 7 +++ lib/registerTypes/RegisterTypes.h | 2 + test/mock/mock_spells_Spell.h | 1 + 13 files changed, 91 insertions(+), 50 deletions(-) diff --git a/config/objects/generic.json b/config/objects/generic.json index c01ac5dc7..59b94ccf0 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -842,7 +842,7 @@ /// Classes without dedicated object "hillFort" : { "index" :35, - "handler": "generic", + "handler": "hillFort", "base" : { "sounds" : { "ambient" : ["LOOPSWAR"], diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 29162850c..1b810cfbe 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2018,54 +2018,28 @@ UpgradeInfo CGameState::fillUpgradeInfo(const CStackInstance &stack) const UpgradeInfo ret; const CCreature *base = stack.type; - const CGHeroInstance * h = stack.armyObj->ID == Obj::HERO ? dynamic_cast(stack.armyObj) : nullptr; - const CGTownInstance *t = nullptr; - - if(stack.armyObj->ID == Obj::TOWN) - t = dynamic_cast(stack.armyObj); - else if(h) - { //hero specialty - TConstBonusListPtr lista = h->getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, base->getId())); - for(const auto & it : *lista) - { - auto nid = CreatureID(it->additionalInfo[0]); - if (nid != base->getId()) //in very specific case the upgrade is available by default (?) - { - ret.newID.push_back(nid); - ret.cost.push_back(nid.toCreature()->getFullRecruitCost() - base->getFullRecruitCost()); - } - } - t = h->visitedTown; - } - if(t) + if (stack.armyObj->ID == Obj::HERO) { - for(const CGTownInstance::TCreaturesSet::value_type & dwelling : t->creatures) + auto hero = dynamic_cast(stack.armyObj); + hero->fillUpgradeInfo(ret, stack); + + if (hero->visitedTown) { - if (vstd::contains(dwelling.second, base->getId())) //Dwelling with our creature - { - for(const auto & upgrID : dwelling.second) - { - if(vstd::contains(base->upgrades, upgrID)) //possible upgrade - { - ret.newID.push_back(upgrID); - ret.cost.push_back(upgrID.toCreature()->getFullRecruitCost() - base->getFullRecruitCost()); - } - } - } + hero->visitedTown->fillUpgradeInfo(ret, stack); + } + else + { + auto object = vstd::frontOrNull(getVisitableObjs(hero->visitablePos())); + auto upgradeSource = dynamic_cast(object); + if (object != hero && upgradeSource != nullptr) + upgradeSource->fillUpgradeInfo(ret, stack); } } - //hero is visiting Hill Fort - if(h && map->getTile(h->visitablePos()).visitableObjects.front()->ID == Obj::HILL_FORT) + if (stack.armyObj->ID == Obj::TOWN) { - static const int costModifiers[] = {0, 25, 50, 75, 100}; //we get cheaper upgrades depending on level - const int costModifier = costModifiers[std::min(std::max((int)base->getLevel() - 1, 0), std::size(costModifiers) - 1)]; - - for(const auto & nid : base->upgrades) - { - ret.newID.push_back(nid); - ret.cost.push_back((nid.toCreature()->getFullRecruitCost() - base->getFullRecruitCost()) * costModifier / 100); - } + auto town = dynamic_cast(stack.armyObj); + town->fillUpgradeInfo(ret, stack); } if(!ret.newID.empty()) diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 3a92ffc79..e752d72d5 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -87,6 +87,7 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("whirlpool", CGWhirlpool); SET_HANDLER("witch", CGWitchHut); SET_HANDLER("terrain", CGTerrainPatch); + SET_HANDLER("hillFort", HillFort); #undef SET_HANDLER_CLASS #undef SET_HANDLER diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 83c8599b7..981f5f257 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1715,4 +1715,18 @@ bool CGHeroInstance::isMissionCritical() const return false; } +void CGHeroInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const +{ + TConstBonusListPtr lista = getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, stack.type->getId())); + for(const auto & it : *lista) + { + auto nid = CreatureID(it->additionalInfo[0]); + if (nid != stack.type->getId()) //in very specific case the upgrade is available by default (?) + { + info.newID.push_back(nid); + info.cost.push_back(nid.toCreature()->getFullRecruitCost() - stack.type->getFullRecruitCost()); + } + } +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 0002f4863..d6d1c1cb4 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -39,7 +39,7 @@ public: }; -class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember +class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember, public ICreatureUpgrader { // We serialize heroes into JSON for crossover friend class CCampaignState; @@ -230,6 +230,8 @@ public: void recreateSecondarySkillsBonuses(); void updateSkillBonus(const SecondarySkill & which, int val); + void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override; + bool hasVisions(const CGObjectInstance * target, const int subtype) const; /// If this hero perishes, the scenario is failed bool isMissionCritical() const; diff --git a/lib/mapObjects/CGObjectInstance.cpp b/lib/mapObjects/CGObjectInstance.cpp index 6418c84d2..5c9ff6158 100644 --- a/lib/mapObjects/CGObjectInstance.cpp +++ b/lib/mapObjects/CGObjectInstance.cpp @@ -254,11 +254,6 @@ void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const { switch(ID) { - case Obj::HILL_FORT: - { - openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum()); - } - break; case Obj::SANCTUARY: { //You enter the sanctuary and immediately feel as if a great weight has been lifted off your shoulders. You feel safe here. diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index e704e3b49..fa80df1d8 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1242,5 +1242,22 @@ int GrowthInfo::totalGrowth() const return ret; } +void CGTownInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const +{ + for(const CGTownInstance::TCreaturesSet::value_type & dwelling : creatures) + { + if (vstd::contains(dwelling.second, stack.type->getId())) //Dwelling with our creature + { + for(const auto & upgrID : dwelling.second) + { + if(vstd::contains(stack.type->upgrades, upgrID)) //possible upgrade + { + info.newID.push_back(upgrID); + info.cost.push_back(upgrID.toCreature()->getFullRecruitCost() - stack.type->getFullRecruitCost()); + } + } + } + } +} VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index aaffde1ce..05a78c90a 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -42,7 +42,7 @@ struct DLL_LINKAGE GrowthInfo int totalGrowth() const; }; -class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider +class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader { std::string name; // name of town public: @@ -197,6 +197,8 @@ public: void battleFinished(const CGHeroInstance * hero, const BattleResult & result) const override; std::string getObjectName() const override; + void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override; + void afterAddToMap(CMap * map) override; void afterRemoveFromMap(CMap * map) override; static void reset(); diff --git a/lib/mapObjects/IObjectInterface.h b/lib/mapObjects/IObjectInterface.h index 05175761a..700898051 100644 --- a/lib/mapObjects/IObjectInterface.h +++ b/lib/mapObjects/IObjectInterface.h @@ -14,6 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN struct BattleResult; +struct UpgradeInfo; class CGObjectInstance; class CRandomGenerator; class IGameCallback; @@ -66,6 +67,14 @@ public: } }; +class DLL_LINKAGE ICreatureUpgrader +{ +public: + virtual void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const = 0; + + virtual ~ICreatureUpgrader() = default; +}; + class DLL_LINKAGE IBoatGenerator { public: diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 5fb0bd92d..b1e50907a 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -2164,4 +2164,21 @@ void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler) serializeJsonOwner(handler); } +void HillFort::onHeroVisit(const CGHeroInstance * h) const +{ + openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum()); +} + +void HillFort::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const +{ + static const int costModifiers[] = {0, 25, 50, 75, 100}; //we get cheaper upgrades depending on level + const int costModifier = costModifiers[std::min(std::max((int)stack.type->getLevel() - 1, 0), std::size(costModifiers) - 1)]; + + for(const auto & nid : stack.type->upgrades) + { + info.newID.push_back(nid); + info.cost.push_back((nid.toCreature()->getFullRecruitCost() - stack.type->getFullRecruitCost()) * costModifier / 100); + } +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index d921a4ac1..382d5b642 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -553,4 +553,11 @@ public: } }; +class DLL_LINKAGE HillFort : public CGObjectInstance, public ICreatureUpgrader +{ +protected: + void onHeroVisit(const CGHeroInstance * h) const override; + void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override; +}; + VCMI_LIB_NAMESPACE_END diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index d38af4b1b..8961f3bf7 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -64,6 +64,7 @@ void registerTypesMapObjects1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); @@ -138,6 +139,7 @@ void registerTypesMapObjectTypes(Serializer &s) REGISTER_GENERIC_HANDLER(CGTownInstance); REGISTER_GENERIC_HANDLER(CGUniversity); REGISTER_GENERIC_HANDLER(CGWitchHut); + REGISTER_GENERIC_HANDLER(HillFort); #undef REGISTER_GENERIC_HANDLER diff --git a/test/mock/mock_spells_Spell.h b/test/mock/mock_spells_Spell.h index 6376eafb4..f507e3836 100644 --- a/test/mock/mock_spells_Spell.h +++ b/test/mock/mock_spells_Spell.h @@ -45,6 +45,7 @@ public: MOCK_CONST_METHOD0(isOffensive, bool()); MOCK_CONST_METHOD0(isSpecial, bool()); MOCK_CONST_METHOD0(isMagical, bool()); + MOCK_CONST_METHOD1(hasSchool, bool(ESpellSchool)); MOCK_CONST_METHOD1(forEachSchool, void(const SchoolCallback &)); MOCK_CONST_METHOD0(getCastSound, const std::string &()); MOCK_CONST_METHOD1(registerIcons, void(const IconRegistar &)); From a94b68e6aa86a9a24cb0f19054e9f216f41f730b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 7 Jun 2023 00:17:39 +0300 Subject: [PATCH 05/12] Hill fort upgrade costs are now loaded from json --- cmake_modules/VCMI_lib.cmake | 2 + config/objects/generic.json | 11 +++- .../CDefaultObjectTypeHandler.h | 4 +- .../CObjectClassesHandler.cpp | 3 +- .../HillFortInstanceConstructor.cpp | 50 +++++++++++++++++++ .../HillFortInstanceConstructor.h | 34 +++++++++++++ lib/mapObjects/MiscObjects.cpp | 14 +++++- lib/mapObjects/MiscObjects.h | 13 +++++ lib/registerTypes/RegisterTypes.h | 4 ++ 9 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 lib/mapObjectConstructors/HillFortInstanceConstructor.cpp create mode 100644 lib/mapObjectConstructors/HillFortInstanceConstructor.h diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 20db1745d..1be2e2289 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -70,6 +70,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.cpp + ${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjects/CArmedInstance.cpp @@ -376,6 +377,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.h ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.h ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.h + ${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/IObjectInfo.h ${MAIN_LIB_DIR}/mapObjectConstructors/RandomMapInfo.h ${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.h diff --git a/config/objects/generic.json b/config/objects/generic.json index 59b94ccf0..32d172cdd 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -857,7 +857,14 @@ "zoneLimit" : 1, "value" : 7000, "rarity" : 20 - } + }, + "upgradeCostFactor" : [ + 0, // level 1 + 25, + 50, + 75, + 100 // level 5+ + ] } } }, @@ -873,7 +880,7 @@ } } }, - "marketOfTime" : { // Unused/not implemented H3 object present on some maps RoE maps + "marketOfTime" : { // Unused/not implemented H3 object present on some RoE maps "index" :50, "handler": "generic" }, diff --git a/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h index 6b055e288..766b01fea 100644 --- a/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h +++ b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h @@ -39,11 +39,11 @@ public: return createTyped(tmpl); } - virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override { } - virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override + std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override { return nullptr; } diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index e752d72d5..6fa6777b5 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -25,6 +25,7 @@ #include "../mapObjectConstructors/CommonConstructors.h" #include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../mapObjectConstructors/ShrineInstanceConstructor.h" +#include "../mapObjectConstructors/HillFortInstanceConstructor.h" #include "../mapObjects/CQuest.h" #include "../mapObjects/CGPandoraBox.h" #include "../mapObjects/ObjectTemplate.h" @@ -46,6 +47,7 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER_CLASS("boat", BoatInstanceConstructor); SET_HANDLER_CLASS("market", MarketInstanceConstructor); SET_HANDLER_CLASS("shrine", ShrineInstanceConstructor); + SET_HANDLER_CLASS("hillFort", HillFortInstanceConstructor); SET_HANDLER_CLASS("static", CObstacleConstructor); SET_HANDLER_CLASS("", CObstacleConstructor); @@ -87,7 +89,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("whirlpool", CGWhirlpool); SET_HANDLER("witch", CGWitchHut); SET_HANDLER("terrain", CGTerrainPatch); - SET_HANDLER("hillFort", HillFort); #undef SET_HANDLER_CLASS #undef SET_HANDLER diff --git a/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp b/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp new file mode 100644 index 000000000..128be8300 --- /dev/null +++ b/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp @@ -0,0 +1,50 @@ +/* +* HillFortInstanceConstructor.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#include "StdInc.h" +#include "HillFortInstanceConstructor.h" + +#include "../mapObjects/MiscObjects.h" +#include "IObjectInfo.h" + +VCMI_LIB_NAMESPACE_BEGIN + +void HillFortInstanceConstructor::initTypeData(const JsonNode & config) +{ + parameters = config; +} + +CGObjectInstance * HillFortInstanceConstructor::create(std::shared_ptr tmpl) const +{ + HillFort * fort = new HillFort; + + preInitObject(fort); + + if(tmpl) + fort->appearance = tmpl; + + return fort; +} + +void HillFortInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +{ + HillFort * fort = dynamic_cast(object); + + if(!fort) + throw std::runtime_error("Unexpected object instance in HillFortInstanceConstructor!"); + + fort->upgradeCostPercentage = parameters["upgradeCostFactor"].convertTo>(); +} + +std::unique_ptr HillFortInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const +{ + return nullptr; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/HillFortInstanceConstructor.h b/lib/mapObjectConstructors/HillFortInstanceConstructor.h new file mode 100644 index 000000000..fc93b8876 --- /dev/null +++ b/lib/mapObjectConstructors/HillFortInstanceConstructor.h @@ -0,0 +1,34 @@ +/* +* HillFortInstanceConstructor.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#pragma once + +#include "AObjectTypeHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class HillFortInstanceConstructor final : public AObjectTypeHandler +{ + JsonNode parameters; + +protected: + void initTypeData(const JsonNode & config) override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + +public: + template void serialize(Handler &h, const int version) + { + h & static_cast(*this); + h & parameters; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index b1e50907a..af5e8edfa 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -2169,10 +2169,20 @@ void HillFort::onHeroVisit(const CGHeroInstance * h) const openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum()); } +void HillFort::initObj(CRandomGenerator & rand) +{ + VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); +} + void HillFort::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const { - static const int costModifiers[] = {0, 25, 50, 75, 100}; //we get cheaper upgrades depending on level - const int costModifier = costModifiers[std::min(std::max((int)stack.type->getLevel() - 1, 0), std::size(costModifiers) - 1)]; + int32_t level = stack.type->getLevel(); + int32_t index = std::clamp(level - 1, 0, upgradeCostPercentage.size() - 1); + + int costModifier = upgradeCostPercentage[index]; + + if (costModifier < 0) + return; // upgrade not allowed for(const auto & nid : stack.type->upgrades) { diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 382d5b642..8315a434a 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -15,6 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN class CMap; +class HillFortInstanceConstructor; // This one teleport-specific, but has to be available everywhere in callbacks and netpacks // For now it's will be there till teleports code refactored and moved into own file @@ -555,9 +556,21 @@ public: class DLL_LINKAGE HillFort : public CGObjectInstance, public ICreatureUpgrader { + friend class HillFortInstanceConstructor; + + std::vector upgradeCostPercentage; + protected: + void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override; + +public: + template void serialize(Handler &h, const int version) + { + h & static_cast(*this); + h & upgradeCostPercentage; + } }; VCMI_LIB_NAMESPACE_END diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 8961f3bf7..a37a61d16 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -21,6 +21,8 @@ #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" #include "../mapObjectConstructors/CBankInstanceConstructor.h" +#include "../mapObjectConstructors/HillFortInstanceConstructor.h" +#include "../mapObjectConstructors/ShrineInstanceConstructor.h" #include "../mapObjects/MapObjects.h" #include "../mapObjects/CGTownBuilding.h" #include "../mapObjects/ObjectTemplate.h" @@ -99,6 +101,8 @@ void registerTypesMapObjectTypes(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); + s.template registerType(); #define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType >() From 6aedb99117369a964d46575ba62886f3c13067a6 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 7 Jun 2023 01:55:21 +0300 Subject: [PATCH 06/12] Cleaned up IShipyard interface --- AI/Nullkiller/Goals/BuildBoat.cpp | 9 ++-- .../Pathfinding/Actions/BoatActions.cpp | 10 ++-- AI/VCAI/Goals/BuildBoat.cpp | 15 +++--- CCallback.cpp | 2 +- client/NetPacksClient.cpp | 2 +- lib/mapObjects/CGHeroInstance.cpp | 6 ++- lib/mapObjects/CGHeroInstance.h | 1 + lib/mapObjects/CGTownInstance.cpp | 15 +++++- lib/mapObjects/CGTownInstance.h | 5 +- lib/mapObjects/IObjectInterface.cpp | 53 ++++--------------- lib/mapObjects/IObjectInterface.h | 26 +++------ lib/mapObjects/MiscObjects.cpp | 16 +++--- lib/mapObjects/MiscObjects.h | 9 ++-- lib/registerTypes/RegisterTypes.h | 6 +-- server/CGameHandler.cpp | 6 --- 15 files changed, 72 insertions(+), 109 deletions(-) diff --git a/AI/Nullkiller/Goals/BuildBoat.cpp b/AI/Nullkiller/Goals/BuildBoat.cpp index 23a031939..5ca46dc1c 100644 --- a/AI/Nullkiller/Goals/BuildBoat.cpp +++ b/AI/Nullkiller/Goals/BuildBoat.cpp @@ -23,7 +23,7 @@ using namespace Goals; bool BuildBoat::operator==(const BuildBoat & other) const { - return shipyard->o->id == other.shipyard->o->id; + return shipyard == other.shipyard; } // //TSubgoal BuildBoat::decomposeSingle() const @@ -54,7 +54,7 @@ void BuildBoat::accept(AIGateway * ai) throw cannotFulfillGoalException("Can not afford boat"); } - if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) + if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES) { throw cannotFulfillGoalException("Can not build boat in enemy shipyard"); } @@ -65,9 +65,8 @@ void BuildBoat::accept(AIGateway * ai) } logAi->trace( - "Building boat at shipyard %s located at %s, estimated boat position %s", - shipyard->o->getObjectName(), - shipyard->o->visitablePos().toString(), + "Building boat at shipyard located at %s, estimated boat position %s", + shipyard->getObject()->visitablePos().toString(), shipyard->bestLocation().toString()); cb->buildBoat(shipyard); diff --git a/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp b/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp index e80ee0e03..9fcf052ce 100644 --- a/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp +++ b/AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp @@ -32,9 +32,9 @@ namespace AIPathfinding Goals::TSubgoal BuildBoatAction::decompose(const CGHeroInstance * hero) const { - if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) + if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES) { - return Goals::sptr(Goals::CaptureObject(shipyard->o)); + return Goals::sptr(Goals::CaptureObject(targetObject())); } return Goals::sptr(Goals::Invalid()); @@ -44,7 +44,7 @@ namespace AIPathfinding { auto hero = source->actor->hero; - if(cb->getPlayerRelations(hero->tempOwner, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) + if(cb->getPlayerRelations(hero->tempOwner, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES) { #if NKAI_TRACE_LEVEL > 1 logAi->trace("Can not build a boat. Shipyard is enemy."); @@ -70,7 +70,7 @@ namespace AIPathfinding const CGObjectInstance * BuildBoatAction::targetObject() const { - return shipyard->o; + return dynamic_cast(shipyard); } const ChainActor * BuildBoatAction::getActor(const ChainActor * sourceActor) const @@ -101,7 +101,7 @@ namespace AIPathfinding std::string BuildBoatAction::toString() const { - return "Build Boat at " + shipyard->o->getObjectName(); + return "Build Boat at " + shipyard->getObject()->visitablePos().toString(); } bool SummonBoatAction::canAct(const AIPathNode * source) const diff --git a/AI/VCAI/Goals/BuildBoat.cpp b/AI/VCAI/Goals/BuildBoat.cpp index f5828005f..4c4136b22 100644 --- a/AI/VCAI/Goals/BuildBoat.cpp +++ b/AI/VCAI/Goals/BuildBoat.cpp @@ -22,14 +22,14 @@ using namespace Goals; bool BuildBoat::operator==(const BuildBoat & other) const { - return shipyard->o->id == other.shipyard->o->id; + return shipyard == other.shipyard; } TSubgoal BuildBoat::whatToDoToAchieve() { - if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) + if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES) { - return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o)); + return fh->chooseSolution(ai->ah->howToVisitObj(dynamic_cast(shipyard))); } if(shipyard->shipyardStatus() != IShipyard::GOOD) @@ -53,7 +53,7 @@ void BuildBoat::accept(VCAI * ai) throw cannotFulfillGoalException("Can not afford boat"); } - if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) + if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES) { throw cannotFulfillGoalException("Can not build boat in enemy shipyard"); } @@ -64,9 +64,8 @@ void BuildBoat::accept(VCAI * ai) } logAi->trace( - "Building boat at shipyard %s located at %s, estimated boat position %s", - shipyard->o->getObjectName(), - shipyard->o->visitablePos().toString(), + "Building boat at shipyard located at %s, estimated boat position %s", + shipyard->getObject()->visitablePos().toString(), shipyard->bestLocation().toString()); cb->buildBoat(shipyard); @@ -81,5 +80,5 @@ std::string BuildBoat::name() const std::string BuildBoat::completeMessage() const { - return "Boat have been built at " + shipyard->o->visitablePos().toString(); + return "Boat have been built at " + shipyard->getObject()->visitablePos().toString(); } diff --git a/CCallback.cpp b/CCallback.cpp index 948ca259f..b9826e278 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -301,7 +301,7 @@ void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * cu void CCallback::buildBoat( const IShipyard *obj ) { BuildBoat bb; - bb.objid = obj->o->id; + bb.objid = dynamic_cast(obj)->id; sendRequest(&bb); } diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 5cf74ae57..1ad096c6e 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -906,7 +906,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack) case EOpenWindowMode::SHIPYARD_WINDOW: { const IShipyard *sy = IShipyard::castFrom(cl.getObj(ObjectInstanceID(pack.id1))); - callInterfaceIfPresent(cl, sy->o->tempOwner, &IGameEventsReceiver::showShipyardDialog, sy); + callInterfaceIfPresent(cl, sy->getObject()->getOwner(), &IGameEventsReceiver::showShipyardDialog, sy); } break; case EOpenWindowMode::THIEVES_GUILD: diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 981f5f257..cc7294479 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -225,7 +225,6 @@ int CGHeroInstance::maxMovePointsCached(bool onLand, const TurnInfo * ti) const } CGHeroInstance::CGHeroInstance(): - IBoatGenerator(this), tacticFormationEnabled(false), inTownGarrison(false), moveDir(4), @@ -973,6 +972,11 @@ void CGHeroInstance::getOutOffsets(std::vector &offsets) const }; } +const IObjectInterface * CGHeroInstance::getObject() const +{ + return this; +} + int32_t CGHeroInstance::getSpellCost(const spells::Spell * sp) const { return sp->getCost(getSpellSchoolLevel(sp)); diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index d6d1c1cb4..08d6f1cea 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -133,6 +133,7 @@ public: BoatId getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral void getOutOffsets(std::vector &offsets) const override; //offsets to obj pos when we boat can be placed + const IObjectInterface * getObject() const override; ////////////////////////////////////////////////////////////////////////// diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index fa80df1d8..d680f64d3 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -224,8 +224,6 @@ bool CGTownInstance::hasCapitol() const } CGTownInstance::CGTownInstance(): - IShipyard(this), - IMarket(), town(nullptr), builded(0), destroyed(0), @@ -590,6 +588,19 @@ void CGTownInstance::getOutOffsets( std::vector &offsets ) const offsets = {int3(-1,2,0), int3(-3,2,0)}; } +CGTownInstance::EGeneratorState CGTownInstance::shipyardStatus() const +{ + if (!hasBuilt(BuildingID::SHIPYARD)) + return EGeneratorState::UNKNOWN; + + return IShipyard::shipyardStatus(); +} + +const IObjectInterface * CGTownInstance::getObject() const +{ + return this; +} + void CGTownInstance::mergeGarrisonOnSiege() const { auto getWeakestStackSlot = [&](ui64 powerLimit) diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 05a78c90a..1e914df89 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -46,6 +46,8 @@ class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public I { std::string name; // name of town public: + using CGDwelling::getPosition; + enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3}; CTownAndVisitingHero townAndVis; @@ -71,7 +73,6 @@ public: template void serialize(Handler &h, const int version) { h & static_cast(*this); - h & static_cast(*this); h & name; h & builded; h & destroyed; @@ -134,6 +135,8 @@ public: int getSightRadius() const override; //returns sight distance BoatId getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral void getOutOffsets(std::vector &offsets) const override; //offsets to obj pos when we boat can be placed. Parameter will be cleared + EGeneratorState shipyardStatus() const override; + const IObjectInterface * getObject() const override; int getMarketEfficiency() const override; //=market count bool allowsTrade(EMarketMode::EMarketMode mode) const override; std::vector availableItemsIds(EMarketMode::EMarketMode mode) const override; diff --git a/lib/mapObjects/IObjectInterface.cpp b/lib/mapObjects/IObjectInterface.cpp index 5be5edf33..512a52315 100644 --- a/lib/mapObjects/IObjectInterface.cpp +++ b/lib/mapObjects/IObjectInterface.cpp @@ -92,10 +92,10 @@ int3 IBoatGenerator::bestLocation() const for (auto & offset : offsets) { - if(const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map + if(const TerrainTile *tile = getObject()->cb->getTile(getObject()->getPosition() + offset, false)) //tile is in the map { if(tile->terType->isWater() && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat - return o->pos + offset; + return getObject()->getPosition() + offset; } } return int3 (-1,-1,-1); @@ -107,24 +107,14 @@ IBoatGenerator::EGeneratorState IBoatGenerator::shipyardStatus() const const TerrainTile *t = IObjectInterface::cb->getTile(tile); if(!t) return TILE_BLOCKED; //no available water - else if(t->blockingObjects.empty()) + + if(t->blockingObjects.empty()) return GOOD; //OK - else if(t->blockingObjects.front()->ID == Obj::BOAT) + + if(t->blockingObjects.front()->ID == Obj::BOAT) return BOAT_ALREADY_BUILT; //blocked with boat - else - return TILE_BLOCKED; //blocked -} -BoatId IBoatGenerator::getBoatType() const -{ - //We make good ships by default - return EBoatId::BOAT_GOOD; -} - - -IBoatGenerator::IBoatGenerator(const CGObjectInstance *O) -: o(O) -{ + return TILE_BLOCKED; //blocked } void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visitor) const @@ -144,7 +134,7 @@ void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visit out.addTxt(MetaString::ADVOB_TXT, 189); break; case NO_WATER: - logGlobal->error("Shipyard without water! %s \t %d", o->pos.toString(), o->id.getNum()); + logGlobal->error("Shipyard without water at tile %s! ", getObject()->getPosition().toString()); return; } } @@ -155,34 +145,9 @@ void IShipyard::getBoatCost(TResources & cost) const cost[EGameResID::GOLD] = 1000; } -IShipyard::IShipyard(const CGObjectInstance *O) - : IBoatGenerator(O) -{ -} - -IShipyard * IShipyard::castFrom( CGObjectInstance *obj ) -{ - if(!obj) - return nullptr; - - if(obj->ID == Obj::TOWN) - { - return dynamic_cast(obj); - } - else if(obj->ID == Obj::SHIPYARD) - { - return dynamic_cast(obj); - } - else - { - return nullptr; - } -} - const IShipyard * IShipyard::castFrom( const CGObjectInstance *obj ) { - return castFrom(const_cast(obj)); + return dynamic_cast(obj); } - VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/IObjectInterface.h b/lib/mapObjects/IObjectInterface.h index 700898051..d724bb7fc 100644 --- a/lib/mapObjects/IObjectInterface.h +++ b/lib/mapObjects/IObjectInterface.h @@ -78,39 +78,25 @@ public: class DLL_LINKAGE IBoatGenerator { public: - const CGObjectInstance *o; - - IBoatGenerator(const CGObjectInstance *O); virtual ~IBoatGenerator() = default; - virtual BoatId getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral - virtual void getOutOffsets(std::vector &offsets) const =0; //offsets to obj pos when we boat can be placed + virtual const IObjectInterface * getObject() const = 0; + + virtual BoatId getBoatType() const = 0; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral + virtual void getOutOffsets(std::vector & offsets) const = 0; //offsets to obj pos when we boat can be placed int3 bestLocation() const; //returns location when the boat should be placed - enum EGeneratorState {GOOD, BOAT_ALREADY_BUILT, TILE_BLOCKED, NO_WATER}; - EGeneratorState shipyardStatus() const; //0 - can buid, 1 - there is already a boat at dest tile, 2 - dest tile is blocked, 3 - no water + enum EGeneratorState {GOOD, BOAT_ALREADY_BUILT, TILE_BLOCKED, NO_WATER, UNKNOWN}; + virtual EGeneratorState shipyardStatus() const; void getProblemText(MetaString &out, const CGHeroInstance *visitor = nullptr) const; - - template void serialize(Handler &h, const int version) - { - h & o; - } }; class DLL_LINKAGE IShipyard : public IBoatGenerator { public: - IShipyard(const CGObjectInstance *O); - virtual void getBoatCost(ResourceSet & cost) const; static const IShipyard *castFrom(const CGObjectInstance *obj); - static IShipyard *castFrom(CGObjectInstance *obj); - - template void serialize(Handler &h, const int version) - { - h & static_cast(*this); - } }; VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index af5e8edfa..b4d475de1 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1915,12 +1915,6 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const } } cb->showInfoDialog(&iw); - -} - -CGShipyard::CGShipyard() - :IShipyard(this) -{ } void CGShipyard::getOutOffsets( std::vector &offsets ) const @@ -1935,6 +1929,11 @@ void CGShipyard::getOutOffsets( std::vector &offsets ) const }; } +const IObjectInterface * CGShipyard::getObject() const +{ + return this; +} + void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const { if(!cb->gameState()->getPlayerRelations(tempOwner, h->tempOwner)) @@ -1960,6 +1959,11 @@ void CGShipyard::serializeJsonOptions(JsonSerializeFormat& handler) serializeJsonOwner(handler); } +BoatId CGShipyard::getBoatType() const +{ + return EBoatId::BOAT_GOOD; +} + void CCartographer::onHeroVisit( const CGHeroInstance * h ) const { //if player has not bought map of this subtype yet and underground exist for stalagmite cartographer diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 8315a434a..1f8db5b72 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -461,14 +461,11 @@ class DLL_LINKAGE CGShipyard : public CGObjectInstance, public IShipyard { public: void getOutOffsets(std::vector &offsets) const override; //offsets to obj pos when we boat can be placed - CGShipyard(); void onHeroVisit(const CGHeroInstance * h) const override; + const IObjectInterface * getObject() const override; + BoatId getBoatType() const override; + - template void serialize(Handler &h, const int version) - { - h & static_cast(*this); - h & static_cast(*this); - } protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; }; diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index a37a61d16..1a0e2c95d 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -62,7 +62,7 @@ void registerTypesMapObjects1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); @@ -75,9 +75,9 @@ void registerTypesMapObjects1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); // Armed objects - s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b55f2bc00..92761f37d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5627,12 +5627,6 @@ bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID) complain("Cannot build boat in this shipyard!"); return false; } - else if (obj->o->ID == Obj::TOWN - && !static_cast(obj)->hasBuilt(BuildingID::SHIPYARD)) - { - complain("Cannot build boat in the town - no shipyard!"); - return false; - } TResources boatCost; obj->getBoatCost(boatCost); From 487f441f4743ad9d05252dec27eb810d7702c2f9 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 7 Jun 2023 19:51:44 +0300 Subject: [PATCH 07/12] Implemented boat selection for town shipyards --- client/windows/GUIClasses.cpp | 20 ++++++++++--- config/factions/castle.json | 1 + config/factions/conflux.json | 1 + config/factions/fortress.json | 1 + config/factions/necropolis.json | 1 + config/objects/generic.json | 2 +- config/objects/moddables.json | 6 ++-- lib/CTownHandler.cpp | 29 +++++++++++++++++++ lib/CTownHandler.h | 4 +++ lib/GameConstants.h | 6 ++-- lib/TerrainHandler.h | 18 ++++++------ .../CommonConstructors.cpp | 14 +++++++++ .../CommonConstructors.h | 6 +++- lib/mapObjects/CGHeroInstance.cpp | 9 ++---- lib/mapObjects/CGTownInstance.cpp | 8 +---- lib/mapObjects/MiscObjects.cpp | 3 +- lib/mapObjects/ObjectTemplate.cpp | 10 +++---- lib/mapObjects/ObjectTemplate.h | 5 ++-- 18 files changed, 101 insertions(+), 43 deletions(-) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 03c586016..9e0097bcc 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -45,6 +45,9 @@ #include "../../CCallback.h" +#include "../lib/mapObjectConstructors/AObjectTypeHandler.h" +#include "../lib/mapObjectConstructors/CObjectClassesHandler.h" +#include "../lib/mapObjectConstructors/CommonConstructors.h" #include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/mapObjects/CGMarket.h" #include "../lib/ArtifactUtils.h" @@ -1093,11 +1096,20 @@ CShipyardWindow::CShipyardWindow(const TResources & cost, int state, BoatId boat bgWater = std::make_shared("TPSHIPBK", 100, 69); - std::string boatFilenames[3] = {"AB01_", "AB02_", "AB03_"}; + auto handler = CGI->objtypeh->getHandlerFor(Obj::BOAT, boatType); - Point waterCenter = Point(bgWater->pos.x+bgWater->pos.w/2, bgWater->pos.y+bgWater->pos.h/2); - bgShip = std::make_shared(boatFilenames[boatType.getNum()], 0, 7, 120, 96, 0); - bgShip->center(waterCenter); + auto boatConstructor = std::dynamic_pointer_cast(handler); + + assert(boatConstructor); + + if (boatConstructor) + { + std::string boatFilename = boatConstructor->getBoatAnimationName(); + + Point waterCenter = Point(bgWater->pos.x+bgWater->pos.w/2, bgWater->pos.y+bgWater->pos.h/2); + bgShip = std::make_shared(boatFilename, 0, 7, 120, 96, 0); + bgShip->center(waterCenter); + } // Create resource icons and costs. std::string goldValue = std::to_string(cost[EGameResID::GOLD]); diff --git a/config/factions/castle.json b/config/factions/castle.json index 1e5da80c7..fddce6cc9 100644 --- a/config/factions/castle.json +++ b/config/factions/castle.json @@ -148,6 +148,7 @@ "mageGuild" : 4, "warMachine" : "ballista", "moatAbility" : "castleMoat", + "boat" : "boatCastle", // primaryResource not specified so town get both Wood and Ore for resource bonus "buildings" : diff --git a/config/factions/conflux.json b/config/factions/conflux.json index 3e6461ab8..24fa448ff 100644 --- a/config/factions/conflux.json +++ b/config/factions/conflux.json @@ -153,6 +153,7 @@ "primaryResource" : "mercury", "warMachine" : "ballista", "moatAbility" : "castleMoat", + "boat" : "boatNecropolis", "buildings" : { diff --git a/config/factions/fortress.json b/config/factions/fortress.json index b930eb60c..61001da09 100644 --- a/config/factions/fortress.json +++ b/config/factions/fortress.json @@ -148,6 +148,7 @@ "mageGuild" : 3, "warMachine" : "firstAidTent", "moatAbility" : "fortressMoat", + "boat" : "boatFortress", // primaryResource not specified so town get both Wood and Ore for resource bonus "buildings" : diff --git a/config/factions/necropolis.json b/config/factions/necropolis.json index 94c1f4263..383a04115 100644 --- a/config/factions/necropolis.json +++ b/config/factions/necropolis.json @@ -153,6 +153,7 @@ "mageGuild" : 5, "warMachine" : "firstAidTent", "moatAbility" : "necropolisMoat", + "boat" : "boatNecropolis", // primaryResource not specified so town get both Wood and Ore for resource bonus "buildings" : diff --git a/config/objects/generic.json b/config/objects/generic.json index 32d172cdd..c3de10cab 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -310,7 +310,7 @@ "rmg" : { "value" : 500, "rarity" : 100 - } + }, "visitText" : 127, "spell" : { "level" : 1 diff --git a/config/objects/moddables.json b/config/objects/moddables.json index 643b66dcb..c986c7cdf 100644 --- a/config/objects/moddables.json +++ b/config/objects/moddables.json @@ -146,21 +146,21 @@ } }, "types" : { - "evil" : // Necropolis + "boatNecropolis" : // Necropolis { "index" : 0, "actualAnimation" : "AB01_.def", "overlayAnimation" : "ABM01_.def", "flagAnimations" : ["ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"] }, - "good" : // Castle + "boatCastle" : // Castle { "index" : 1, "actualAnimation" : "AB02_.def", "overlayAnimation" : "ABM02_.def", "flagAnimations" : ["ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"] }, - "neutral" : { // Fortress + "boatFortress" : { // Fortress "index" : 2, "actualAnimation" : "AB03_.def", "overlayAnimation" : "ABM03_.def", diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index a2ce90c94..018388dbc 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -893,6 +893,16 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source) warMachinesToLoad[town] = source["warMachine"]; + + town->shipyardBoat = EBoatId::NONE; + if (!source["boat"].isNull()) + { + VLC->modh->identifiers.requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID) + { + town->shipyardBoat = BoatId(boatTypeID); + }); + } + town->mageLevel = static_cast(source["mageGuild"].Float()); town->namesCount = 0; @@ -1148,6 +1158,25 @@ void CTownHandler::afterLoadFinalization() initializeRequirements(); initializeOverridden(); initializeWarMachines(); + + for(auto & faction : objects) + { + if (!faction->town) + continue; + + bool hasBoat = faction->town->shipyardBoat != EBoatId::NONE; + bool hasShipyard = faction->town->buildings.count(BuildingID::SHIPYARD); + + if ( hasBoat && !hasShipyard ) + logMod->warn("Town %s has boat but has no shipyard!", faction->getJsonKey()); + + if ( !hasBoat && hasShipyard ) + { + logMod->warn("Town %s has shipyard but has no boat set!", faction->getJsonKey()); + // Mod compatibility for 1.3 + faction->town->shipyardBoat = EBoatId::CASTLE; + } + } } void CTownHandler::initializeRequirements() diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index 983611d0c..48e1f2bb5 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -281,6 +281,9 @@ public: GameResID primaryRes; ArtifactID warMachine; SpellID moatAbility; + + /// boat that will be built by town shipyard, if exists + BoatId shipyardBoat; // default chance for hero of specific class to appear in tavern, if field "tavern" was not set // resulting chance = sqrt(town.chance * heroClass.chance) ui32 defaultTavernChance; @@ -346,6 +349,7 @@ public: h & mageLevel; h & primaryRes; h & warMachine; + h & shipyardBoat; h & clientInfo; h & moatAbility; h & defaultTavernChance; diff --git a/lib/GameConstants.h b/lib/GameConstants.h index af8dfe4f2..4192ba1eb 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -1285,9 +1285,9 @@ class BattleField : public BaseForID enum class EBoatId { NONE = -1, - BOAT_EVIL = 0, - BOAT_GOOD, - BOAT_NEUTRAL + NECROPOLIS = 0, + CASTLE, + FORTRESS }; using BoatId = Identifier; diff --git a/lib/TerrainHandler.h b/lib/TerrainHandler.h index e9b2fa364..6d23c59fa 100644 --- a/lib/TerrainHandler.h +++ b/lib/TerrainHandler.h @@ -26,6 +26,15 @@ class DLL_LINKAGE TerrainType : public EntityT TerrainId id; ui8 passabilityType; + enum PassabilityType : ui8 + { + LAND = 1, + WATER = 2, + SURFACE = 4, + SUBTERRANEAN = 8, + ROCK = 16 + }; + public: int32_t getIndex() const override { return id.getNum(); } int32_t getIconIndex() const override { return 0; } @@ -37,15 +46,6 @@ public: std::string getNameTextID() const override; std::string getNameTranslated() const override; - enum PassabilityType : ui8 - { - LAND = 1, - WATER = 2, - SURFACE = 4, - SUBTERRANEAN = 8, - ROCK = 16 - }; - std::vector battleFields; std::vector prohibitTransitions; ColorRGBA minimapBlocked; diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index 7f7a5464e..b25ab34d5 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -289,11 +289,25 @@ CGObjectInstance * BoatInstanceConstructor::create(std::shared_ptrwarn("Boat of type %s has no templates suitable for water!", getJsonKey()); + } +} + void MarketInstanceConstructor::initTypeData(const JsonNode & input) { for(auto & element : input["modes"].Vector()) diff --git a/lib/mapObjectConstructors/CommonConstructors.h b/lib/mapObjectConstructors/CommonConstructors.h index d50630714..359c57b93 100644 --- a/lib/mapObjectConstructors/CommonConstructors.h +++ b/lib/mapObjectConstructors/CommonConstructors.h @@ -110,7 +110,7 @@ public: } }; -class BoatInstanceConstructor : public CDefaultObjectTypeHandler +class DLL_LINKAGE BoatInstanceConstructor : public CDefaultObjectTypeHandler { protected: void initTypeData(const JsonNode & config) override; @@ -127,6 +127,10 @@ protected: public: CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + void afterLoadFinalization() override; + + /// Returns boat preview animation, for use in Shipyards + std::string getBoatAnimationName() const; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index cc7294479..95a831445 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -953,13 +953,8 @@ si32 CGHeroInstance::getManaNewTurn() const BoatId CGHeroInstance::getBoatType() const { - switch (type->heroClass->getAlignment()) - { - case EAlignment::EVIL: return EBoatId::BOAT_EVIL; - case EAlignment::GOOD: return EBoatId::BOAT_GOOD; - case EAlignment::NEUTRAL: return EBoatId::BOAT_NEUTRAL; - default: return EBoatId::NONE; - } + // hero can only generate boat via "Summon Boat" spell which always create same boat as in Necropolis shipyard + return EBoatId::NECROPOLIS; } void CGHeroInstance::getOutOffsets(std::vector &offsets) const diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index d680f64d3..208229685 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -682,13 +682,7 @@ void CGTownInstance::clearArmy() const BoatId CGTownInstance::getBoatType() const { - switch (town->faction->alignment) - { - case EAlignment::EVIL : return EBoatId::BOAT_EVIL; - case EAlignment::GOOD : return EBoatId::BOAT_GOOD; - case EAlignment::NEUTRAL : return EBoatId::BOAT_NEUTRAL; - default: return EBoatId::NONE; - } + return town->shipyardBoat; } int CGTownInstance::getMarketEfficiency() const diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index b4d475de1..ec4e065aa 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1961,7 +1961,8 @@ void CGShipyard::serializeJsonOptions(JsonSerializeFormat& handler) BoatId CGShipyard::getBoatType() const { - return EBoatId::BOAT_GOOD; + // In H3, external shipyard will always create same boat as castle + return EBoatId::CASTLE; } void CCartographer::onHeroVisit( const CGHeroInstance * h ) const diff --git a/lib/mapObjects/ObjectTemplate.cpp b/lib/mapObjects/ObjectTemplate.cpp index 83bfd2b26..83ac7236c 100644 --- a/lib/mapObjects/ObjectTemplate.cpp +++ b/lib/mapObjects/ObjectTemplate.cpp @@ -164,7 +164,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser) } //assuming that object can be placed on other land terrains - anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER); + anyLandTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER); id = Obj(boost::lexical_cast(strings[5])); subid = boost::lexical_cast(strings[6]); @@ -230,7 +230,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader) } //assuming that object can be placed on other land terrains - anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER); + anyLandTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER); id = Obj(reader.readUInt32()); subid = reader.readUInt32(); @@ -277,11 +277,11 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain) allowedTerrains.insert(TerrainId(identifier)); }); } - anyTerrain = false; + anyLandTerrain = false; } else { - anyTerrain = true; + anyLandTerrain = true; } auto charToTile = [&](const char & ch) -> ui8 @@ -557,7 +557,7 @@ void ObjectTemplate::calculateVisitableOffset() bool ObjectTemplate::canBePlacedAt(TerrainId terrainID) const { - if (anyTerrain) + if (anyLandTerrain) { const auto & terrain = VLC->terrainTypeHandler->getById(terrainID); return terrain->isLand() && terrain->isPassable(); diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index ef48b0b57..4560d75ea 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -36,7 +36,7 @@ class DLL_LINKAGE ObjectTemplate std::set allowedTerrains; /// or, allow placing object on any terrain - bool anyTerrain; + bool anyLandTerrain; void afterLoadFixup(); @@ -109,7 +109,7 @@ public: inline bool canBePlacedAtAnyTerrain() const { - return anyTerrain; + return anyLandTerrain; }; const std::set& getAllowedTerrains() const @@ -159,6 +159,7 @@ public: { h & usedTiles; h & allowedTerrains; + h & anyLandTerrain; h & animationFile; h & stringID; h & id; From 4d947be28746f3fee1ea47ed5f3c68b22c159b67 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 8 Jun 2023 00:04:13 +0300 Subject: [PATCH 08/12] Adventure map shipyard nwo has configurable boat type --- cmake_modules/VCMI_lib.cmake | 2 + config/objects/generic.json | 3 +- .../CObjectClassesHandler.cpp | 9 ++-- .../HillFortInstanceConstructor.cpp | 7 +-- .../ShipyardInstanceConstructor.cpp | 47 +++++++++++++++++++ .../ShipyardInstanceConstructor.h | 34 ++++++++++++++ lib/mapObjects/MiscObjects.cpp | 5 -- lib/mapObjects/MiscObjects.h | 16 +++++-- lib/mapping/MapFormatH3M.cpp | 6 +-- lib/mapping/MapFormatH3M.h | 2 +- lib/registerTypes/RegisterTypes.h | 2 + 11 files changed, 109 insertions(+), 24 deletions(-) create mode 100644 lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp create mode 100644 lib/mapObjectConstructors/ShipyardInstanceConstructor.h diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 1be2e2289..c7b52da26 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -71,6 +71,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.cpp + ${MAIN_LIB_DIR}/mapObjectConstructors/ShipyardInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjects/CArmedInstance.cpp @@ -380,6 +381,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/IObjectInfo.h ${MAIN_LIB_DIR}/mapObjectConstructors/RandomMapInfo.h + ${MAIN_LIB_DIR}/mapObjectConstructors/ShipyardInstanceConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/SObjectSounds.h diff --git a/config/objects/generic.json b/config/objects/generic.json index c3de10cab..9dfa2ade0 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -488,7 +488,8 @@ "index" : 0, "aiValue" : 1000, "rmg" : { - } + }, + "boat" : "boatCastle" } } }, diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 6fa6777b5..82416c320 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -21,13 +21,14 @@ #include "../JsonNode.h" #include "../CSoundBase.h" +#include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" -#include "../mapObjectConstructors/CBankInstanceConstructor.h" -#include "../mapObjectConstructors/ShrineInstanceConstructor.h" #include "../mapObjectConstructors/HillFortInstanceConstructor.h" -#include "../mapObjects/CQuest.h" +#include "../mapObjectConstructors/ShipyardInstanceConstructor.h" +#include "../mapObjectConstructors/ShrineInstanceConstructor.h" #include "../mapObjects/CGPandoraBox.h" +#include "../mapObjects/CQuest.h" #include "../mapObjects/ObjectTemplate.h" VCMI_LIB_NAMESPACE_BEGIN @@ -48,6 +49,7 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER_CLASS("market", MarketInstanceConstructor); SET_HANDLER_CLASS("shrine", ShrineInstanceConstructor); SET_HANDLER_CLASS("hillFort", HillFortInstanceConstructor); + SET_HANDLER_CLASS("shipyard", ShipyardInstanceConstructor); SET_HANDLER_CLASS("static", CObstacleConstructor); SET_HANDLER_CLASS("", CObstacleConstructor); @@ -81,7 +83,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("resource", CGResource); SET_HANDLER("scholar", CGScholar); SET_HANDLER("seerHut", CGSeerHut); - SET_HANDLER("shipyard", CGShipyard); SET_HANDLER("sign", CGSignBottle); SET_HANDLER("siren", CGSirens); SET_HANDLER("monolith", CGMonolith); diff --git a/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp b/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp index 128be8300..9276fc4c0 100644 --- a/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp @@ -29,17 +29,12 @@ CGObjectInstance * HillFortInstanceConstructor::create(std::shared_ptrappearance = tmpl; + fort->upgradeCostPercentage = parameters["upgradeCostFactor"].convertTo>(); return fort; } void HillFortInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const { - HillFort * fort = dynamic_cast(object); - - if(!fort) - throw std::runtime_error("Unexpected object instance in HillFortInstanceConstructor!"); - - fort->upgradeCostPercentage = parameters["upgradeCostFactor"].convertTo>(); } std::unique_ptr HillFortInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const diff --git a/lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp b/lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp new file mode 100644 index 000000000..24b489f10 --- /dev/null +++ b/lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp @@ -0,0 +1,47 @@ +/* +* ShipyardInstanceConstructor.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#include "StdInc.h" +#include "ShipyardInstanceConstructor.h" + +#include "../mapObjects/MiscObjects.h" +#include "IObjectInfo.h" +#include "../CModHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +void ShipyardInstanceConstructor::initTypeData(const JsonNode & config) +{ + parameters = config; +} + +CGObjectInstance * ShipyardInstanceConstructor::create(std::shared_ptr tmpl) const +{ + CGShipyard * shipyard = new CGShipyard; + + preInitObject(shipyard); + + if(tmpl) + shipyard->appearance = tmpl; + + shipyard->createdBoat = BoatId(*VLC->modh->identifiers.getIdentifier("core:boat", parameters["boat"])); + + return shipyard; +} + +void ShipyardInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +{ +} + +std::unique_ptr ShipyardInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const +{ + return nullptr; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/ShipyardInstanceConstructor.h b/lib/mapObjectConstructors/ShipyardInstanceConstructor.h new file mode 100644 index 000000000..6c2805c1e --- /dev/null +++ b/lib/mapObjectConstructors/ShipyardInstanceConstructor.h @@ -0,0 +1,34 @@ +/* +* ShipyardInstanceConstructor.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#pragma once + +#include "AObjectTypeHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class ShipyardInstanceConstructor final : public AObjectTypeHandler +{ + JsonNode parameters; + +protected: + void initTypeData(const JsonNode & config) override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + +public: + template void serialize(Handler &h, const int version) + { + h & static_cast(*this); + h & parameters; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index ec4e065aa..42c168463 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -2174,11 +2174,6 @@ void HillFort::onHeroVisit(const CGHeroInstance * h) const openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum()); } -void HillFort::initObj(CRandomGenerator & rand) -{ - VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); -} - void HillFort::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const { int32_t level = stack.type->getLevel(); diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 1f8db5b72..a13a2c5d4 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -15,7 +15,6 @@ VCMI_LIB_NAMESPACE_BEGIN class CMap; -class HillFortInstanceConstructor; // This one teleport-specific, but has to be available everywhere in callbacks and netpacks // For now it's will be there till teleports code refactored and moved into own file @@ -459,12 +458,22 @@ public: class DLL_LINKAGE CGShipyard : public CGObjectInstance, public IShipyard { -public: - void getOutOffsets(std::vector &offsets) const override; //offsets to obj pos when we boat can be placed + friend class ShipyardInstanceConstructor; + + BoatId createdBoat; + +protected: + void getOutOffsets(std::vector & offsets) const override; void onHeroVisit(const CGHeroInstance * h) const override; const IObjectInterface * getObject() const override; BoatId getBoatType() const override; +public: + template void serialize(Handler & h, const int version) + { + h & static_cast(*this); + h & createdBoat; + } protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; @@ -558,7 +567,6 @@ class DLL_LINKAGE HillFort : public CGObjectInstance, public ICreatureUpgrader std::vector upgradeCostPercentage; protected: - void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index e3dfa3a2c..aaca64bb2 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1292,9 +1292,9 @@ CGObjectInstance * CMapLoaderH3M::readQuestGuard(const int3 & mapPosition) return guard; } -CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & mapPosition) +CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & mapPosition, std::shared_ptr objectTemplate) { - auto * object = new CGShipyard(); + auto * object = readGeneric(mapPosition, objectTemplate); setOwnerAndValidate(mapPosition, object, reader->readPlayer32()); return object; } @@ -1448,7 +1448,7 @@ CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr objTempl); CGObjectInstance * readQuestGuard(const int3 & position); - CGObjectInstance * readShipyard(const int3 & mapPosition); + CGObjectInstance * readShipyard(const int3 & mapPosition, std::shared_ptr objectTemplate); CGObjectInstance * readLighthouse(const int3 & mapPosition); CGObjectInstance * readGeneric(const int3 & position, std::shared_ptr objectTemplate); CGObjectInstance * readBank(const int3 & position, std::shared_ptr objectTemplate); diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 1a0e2c95d..83ee7379b 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -22,6 +22,7 @@ #include "../mapObjectConstructors/CommonConstructors.h" #include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../mapObjectConstructors/HillFortInstanceConstructor.h" +#include "../mapObjectConstructors/ShipyardInstanceConstructor.h" #include "../mapObjectConstructors/ShrineInstanceConstructor.h" #include "../mapObjects/MapObjects.h" #include "../mapObjects/CGTownBuilding.h" @@ -102,6 +103,7 @@ void registerTypesMapObjectTypes(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); #define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType >() From 77b58bc66dbd9d1fe824f049d887530fc0c6d761 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 8 Jun 2023 00:42:47 +0300 Subject: [PATCH 09/12] Cleaned up object constructors to reduce duplicated code --- config/objects/moddables.json | 8 +-- config/schemas/faction.json | 4 ++ .../AObjectTypeHandler.cpp | 5 ++ .../AObjectTypeHandler.h | 2 +- .../CBankInstanceConstructor.cpp | 9 +-- .../CBankInstanceConstructor.h | 3 +- .../CDefaultObjectTypeHandler.h | 57 ++++++++--------- .../CObjectClassesHandler.cpp | 5 ++ .../CommonConstructors.cpp | 61 +++++++------------ .../CommonConstructors.h | 27 ++++---- .../HillFortInstanceConstructor.cpp | 20 +----- .../HillFortInstanceConstructor.h | 10 +-- .../ShipyardInstanceConstructor.cpp | 21 +------ .../ShipyardInstanceConstructor.h | 10 +-- .../ShrineInstanceConstructor.cpp | 25 +------- .../ShrineInstanceConstructor.h | 10 +-- lib/mapObjects/MiscObjects.cpp | 9 ++- lib/mapObjects/MiscObjects.h | 7 +-- lib/rmg/modificators/TreasurePlacer.cpp | 1 + 19 files changed, 111 insertions(+), 183 deletions(-) diff --git a/config/objects/moddables.json b/config/objects/moddables.json index c986c7cdf..bb48baadf 100644 --- a/config/objects/moddables.json +++ b/config/objects/moddables.json @@ -146,21 +146,19 @@ } }, "types" : { - "boatNecropolis" : // Necropolis - { + "boatNecropolis" : { "index" : 0, "actualAnimation" : "AB01_.def", "overlayAnimation" : "ABM01_.def", "flagAnimations" : ["ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"] }, - "boatCastle" : // Castle - { + "boatCastle" : { "index" : 1, "actualAnimation" : "AB02_.def", "overlayAnimation" : "ABM02_.def", "flagAnimations" : ["ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"] }, - "boatFortress" : { // Fortress + "boatFortress" : { "index" : 2, "actualAnimation" : "AB03_.def", "overlayAnimation" : "ABM03_.def", diff --git a/config/schemas/faction.json b/config/schemas/faction.json index c646065e1..899c9eb09 100644 --- a/config/schemas/faction.json +++ b/config/schemas/faction.json @@ -124,6 +124,10 @@ "type" : "string", "description" : "Identifier of war machine produced by blacksmith in town" }, + "boat" : { + "type" : "string", + "description" : "Identifier of boat type that is produced by shipyard in town, if any" + }, "horde" : { "type" : "array", "maxItems" : 2, diff --git a/lib/mapObjectConstructors/AObjectTypeHandler.cpp b/lib/mapObjectConstructors/AObjectTypeHandler.cpp index 2273415c9..da12685fc 100644 --- a/lib/mapObjectConstructors/AObjectTypeHandler.cpp +++ b/lib/mapObjectConstructors/AObjectTypeHandler.cpp @@ -11,6 +11,7 @@ #include "StdInc.h" #include "AObjectTypeHandler.h" +#include "IObjectInfo.h" #include "../CGeneralTextHandler.h" #include "../VCMI_Lib.h" #include "../mapObjects/CGObjectInstance.h" @@ -221,5 +222,9 @@ void AObjectTypeHandler::afterLoadFinalization() { } +std::unique_ptr AObjectTypeHandler::getObjectInfo(std::shared_ptr tmpl) const +{ + return nullptr; +} VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/AObjectTypeHandler.h b/lib/mapObjectConstructors/AObjectTypeHandler.h index 56dcec4de..9ce7aabee 100644 --- a/lib/mapObjectConstructors/AObjectTypeHandler.h +++ b/lib/mapObjectConstructors/AObjectTypeHandler.h @@ -107,7 +107,7 @@ public: virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0; /// Returns object configuration, if available. Otherwise returns NULL - virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const = 0; + virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.cpp b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp index fb55d43f1..348527285 100644 --- a/lib/mapObjectConstructors/CBankInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp @@ -32,11 +32,6 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input) bankResetDuration = static_cast(input["resetDuration"].Float()); } -CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr tmpl) const -{ - return createTyped(tmpl); -} - BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const { BankConfig bc; @@ -60,10 +55,8 @@ BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRan return bc; } -void CBankInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +void CBankInstanceConstructor::randomizeObject(CBank * bank, CRandomGenerator & rng) const { - auto * bank = dynamic_cast(object); - bank->resetDuration = bankResetDuration; si32 totalChance = 0; diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.h b/lib/mapObjectConstructors/CBankInstanceConstructor.h index 232e61676..d9685f29c 100644 --- a/lib/mapObjectConstructors/CBankInstanceConstructor.h +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.h @@ -87,8 +87,7 @@ public: // all banks of this type will be reset N days after clearing, si32 bankResetDuration = 0; - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + void randomizeObject(CBank * object, CRandomGenerator & rng) const override; bool hasNameTextID() const override; diff --git a/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h index 766b01fea..15c545906 100644 --- a/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h +++ b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h @@ -13,39 +13,40 @@ VCMI_LIB_NAMESPACE_BEGIN -/// Class that is used for objects that do not have dedicated handler +/// Class that is used as base for multiple object constructors template class CDefaultObjectTypeHandler : public AObjectTypeHandler { + void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const final + { + ObjectType * castedObject = dynamic_cast(object); + + if(castedObject == nullptr) + throw std::runtime_error("Unexpected object type!"); + + randomizeObject(castedObject, rng); + } + + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const final + { + ObjectType * result = createObject(); + + preInitObject(result); + + if(tmpl) + result->appearance = tmpl; + + initializeObject(result); + + return result; + } + protected: - ObjectType * createTyped(std::shared_ptr tmpl /* = nullptr */) const + virtual void initializeObject(ObjectType * object) const {} + virtual void randomizeObject(ObjectType * object, CRandomGenerator & rng) const {} + virtual ObjectType * createObject() const { - auto obj = new ObjectType(); - preInitObject(obj); - - //Set custom template or leave null - if (tmpl) - { - obj->appearance = tmpl; - } - - return obj; - } -public: - CDefaultObjectTypeHandler() {} - - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override - { - return createTyped(tmpl); - } - - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override - { - } - - std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override - { - return nullptr; + return new ObjectType(); } }; diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 82416c320..71f47dbcd 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -30,6 +30,11 @@ #include "../mapObjects/CGPandoraBox.h" #include "../mapObjects/CQuest.h" #include "../mapObjects/ObjectTemplate.h" +#include "../mapObjects/CGMarket.h" +#include "../mapObjects/MiscObjects.h" +#include "../mapObjects/CGHeroInstance.h" +#include "../mapObjects/CGTownInstance.h" + VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index b25ab34d5..8824a75bc 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -15,14 +15,18 @@ #include "../CHeroHandler.h" #include "../CModHandler.h" #include "../IGameCallback.h" +#include "../JsonRandom.h" #include "../StringConstants.h" #include "../TerrainHandler.h" + #include "../mapObjects/CBank.h" #include "../mapObjects/CGHeroInstance.h" +#include "../mapObjects/CGMarket.h" #include "../mapObjects/CGTownInstance.h" +#include "../mapObjects/MiscObjects.h" #include "../mapObjects/ObjectTemplate.h" + #include "../mapping/CMapDefines.h" -#include "../JsonRandom.h" VCMI_LIB_NAMESPACE_BEGIN @@ -69,15 +73,13 @@ bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, std return filters.count(templ->stringID) != 0 && filters.at(templ->stringID).test(buildTest); } -CGObjectInstance * CTownInstanceConstructor::create(std::shared_ptr tmpl) const +void CTownInstanceConstructor::initializeObject(CGTownInstance * obj) const { - CGTownInstance * obj = createTyped(tmpl); obj->town = faction->town; obj->tempOwner = PlayerColor::NEUTRAL; - return obj; } -void CTownInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const { auto templ = getOverride(CGObjectInstance::cb->getTile(object->pos)->terType->getId(), object); if(templ) @@ -121,14 +123,12 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, std return false; } -CGObjectInstance * CHeroInstanceConstructor::create(std::shared_ptr tmpl) const +void CHeroInstanceConstructor::initializeObject(CGHeroInstance * obj) const { - CGHeroInstance * obj = createTyped(tmpl); obj->type = nullptr; //FIXME: set to valid value. somehow. - return obj; } -void CHeroInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, CRandomGenerator & rng) const { } @@ -172,20 +172,17 @@ bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance * obj, st return false; } -CGObjectInstance * CDwellingInstanceConstructor::create(std::shared_ptr tmpl) const +void CDwellingInstanceConstructor::initializeObject(CGDwelling * obj) const { - CGDwelling * obj = createTyped(tmpl); - obj->creatures.resize(availableCreatures.size()); for(const auto & entry : availableCreatures) { for(const CCreature * cre : entry) obj->creatures.back().second.push_back(cre->getId()); } - return obj; } -void CDwellingInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator &rng) const +void CDwellingInstanceConstructor::randomizeObject(CGDwelling * object, CRandomGenerator &rng) const { auto * dwelling = dynamic_cast(object); @@ -274,9 +271,8 @@ void BoatInstanceConstructor::initTypeData(const JsonNode & input) bonuses = JsonRandom::loadBonuses(input["bonuses"]); } -CGObjectInstance * BoatInstanceConstructor::create(std::shared_ptr tmpl) const +void BoatInstanceConstructor::initializeObject(CGBoat * boat) const { - CGBoat * boat = createTyped(tmpl); boat->layer = layer; boat->actualAnimation = actualAnimation; boat->overlayAnimation = overlayAnimation; @@ -285,8 +281,6 @@ CGObjectInstance * BoatInstanceConstructor::create(std::shared_ptronboardVisitAllowed = onboardVisitAllowed; for(auto & b : bonuses) boat->addNewBonus(std::make_shared(b)); - - return boat; } std::string BoatInstanceConstructor::getBoatAnimationName() const @@ -294,11 +288,6 @@ std::string BoatInstanceConstructor::getBoatAnimationName() const return actualAnimation; } -void BoatInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const -{ - -} - void BoatInstanceConstructor::afterLoadFinalization() { if (layer == EPathfindingLayer::SAIL) @@ -323,31 +312,25 @@ void MarketInstanceConstructor::initTypeData(const JsonNode & input) speech = input["speech"].String(); } -CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptr tmpl) const +CGMarket * MarketInstanceConstructor::createObject() const { - CGMarket * market = nullptr; if(marketModes.size() == 1) { switch(*marketModes.begin()) { case EMarketMode::ARTIFACT_RESOURCE: case EMarketMode::RESOURCE_ARTIFACT: - market = new CGBlackMarket; - break; - + return new CGBlackMarket; + case EMarketMode::RESOURCE_SKILL: - market = new CGUniversity; - break; + return new CGUniversity; } } - - if(!market) - market = new CGMarket; - - preInitObject(market); + return new CGMarket; +} - if(tmpl) - market->appearance = tmpl; +void MarketInstanceConstructor::initializeObject(CGMarket * market) const +{ market->marketModes = marketModes; market->marketEfficiency = marketEfficiency; @@ -357,11 +340,9 @@ CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptrspeech = VLC->generaltexth->translate(speech); - - return market; } -void MarketInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +void MarketInstanceConstructor::randomizeObject(CGMarket * object, CRandomGenerator & rng) const { if(auto * university = dynamic_cast(object)) { diff --git a/lib/mapObjectConstructors/CommonConstructors.h b/lib/mapObjectConstructors/CommonConstructors.h index 359c57b93..a93f36943 100644 --- a/lib/mapObjectConstructors/CommonConstructors.h +++ b/lib/mapObjectConstructors/CommonConstructors.h @@ -12,20 +12,19 @@ #include "AObjectTypeHandler.h" #include "CDefaultObjectTypeHandler.h" -#include "../mapObjects/CGMarket.h" -#include "../mapObjects/MiscObjects.h" -#include "../mapObjects/CGHeroInstance.h" -#include "../mapObjects/CGTownInstance.h" #include "../LogicalExpression.h" VCMI_LIB_NAMESPACE_BEGIN +class CGArtifact; class CGObjectInstance; class CGTownInstance; class CGHeroInstance; class CGDwelling; +class CGMarket; class CHeroClass; class CBank; +class CGBoat; class CFaction; class CStackBasicDescriptor; @@ -46,8 +45,8 @@ public: CFaction * faction = nullptr; std::map> filters; - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + void initializeObject(CGTownInstance * object) const override; + void randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const override; void afterLoadFinalization() override; template void serialize(Handler &h, const int version) @@ -70,8 +69,8 @@ public: CHeroClass * heroClass = nullptr; std::map> filters; - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + void initializeObject(CGHeroInstance * object) const override; + void randomizeObject(CGHeroInstance * object, CRandomGenerator & rng) const override; void afterLoadFinalization() override; template void serialize(Handler &h, const int version) @@ -96,8 +95,8 @@ protected: public: bool hasNameTextID() const override; - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + void initializeObject(CGDwelling * object) const override; + void randomizeObject(CGDwelling * object, CRandomGenerator & rng) const override; bool producesCreature(const CCreature * crea) const; std::vector getProducedCreatures() const; @@ -125,8 +124,7 @@ protected: std::array flagAnimations; public: - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + void initializeObject(CGBoat * object) const override; void afterLoadFinalization() override; /// Returns boat preview animation, for use in Shipyards @@ -157,8 +155,9 @@ protected: std::string title, speech; public: - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + CGMarket * createObject() const override; + void initializeObject(CGMarket * object) const override; + void randomizeObject(CGMarket * object, CRandomGenerator & rng) const override; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp b/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp index 9276fc4c0..b325edde3 100644 --- a/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/HillFortInstanceConstructor.cpp @@ -11,7 +11,6 @@ #include "HillFortInstanceConstructor.h" #include "../mapObjects/MiscObjects.h" -#include "IObjectInfo.h" VCMI_LIB_NAMESPACE_BEGIN @@ -20,26 +19,9 @@ void HillFortInstanceConstructor::initTypeData(const JsonNode & config) parameters = config; } -CGObjectInstance * HillFortInstanceConstructor::create(std::shared_ptr tmpl) const +void HillFortInstanceConstructor::initializeObject(HillFort * fort) const { - HillFort * fort = new HillFort; - - preInitObject(fort); - - if(tmpl) - fort->appearance = tmpl; - fort->upgradeCostPercentage = parameters["upgradeCostFactor"].convertTo>(); - return fort; -} - -void HillFortInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const -{ -} - -std::unique_ptr HillFortInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const -{ - return nullptr; } VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/HillFortInstanceConstructor.h b/lib/mapObjectConstructors/HillFortInstanceConstructor.h index fc93b8876..526e66571 100644 --- a/lib/mapObjectConstructors/HillFortInstanceConstructor.h +++ b/lib/mapObjectConstructors/HillFortInstanceConstructor.h @@ -9,19 +9,19 @@ */ #pragma once -#include "AObjectTypeHandler.h" +#include "CDefaultObjectTypeHandler.h" VCMI_LIB_NAMESPACE_BEGIN -class HillFortInstanceConstructor final : public AObjectTypeHandler +class HillFort; + +class HillFortInstanceConstructor final : public CDefaultObjectTypeHandler { JsonNode parameters; protected: void initTypeData(const JsonNode & config) override; - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; - std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + void initializeObject(HillFort * object) const override; public: template void serialize(Handler &h, const int version) diff --git a/lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp b/lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp index 24b489f10..20052b57f 100644 --- a/lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp @@ -11,7 +11,6 @@ #include "ShipyardInstanceConstructor.h" #include "../mapObjects/MiscObjects.h" -#include "IObjectInfo.h" #include "../CModHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -21,27 +20,9 @@ void ShipyardInstanceConstructor::initTypeData(const JsonNode & config) parameters = config; } -CGObjectInstance * ShipyardInstanceConstructor::create(std::shared_ptr tmpl) const +void ShipyardInstanceConstructor::initializeObject(CGShipyard * shipyard) const { - CGShipyard * shipyard = new CGShipyard; - - preInitObject(shipyard); - - if(tmpl) - shipyard->appearance = tmpl; - shipyard->createdBoat = BoatId(*VLC->modh->identifiers.getIdentifier("core:boat", parameters["boat"])); - - return shipyard; -} - -void ShipyardInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const -{ -} - -std::unique_ptr ShipyardInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const -{ - return nullptr; } VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/ShipyardInstanceConstructor.h b/lib/mapObjectConstructors/ShipyardInstanceConstructor.h index 6c2805c1e..c0d47be4f 100644 --- a/lib/mapObjectConstructors/ShipyardInstanceConstructor.h +++ b/lib/mapObjectConstructors/ShipyardInstanceConstructor.h @@ -9,19 +9,19 @@ */ #pragma once -#include "AObjectTypeHandler.h" +#include "CDefaultObjectTypeHandler.h" VCMI_LIB_NAMESPACE_BEGIN -class ShipyardInstanceConstructor final : public AObjectTypeHandler +class CGShipyard; + +class ShipyardInstanceConstructor final : public CDefaultObjectTypeHandler { JsonNode parameters; protected: void initTypeData(const JsonNode & config) override; - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; - std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + void initializeObject(CGShipyard * object) const override; public: template void serialize(Handler &h, const int version) diff --git a/lib/mapObjectConstructors/ShrineInstanceConstructor.cpp b/lib/mapObjectConstructors/ShrineInstanceConstructor.cpp index 1e8c8b72d..a9a7dbe78 100644 --- a/lib/mapObjectConstructors/ShrineInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/ShrineInstanceConstructor.cpp @@ -10,7 +10,6 @@ #include "StdInc.h" #include "ShrineInstanceConstructor.h" -#include "IObjectInfo.h" #include "../mapObjects/MiscObjects.h" #include "../JsonRandom.h" #include "../IGameCallback.h" @@ -22,25 +21,8 @@ void ShrineInstanceConstructor::initTypeData(const JsonNode & config) parameters = config; } -CGObjectInstance * ShrineInstanceConstructor::create(std::shared_ptr tmpl) const +void ShrineInstanceConstructor::randomizeObject(CGShrine * shrine, CRandomGenerator & rng) const { - CGShrine * shrine = new CGShrine; - - preInitObject(shrine); - - if(tmpl) - shrine->appearance = tmpl; - - return shrine; -} - -void ShrineInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const -{ - CGShrine * shrine = dynamic_cast(object); - - if (!shrine) - throw std::runtime_error("Unexpected object instance in ShrineInstanceConstructor!"); - auto visitTextParameter = parameters["visitText"]; if (visitTextParameter.isNumber()) @@ -57,9 +39,4 @@ void ShrineInstanceConstructor::configureObject(CGObjectInstance * object, CRand } } -std::unique_ptr ShrineInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const -{ - return nullptr; -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/ShrineInstanceConstructor.h b/lib/mapObjectConstructors/ShrineInstanceConstructor.h index 1350f92f4..0ceb22690 100644 --- a/lib/mapObjectConstructors/ShrineInstanceConstructor.h +++ b/lib/mapObjectConstructors/ShrineInstanceConstructor.h @@ -9,19 +9,19 @@ */ #pragma once -#include "AObjectTypeHandler.h" +#include "CDefaultObjectTypeHandler.h" VCMI_LIB_NAMESPACE_BEGIN -class ShrineInstanceConstructor final : public AObjectTypeHandler +class CGShrine; + +class ShrineInstanceConstructor final : public CDefaultObjectTypeHandler { JsonNode parameters; protected: void initTypeData(const JsonNode & config) override; - CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; - std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + void randomizeObject(CGShrine * object, CRandomGenerator & rng) const override; public: template void serialize(Handler &h, const int version) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 42c168463..c3fdf9bbd 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1839,8 +1839,15 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const { h->showInfoDialog(48); } - } + +CGBoat::CGBoat() +{ + hero = nullptr; + direction = 4; + layer = EPathfindingLayer::EEPathfindingLayer::SAIL; +} + void CGBoat::initObj(CRandomGenerator & rand) { hero = nullptr; diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index a13a2c5d4..287f422d3 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -432,15 +432,10 @@ public: std::string overlayAnimation; //waves animations std::array flagAnimations; + CGBoat(); void initObj(CRandomGenerator & rand) override; static int3 translatePos(const int3 &pos, bool reverse = false); - CGBoat() - { - hero = nullptr; - direction = 4; - layer = EPathfindingLayer::EEPathfindingLayer::SAIL; - } template void serialize(Handler &h, const int version) { h & static_cast(*this); diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 56a528136..466f712b5 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -23,6 +23,7 @@ #include "../../mapObjectConstructors/AObjectTypeHandler.h" #include "../../mapObjectConstructors/CObjectClassesHandler.h" #include "../../mapObjectConstructors/CommonConstructors.h" +#include "../../mapObjects/CGHeroInstance.h" #include "../../mapObjects/CGPandoraBox.h" #include "../../CCreatureHandler.h" #include "../../spells/CSpellHandler.h" //for choosing random spells From 58661fc8ec765a40553d6cf6028c1df020e31ef1 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 8 Jun 2023 17:29:29 +0300 Subject: [PATCH 10/12] Moved DwellingInstanceConstructor to a new file --- cmake_modules/VCMI_lib.cmake | 2 + lib/CGameState.cpp | 2 +- .../CObjectClassesHandler.cpp | 3 +- .../CommonConstructors.cpp | 127 +-------------- .../CommonConstructors.h | 32 +--- .../DwellingInstanceConstructor.cpp | 146 ++++++++++++++++++ .../DwellingInstanceConstructor.h | 45 ++++++ lib/registerTypes/RegisterTypes.h | 3 +- lib/rmg/modificators/TreasurePlacer.cpp | 4 +- 9 files changed, 204 insertions(+), 160 deletions(-) create mode 100644 lib/mapObjectConstructors/DwellingInstanceConstructor.cpp create mode 100644 lib/mapObjectConstructors/DwellingInstanceConstructor.h diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index c7b52da26..01ab3c668 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -70,6 +70,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.cpp + ${MAIN_LIB_DIR}/mapObjectConstructors/DwellingInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/ShipyardInstanceConstructor.cpp ${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.cpp @@ -378,6 +379,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.h ${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.h ${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.h + ${MAIN_LIB_DIR}/mapObjectConstructors/DwellingInstanceConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.h ${MAIN_LIB_DIR}/mapObjectConstructors/IObjectInfo.h ${MAIN_LIB_DIR}/mapObjectConstructors/RandomMapInfo.h diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 1b810cfbe..f5fbb3f8f 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -621,7 +621,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID); for (si32 entry : dwellingIDs) { - const auto * handler = dynamic_cast(VLC->objtypeh->getHandlerFor(primaryID, entry).get()); + const auto * handler = dynamic_cast(VLC->objtypeh->getHandlerFor(primaryID, entry).get()); if (handler->producesCreature(VLC->creh->objects[cid])) result = std::make_pair(primaryID, entry); diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 71f47dbcd..fd24486c9 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -24,6 +24,7 @@ #include "../mapObjectConstructors/CBankInstanceConstructor.h" #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" +#include "../mapObjectConstructors/DwellingInstanceConstructor.h" #include "../mapObjectConstructors/HillFortInstanceConstructor.h" #include "../mapObjectConstructors/ShipyardInstanceConstructor.h" #include "../mapObjectConstructors/ShrineInstanceConstructor.h" @@ -46,7 +47,7 @@ CObjectClassesHandler::CObjectClassesHandler() // list of all known handlers, hardcoded for now since the only way to add new objects is via C++ code //Note: should be in sync with registerTypesMapObjectTypes function SET_HANDLER_CLASS("configurable", CRewardableConstructor); - SET_HANDLER_CLASS("dwelling", CDwellingInstanceConstructor); + SET_HANDLER_CLASS("dwelling", DwellingInstanceConstructor); SET_HANDLER_CLASS("hero", CHeroInstanceConstructor); SET_HANDLER_CLASS("town", CTownInstanceConstructor); SET_HANDLER_CLASS("bank", CBankInstanceConstructor); diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index 8824a75bc..cef36448e 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -10,16 +10,16 @@ #include "StdInc.h" #include "CommonConstructors.h" -#include "../CCreatureHandler.h" #include "../CGeneralTextHandler.h" #include "../CHeroHandler.h" #include "../CModHandler.h" +#include "../CTownHandler.h" #include "../IGameCallback.h" #include "../JsonRandom.h" #include "../StringConstants.h" #include "../TerrainHandler.h" +#include "../VCMI_Lib.h" -#include "../mapObjects/CBank.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGMarket.h" #include "../mapObjects/CGTownInstance.h" @@ -133,129 +133,6 @@ void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, CRandomG } -bool CDwellingInstanceConstructor::hasNameTextID() const -{ - return true; -} - -void CDwellingInstanceConstructor::initTypeData(const JsonNode & input) -{ - if (input.Struct().count("name") == 0) - logMod->warn("Dwelling %s missing name!", getJsonKey()); - - VLC->generaltexth->registerString( input.meta, getNameTextID(), input["name"].String()); - - const JsonVector & levels = input["creatures"].Vector(); - const auto totalLevels = levels.size(); - - availableCreatures.resize(totalLevels); - for(auto currentLevel = 0; currentLevel < totalLevels; currentLevel++) - { - const JsonVector & creaturesOnLevel = levels[currentLevel].Vector(); - const auto creaturesNumber = creaturesOnLevel.size(); - availableCreatures[currentLevel].resize(creaturesNumber); - - for(auto currentCreature = 0; currentCreature < creaturesNumber; currentCreature++) - { - VLC->modh->identifiers.requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index) - { - availableCreatures[currentLevel][currentCreature] = VLC->creh->objects[index]; - }); - } - assert(!availableCreatures[currentLevel].empty()); - } - guards = input["guards"]; -} - -bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance * obj, std::shared_ptr tmpl) const -{ - return false; -} - -void CDwellingInstanceConstructor::initializeObject(CGDwelling * obj) const -{ - obj->creatures.resize(availableCreatures.size()); - for(const auto & entry : availableCreatures) - { - for(const CCreature * cre : entry) - obj->creatures.back().second.push_back(cre->getId()); - } -} - -void CDwellingInstanceConstructor::randomizeObject(CGDwelling * object, CRandomGenerator &rng) const -{ - auto * dwelling = dynamic_cast(object); - - dwelling->creatures.clear(); - dwelling->creatures.reserve(availableCreatures.size()); - - for(const auto & entry : availableCreatures) - { - dwelling->creatures.resize(dwelling->creatures.size() + 1); - for(const CCreature * cre : entry) - dwelling->creatures.back().second.push_back(cre->getId()); - } - - bool guarded = false; //TODO: serialize for sanity - - if(guards.getType() == JsonNode::JsonType::DATA_BOOL) //simple switch - { - if(guards.Bool()) - { - guarded = true; - } - } - else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux) - { - for(auto & stack : JsonRandom::loadCreatures(guards, rng)) - { - dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->getId(), stack.count)); - } - } - else //default condition - creatures are of level 5 or higher - { - for(auto creatureEntry : availableCreatures) - { - if(creatureEntry.at(0)->getLevel() >= 5) - { - guarded = true; - break; - } - } - } - - if(guarded) - { - for(auto creatureEntry : availableCreatures) - { - const CCreature * crea = creatureEntry.at(0); - dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(crea->getId(), crea->getGrowth() * 3)); - } - } -} - -bool CDwellingInstanceConstructor::producesCreature(const CCreature * crea) const -{ - for(const auto & entry : availableCreatures) - { - for(const CCreature * cre : entry) - if(crea == cre) - return true; - } - return false; -} - -std::vector CDwellingInstanceConstructor::getProducedCreatures() const -{ - std::vector creatures; //no idea why it's 2D, to be honest - for(const auto & entry : availableCreatures) - { - for(const CCreature * cre : entry) - creatures.push_back(cre); - } - return creatures; -} - void BoatInstanceConstructor::initTypeData(const JsonNode & input) { layer = EPathfindingLayer::SAIL; diff --git a/lib/mapObjectConstructors/CommonConstructors.h b/lib/mapObjectConstructors/CommonConstructors.h index a93f36943..7144596e6 100644 --- a/lib/mapObjectConstructors/CommonConstructors.h +++ b/lib/mapObjectConstructors/CommonConstructors.h @@ -9,18 +9,17 @@ */ #pragma once -#include "AObjectTypeHandler.h" #include "CDefaultObjectTypeHandler.h" - #include "../LogicalExpression.h" +#include "../mapObjects/MiscObjects.h" + VCMI_LIB_NAMESPACE_BEGIN class CGArtifact; class CGObjectInstance; class CGTownInstance; class CGHeroInstance; -class CGDwelling; class CGMarket; class CHeroClass; class CBank; @@ -82,33 +81,6 @@ public: } }; -class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler -{ - std::vector> availableCreatures; - - JsonNode guards; - -protected: - bool objectFilter(const CGObjectInstance * obj, std::shared_ptr tmpl) const override; - void initTypeData(const JsonNode & input) override; - -public: - bool hasNameTextID() const override; - - void initializeObject(CGDwelling * object) const override; - void randomizeObject(CGDwelling * object, CRandomGenerator & rng) const override; - - bool producesCreature(const CCreature * crea) const; - std::vector getProducedCreatures() const; - - template void serialize(Handler &h, const int version) - { - h & availableCreatures; - h & guards; - h & static_cast&>(*this); - } -}; - class DLL_LINKAGE BoatInstanceConstructor : public CDefaultObjectTypeHandler { protected: diff --git a/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp new file mode 100644 index 000000000..14a28944d --- /dev/null +++ b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp @@ -0,0 +1,146 @@ +/* +* DwellingInstanceConstructor.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#include "StdInc.h" +#include "DwellingInstanceConstructor.h" + +#include "../CCreatureHandler.h" +#include "../CGeneralTextHandler.h" +#include "../CModHandler.h" +#include "../JsonRandom.h" +#include "../VCMI_Lib.h" +#include "../mapObjects/CGDwelling.h" + +VCMI_LIB_NAMESPACE_BEGIN + +bool DwellingInstanceConstructor::hasNameTextID() const +{ + return true; +} + +void DwellingInstanceConstructor::initTypeData(const JsonNode & input) +{ + if (input.Struct().count("name") == 0) + logMod->warn("Dwelling %s missing name!", getJsonKey()); + + VLC->generaltexth->registerString( input.meta, getNameTextID(), input["name"].String()); + + const JsonVector & levels = input["creatures"].Vector(); + const auto totalLevels = levels.size(); + + availableCreatures.resize(totalLevels); + for(auto currentLevel = 0; currentLevel < totalLevels; currentLevel++) + { + const JsonVector & creaturesOnLevel = levels[currentLevel].Vector(); + const auto creaturesNumber = creaturesOnLevel.size(); + availableCreatures[currentLevel].resize(creaturesNumber); + + for(auto currentCreature = 0; currentCreature < creaturesNumber; currentCreature++) + { + VLC->modh->identifiers.requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index) + { + availableCreatures[currentLevel][currentCreature] = VLC->creh->objects[index]; + }); + } + assert(!availableCreatures[currentLevel].empty()); + } + guards = input["guards"]; +} + +bool DwellingInstanceConstructor::objectFilter(const CGObjectInstance * obj, std::shared_ptr tmpl) const +{ + return false; +} + +void DwellingInstanceConstructor::initializeObject(CGDwelling * obj) const +{ + obj->creatures.resize(availableCreatures.size()); + for(const auto & entry : availableCreatures) + { + for(const CCreature * cre : entry) + obj->creatures.back().second.push_back(cre->getId()); + } +} + +void DwellingInstanceConstructor::randomizeObject(CGDwelling * object, CRandomGenerator &rng) const +{ + auto * dwelling = dynamic_cast(object); + + dwelling->creatures.clear(); + dwelling->creatures.reserve(availableCreatures.size()); + + for(const auto & entry : availableCreatures) + { + dwelling->creatures.resize(dwelling->creatures.size() + 1); + for(const CCreature * cre : entry) + dwelling->creatures.back().second.push_back(cre->getId()); + } + + bool guarded = false; //TODO: serialize for sanity + + if(guards.getType() == JsonNode::JsonType::DATA_BOOL) //simple switch + { + if(guards.Bool()) + { + guarded = true; + } + } + else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux) + { + for(auto & stack : JsonRandom::loadCreatures(guards, rng)) + { + dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->getId(), stack.count)); + } + } + else //default condition - creatures are of level 5 or higher + { + for(auto creatureEntry : availableCreatures) + { + if(creatureEntry.at(0)->getLevel() >= 5) + { + guarded = true; + break; + } + } + } + + if(guarded) + { + for(auto creatureEntry : availableCreatures) + { + const CCreature * crea = creatureEntry.at(0); + dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(crea->getId(), crea->getGrowth() * 3)); + } + } +} + +bool DwellingInstanceConstructor::producesCreature(const CCreature * crea) const +{ + for(const auto & entry : availableCreatures) + { + for(const CCreature * cre : entry) + if(crea == cre) + return true; + } + return false; +} + +std::vector DwellingInstanceConstructor::getProducedCreatures() const +{ + std::vector creatures; //no idea why it's 2D, to be honest + for(const auto & entry : availableCreatures) + { + for(const CCreature * cre : entry) + creatures.push_back(cre); + } + return creatures; +} + + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/DwellingInstanceConstructor.h b/lib/mapObjectConstructors/DwellingInstanceConstructor.h new file mode 100644 index 000000000..4022be309 --- /dev/null +++ b/lib/mapObjectConstructors/DwellingInstanceConstructor.h @@ -0,0 +1,45 @@ +/* +* DwellingInstanceConstructor.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#pragma once + +#include "CDefaultObjectTypeHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CGDwelling; + +class DwellingInstanceConstructor : public CDefaultObjectTypeHandler +{ + std::vector> availableCreatures; + + JsonNode guards; + +protected: + bool objectFilter(const CGObjectInstance * obj, std::shared_ptr tmpl) const override; + void initTypeData(const JsonNode & input) override; + +public: + bool hasNameTextID() const override; + + void initializeObject(CGDwelling * object) const override; + void randomizeObject(CGDwelling * object, CRandomGenerator & rng) const override; + + bool producesCreature(const CCreature * crea) const; + std::vector getProducedCreatures() const; + + template void serialize(Handler &h, const int version) + { + h & availableCreatures; + h & guards; + h & static_cast&>(*this); + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 83ee7379b..f7aa6ee30 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -21,6 +21,7 @@ #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" #include "../mapObjectConstructors/CBankInstanceConstructor.h" +#include "../mapObjectConstructors/DwellingInstanceConstructor.h" #include "../mapObjectConstructors/HillFortInstanceConstructor.h" #include "../mapObjectConstructors/ShipyardInstanceConstructor.h" #include "../mapObjectConstructors/ShrineInstanceConstructor.h" @@ -97,7 +98,7 @@ void registerTypesMapObjectTypes(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); - s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 466f712b5..bf362f23c 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -22,7 +22,7 @@ #include "../../ArtifactUtils.h" #include "../../mapObjectConstructors/AObjectTypeHandler.h" #include "../../mapObjectConstructors/CObjectClassesHandler.h" -#include "../../mapObjectConstructors/CommonConstructors.h" +#include "../../mapObjectConstructors/DwellingInstanceConstructor.h" #include "../../mapObjects/CGHeroInstance.h" #include "../../mapObjects/CGPandoraBox.h" #include "../../CCreatureHandler.h" @@ -172,7 +172,7 @@ void TreasurePlacer::addAllPossibleObjects() for(auto secondaryID : subObjects) { - const auto * dwellingHandler = dynamic_cast(VLC->objtypeh->getHandlerFor(dwellingType, secondaryID).get()); + const auto * dwellingHandler = dynamic_cast(VLC->objtypeh->getHandlerFor(dwellingType, secondaryID).get()); auto creatures = dwellingHandler->getProducedCreatures(); if(creatures.empty()) continue; From 5118a642bc6c51ccc811e3be0db4bad26134f56a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 16 Jun 2023 13:21:24 +0300 Subject: [PATCH 11/12] Fix duplicated randomResource object --- config/objects/generic.json | 13 ------------- config/objects/moddables.json | 9 +++++---- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/config/objects/generic.json b/config/objects/generic.json index 9dfa2ade0..602f314cc 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -653,19 +653,6 @@ }, "randomDwelling" : { "index" :216, "handler": "randomDwelling", "types" : { "object" : { "index" : 0} } }, - "randomResource" : { - "index" :76, - "handler": "randomResource", - "types" : { - "object" : { - "index" : 0, - "templates" : { - "normal" : { "animation" : "AVTrndm0", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] } - } - } - } - }, - "randomArtifact" : { "index" :65, "handler": "randomArtifact", diff --git a/config/objects/moddables.json b/config/objects/moddables.json index bb48baadf..da0b7e793 100644 --- a/config/objects/moddables.json +++ b/config/objects/moddables.json @@ -53,7 +53,7 @@ "randomResource": { "index" :76, - "handler": "resource", + "handler": "randomResource", "base" : { "base" : { "visitableFrom" : [ "+++", "+-+", "+++" ], @@ -69,9 +69,10 @@ }, "templates" : { - "res" : - { - "animation" : "AVTrndm0.def" + "normal" : { + "animation" : "AVTrndm0", + "visitableFrom" : [ "+++", "+-+", "+++" ], + "mask" : [ "VV", "VA"] } } } From ece9982ca03576a0762691f8e8d253dc876a0ec0 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 16 Jun 2023 18:11:45 +0300 Subject: [PATCH 12/12] Fix build --- lib/mapObjects/CGHeroInstance.cpp | 2 +- lib/spells/AdventureSpellMechanics.cpp | 2 +- server/CGameHandler.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 95a831445..a05d97f01 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -452,7 +452,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const //Create a new boat for hero NewObject no; no.ID = Obj::BOAT; - no.subID = BoatId(EBoatId::BOAT_NEUTRAL); + no.subID = BoatId(EBoatId::CASTLE); no.pos = CGBoat::translatePos(boatPos); cb->sendAndApply(&no); diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 85f23fa8c..0da946200 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -215,7 +215,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment { NewObject no; no.ID = Obj::BOAT; - no.subID = BoatId(EBoatId::BOAT_EVIL); + no.subID = BoatId(EBoatId::NECROPOLIS); no.pos = CGBoat::translatePos(summonPos); env->apply(&no); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 92761f37d..fc416b4bf 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4421,7 +4421,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl //Create a new boat for hero NewObject no; no.ID = Obj::BOAT; - no.subID = BoatId(EBoatId::BOAT_NEUTRAL); + no.subID = BoatId(EBoatId::CASTLE); no.pos = hr.tile + int3(1,0,0); sendAndApply(&no);