diff --git a/config/heroes/castle.json b/config/heroes/castle.json index 727b31b1f..0593b1f3d 100644 --- a/config/heroes/castle.json +++ b/config/heroes/castle.json @@ -14,10 +14,8 @@ "archery" : { "subtype" : "skill.archery", "type" : "SECONDARY_SKILL_PREMY", - "updater" : { - "parameters" : [ 100 ], - "type" : "GROWS_WITH_LEVEL" - }, + "updater" : "TIMES_HERO_LEVEL", + "val" : 5, "valueType" : "PERCENT_TO_BASE" } } diff --git a/config/schemas/bonus.json b/config/schemas/bonus.json index 80060f076..7e6f64852 100644 --- a/config/schemas/bonus.json +++ b/config/schemas/bonus.json @@ -71,21 +71,28 @@ ] }, "updater" : { - "description" : "updater", - "type" : "object", - "required" : ["type", "parameters"], - "additionalProperties" : false, - "properties" : { - "type" : { - "type" : "string", - "description" : "type" + "anyOf" : [ + { + "type" : "string" }, - "parameters": { - "type" : "array", - "description" : "parameters", - "additionalItems" : true + { + "description" : "updater", + "type" : "object", + "required" : ["type", "parameters"], + "additionalProperties" : false, + "properties" : { + "type" : { + "type" : "string", + "description" : "type" + }, + "parameters": { + "type" : "array", + "description" : "parameters", + "additionalItems" : true + } + } } - } + ] }, "sourceID": { "type":"number", diff --git a/config/schemas/hero.json b/config/schemas/hero.json index 99c532621..30735c1b9 100644 --- a/config/schemas/hero.json +++ b/config/schemas/hero.json @@ -115,7 +115,7 @@ "additionalItems" : true }, "specialty": { - "oneOf" : [ + "anyOf" : [ { "type":"array", "description": "Description of hero specialty using bonus system (deprecated)", diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index e4a3de8da..b128376ec 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -408,12 +408,12 @@ std::vector> SpecialtyInfoToBonuses(const SSpecialtyInfo int stepSize = specCreature.level ? specCreature.level : 5; bonus->subtype = PrimarySkill::ATTACK; - bonus->updater.reset(new ScalingUpdater(specCreature.getAttack(false), stepSize)); + bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getAttack(false), stepSize)); result.push_back(bonus); bonus = std::make_shared(*bonus); bonus->subtype = PrimarySkill::DEFENSE; - bonus->updater.reset(new ScalingUpdater(specCreature.getDefence(false), stepSize)); + bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefence(false), stepSize)); result.push_back(bonus); } break; @@ -421,8 +421,7 @@ std::vector> SpecialtyInfoToBonuses(const SSpecialtyInfo bonus->type = Bonus::SECONDARY_SKILL_PREMY; bonus->valType = Bonus::PERCENT_TO_BASE; bonus->subtype = spec.subtype; - bonus->val = 0; - bonus->updater.reset(new ScalingUpdater(spec.val * 20)); + bonus->updater.reset(new TimesHeroLevelUpdater()); result.push_back(bonus); break; case 3: //spell damage bonus, level dependent but calculated elsewhere @@ -548,8 +547,7 @@ std::vector> SpecialtyBonusToBonuses(const SSpecialtyBonu break; // ignore - used to be overwritten based on SPECIAL_SECONDARY_SKILL case Bonus::SPECIAL_SECONDARY_SKILL: newBonus->type = Bonus::SECONDARY_SKILL_PREMY; - newBonus->updater = std::make_shared(newBonus->val * 20); - newBonus->val = 0; // for json printing + newBonus->updater = std::make_shared(); result.push_back(newBonus); break; case Bonus::PRIMARY_SKILL: @@ -561,7 +559,7 @@ std::vector> SpecialtyBonusToBonuses(const SSpecialtyBonu const CCreature * cre = creatureLimiter->creature; int creStat = newBonus->subtype == PrimarySkill::ATTACK ? cre->getAttack(false) : cre->getDefence(false); int creLevel = cre->level ? cre->level : 5; - newBonus->updater = std::make_shared(creStat, creLevel); + newBonus->updater = std::make_shared(creStat, creLevel); } result.push_back(newBonus); } diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 28f44cba6..985ccb2bf 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -81,6 +81,11 @@ const std::map bonusPropagatorMap = {"GLOBAL_EFFECT", std::make_shared(CBonusSystemNode::GLOBAL_EFFECTS)} }; //untested +const std::map bonusUpdaterMap = +{ + {"TIMES_HERO_LEVEL", std::make_shared()} +}; + ///CBonusProxy CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector) : cachedLast(0), @@ -1712,20 +1717,30 @@ IUpdater::~IUpdater() { } +const std::shared_ptr IUpdater::update(const std::shared_ptr b, const CBonusSystemNode & context) const +{ + return b; +} + std::string IUpdater::toString() const { return typeid(*this).name(); } -ScalingUpdater::ScalingUpdater() : valPer20(0), stepSize(1) +JsonNode IUpdater::toJsonNode() const +{ + return JsonNode(JsonNode::JsonType::DATA_NULL); +} + +GrowsWithLevelUpdater::GrowsWithLevelUpdater() : valPer20(0), stepSize(1) { } -ScalingUpdater::ScalingUpdater(int valPer20, int stepSize) : valPer20(valPer20), stepSize(stepSize) +GrowsWithLevelUpdater::GrowsWithLevelUpdater(int valPer20, int stepSize) : valPer20(valPer20), stepSize(stepSize) { } -const std::shared_ptr ScalingUpdater::update(const std::shared_ptr b, const CBonusSystemNode & context) const +const std::shared_ptr GrowsWithLevelUpdater::update(const std::shared_ptr b, const CBonusSystemNode & context) const { if(context.getNodeType() == CBonusSystemNode::HERO) { @@ -1741,14 +1756,14 @@ const std::shared_ptr ScalingUpdater::update(const std::shared_ptr return b; } -std::string ScalingUpdater::toString() const +std::string GrowsWithLevelUpdater::toString() const { char buf[100]; - sprintf(buf, "ScalingUpdater(valPer20=%d, stepSize=%d)", valPer20, stepSize); + sprintf(buf, "GrowsWithLevelUpdater(valPer20=%d, stepSize=%d)", valPer20, stepSize); return std::string(buf); } -JsonNode ScalingUpdater::toJsonNode() const +JsonNode GrowsWithLevelUpdater::toJsonNode() const { JsonNode root(JsonNode::JsonType::DATA_STRUCT); @@ -1759,3 +1774,29 @@ JsonNode ScalingUpdater::toJsonNode() const return root; } + +TimesHeroLevelUpdater::TimesHeroLevelUpdater() +{ +} + +const std::shared_ptr TimesHeroLevelUpdater::update(const std::shared_ptr b, const CBonusSystemNode & context) const +{ + if(context.getNodeType() == CBonusSystemNode::HERO) + { + int level = static_cast(context).level; + std::shared_ptr newBonus = std::make_shared(*b); + newBonus->val *= level; + return newBonus; + } + return b; +} + +std::string TimesHeroLevelUpdater::toString() const +{ + return "TimesHeroLevelUpdater"; +} + +JsonNode TimesHeroLevelUpdater::toJsonNode() const +{ + return JsonUtils::stringNode("TIMES_HERO_LEVEL"); +} diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index e2070ef21..b6bb5a37a 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -1013,7 +1013,7 @@ extern DLL_LINKAGE const std::map bonusDurationMap; extern DLL_LINKAGE const std::map bonusLimitEffect; extern DLL_LINKAGE const std::map bonusLimiterMap; extern DLL_LINKAGE const std::map bonusPropagatorMap; - +extern DLL_LINKAGE const std::map bonusUpdaterMap; // BonusList template that requires full interface of CBonusSystemNode template @@ -1030,23 +1030,23 @@ class DLL_LINKAGE IUpdater public: virtual ~IUpdater(); - virtual const std::shared_ptr update(const std::shared_ptr b, const CBonusSystemNode & context) const = 0; + virtual const std::shared_ptr update(const std::shared_ptr b, const CBonusSystemNode & context) const; virtual std::string toString() const; - virtual JsonNode toJsonNode() const = 0; + virtual JsonNode toJsonNode() const; template void serialize(Handler & h, const int version) { } }; -class DLL_LINKAGE ScalingUpdater : public IUpdater +class DLL_LINKAGE GrowsWithLevelUpdater : public IUpdater { public: int valPer20; int stepSize; - ScalingUpdater(); - ScalingUpdater(int valPer20, int stepSize = 1); + GrowsWithLevelUpdater(); + GrowsWithLevelUpdater(int valPer20, int stepSize = 1); template void serialize(Handler & h, const int version) { @@ -1059,3 +1059,18 @@ public: virtual std::string toString() const override; virtual JsonNode toJsonNode() const override; }; + +class DLL_LINKAGE TimesHeroLevelUpdater : public IUpdater +{ +public: + TimesHeroLevelUpdater(); + + template void serialize(Handler & h, const int version) + { + h & static_cast(*this); + } + + const std::shared_ptr update(const std::shared_ptr b, const CBonusSystemNode & context) const override; + virtual std::string toString() const override; + virtual JsonNode toJsonNode() const override; +}; diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index de0d70880..19fa62fde 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -651,17 +651,25 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b) if(!value->isNull()) { const JsonNode & updaterJson = *value; - if(updaterJson["type"].String() == "GROWS_WITH_LEVEL") + switch(updaterJson.getType()) { - std::shared_ptr updater = std::make_shared(); - const JsonVector param = updaterJson["parameters"].Vector(); - updater->valPer20 = param[0].Integer(); - if(param.size() > 1) - updater->stepSize = param[1].Integer(); - b->addUpdater(updater); + case JsonNode::JsonType::DATA_STRING: + b->addUpdater(parseByMap(bonusUpdaterMap, &updaterJson, "updater type ")); + break; + case JsonNode::JsonType::DATA_STRUCT: + if(updaterJson["type"].String() == "GROWS_WITH_LEVEL") + { + std::shared_ptr updater = std::make_shared(); + const JsonVector param = updaterJson["parameters"].Vector(); + updater->valPer20 = param[0].Integer(); + if(param.size() > 1) + updater->stepSize = param[1].Integer(); + b->addUpdater(updater); + } + else + logMod->warn("Unknown updater type \"%s\"", updaterJson["type"].String()); + break; } - else - logMod->warn("Unknown updater type \"%s\"", updaterJson["type"].String()); } return true; diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index d65b5a280..9fc7062a0 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -136,7 +136,8 @@ void registerTypesMapObjectTypes(Serializer &s) #undef REGISTER_GENERIC_HANDLER - s.template registerType(); + s.template registerType(); + s.template registerType(); //new types (other than netpacks) must register here //order of type registration is critical for loading old savegames }