From 8d0d5341dbcf143c537fe783d714f14bc2863c28 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 28 Mar 2023 17:09:54 +0300 Subject: [PATCH] Refactoring of hero specialty loading code: - removed no longer used code - follow creature upgrade chains (e.g. third upgrades) --- lib/CHeroHandler.cpp | 318 +++++++----------------------- lib/CHeroHandler.h | 23 +-- lib/HeroBonus.h | 2 +- lib/mapObjects/CGHeroInstance.cpp | 3 - 4 files changed, 78 insertions(+), 268 deletions(-) diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index fa9a0be05..a1f57cd06 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -504,182 +504,60 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) const } } -// add standard creature specialty to result -void AddSpecialtyForCreature(int creatureID, std::shared_ptr bonus, std::vector> &result) -{ - const CCreature &specBaseCreature = *VLC->creh->objects[creatureID]; //base creature in which we have specialty - - bonus->limiter.reset(new CCreatureTypeLimiter(specBaseCreature, true)); - bonus->type = Bonus::STACKS_SPEED; - bonus->valType = Bonus::ADDITIVE_VALUE; - bonus->val = 1; - result.push_back(bonus); - - // attack and defense may differ for upgraded creatures => separate bonuses - std::vector specTargets; - specTargets.push_back(creatureID); - specTargets.insert(specTargets.end(), specBaseCreature.upgrades.begin(), specBaseCreature.upgrades.end()); - - for(int cid : specTargets) - { - const CCreature &specCreature = *VLC->creh->objects[cid]; - bonus = std::make_shared(*bonus); - bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); - bonus->type = Bonus::PRIMARY_SKILL; - bonus->val = 0; - - int stepSize = specCreature.level ? specCreature.level : 5; - - bonus->subtype = PrimarySkill::ATTACK; - 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 GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize)); - result.push_back(bonus); - } -} - -// convert deprecated format -std::vector> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid) +/// creates standard H3 hero specialty for creatures +static std::vector> createCreatureSpecialty(CreatureID baseCreatureID) { std::vector> result; + std::set targets; + targets.insert(baseCreatureID); - std::shared_ptr bonus = std::make_shared(); - bonus->duration = Bonus::PERMANENT; - bonus->source = Bonus::HERO_SPECIAL; - bonus->sid = sid; - bonus->val = spec.val; - - switch (spec.type) + // go through entire upgrade chain and collect all creatures to which baseCreatureID can be upgraded + for (;;) { - case 1: //creature specialty - AddSpecialtyForCreature(spec.additionalinfo, bonus, result); - break; - case 2: //secondary skill + std::set oldTargets = targets; + + for (auto const & upgradeSourceID : oldTargets) { - auto params = BonusParams("SECONDARY_SKILL_PREMY", "", spec.subtype); - bonus->type = params.type; - if(params.subtypeRelevant) - bonus->subtype = params.subtype; - bonus->valType = Bonus::PERCENT_TO_TARGET_TYPE; - bonus->targetSourceType = Bonus::SECONDARY_SKILL; - bonus->updater.reset(new TimesHeroLevelUpdater()); - result.push_back(bonus); - break; + const CCreature * upgradeSource = VLC->creh->objects[upgradeSourceID]; + targets.insert(upgradeSource->upgrades.begin(), upgradeSource->upgrades.end()); } - case 3: //spell damage bonus, level dependent but calculated elsewhere - bonus->type = Bonus::SPECIAL_SPELL_LEV; - bonus->subtype = spec.subtype; - bonus->updater.reset(new TimesHeroLevelUpdater()); - result.push_back(bonus); - break; - case 4: //creature stat boost - switch (spec.subtype) + + if (oldTargets.size() == targets.size()) + break; + } + + for(CreatureID cid : targets) + { + const CCreature &specCreature = *VLC->creh->objects[cid]; + int stepSize = specCreature.level ? specCreature.level : 5; + { - case 1: - bonus->type = Bonus::PRIMARY_SKILL; - bonus->subtype = PrimarySkill::ATTACK; - break; - case 2: - bonus->type = Bonus::PRIMARY_SKILL; - bonus->subtype = PrimarySkill::DEFENSE; - break; - case 3: - bonus->type = Bonus::CREATURE_DAMAGE; - bonus->subtype = 0; //both min and max - break; - case 4: - bonus->type = Bonus::STACK_HEALTH; - break; - case 5: + std::shared_ptr bonus = std::make_shared(); + bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); bonus->type = Bonus::STACKS_SPEED; - break; - default: - logMod->warn("Unknown subtype for specialty 4"); - return result; - } - bonus->valType = Bonus::ADDITIVE_VALUE; - bonus->limiter.reset(new CCreatureTypeLimiter(*VLC->creh->objects[spec.additionalinfo], true)); - result.push_back(bonus); - break; - case 5: //spell damage bonus in percent - bonus->type = Bonus::SPECIFIC_SPELL_DAMAGE; - bonus->valType = Bonus::BASE_NUMBER; //current spell system is screwed - bonus->subtype = spec.subtype; //spell id - result.push_back(bonus); - break; - case 6: //damage bonus for bless (Adela) - { - auto limiter = std::make_shared(Bonus::GENERAL_DAMAGE_PREMY,Bonus::SPELL_EFFECT); - limiter->sid = spec.subtype; //spell id if you ever wanted to use it otherwise - limiter->isSourceIDRelevant = true; - bonus->type = Bonus::GENERAL_DAMAGE_PREMY; - bonus->updater.reset(new TimesHeroLevelUpdater()); - bonus->addLimiter(limiter); + bonus->val = 1; result.push_back(bonus); - break; } - case 7: //maxed mastery for spell - bonus->type = Bonus::SPELL; - bonus->subtype = spec.subtype; //spell id - bonus->val = 3; //to match MAXED_SPELL - bonus->valType = Bonus::INDEPENDENT_MAX; - result.push_back(bonus); - break; - case 8: //peculiar spells - enchantments - bonus->type = Bonus::SPECIAL_PECULIAR_ENCHANT; - bonus->subtype = spec.subtype; //spell id - bonus->additionalInfo = spec.additionalinfo; //0, 1 for Coronius - result.push_back(bonus); - break; - case 9: //upgrade creatures + { - const auto &creatures = VLC->creh->objects; - bonus->type = Bonus::SPECIAL_UPGRADE; - bonus->subtype = spec.subtype; //base id - bonus->additionalInfo = spec.additionalinfo; //target id - result.push_back(bonus); - //propagate for regular upgrades of base creature - for(const auto & cre_id : creatures[spec.subtype]->upgrades) - { - std::shared_ptr upgradeUpgradedVersion = std::make_shared(*bonus); - upgradeUpgradedVersion->subtype = cre_id; - result.push_back(upgradeUpgradedVersion); - } - } - break; - case 10: //resource generation - bonus->type = Bonus::GENERATE_RESOURCE; - bonus->subtype = spec.subtype; - result.push_back(bonus); - break; - case 11: //starting skill with mastery (Adrienne) - logMod->warn("Secondary skill mastery is no longer supported as specialty."); - break; - case 12: //army speed - bonus->type = Bonus::STACKS_SPEED; - result.push_back(bonus); - break; - case 13: //Dragon bonuses (Mutare) - bonus->type = Bonus::PRIMARY_SKILL; - bonus->valType = Bonus::ADDITIVE_VALUE; - switch(spec.subtype) - { - case 1: + std::shared_ptr bonus = std::make_shared(); + bonus->type = Bonus::PRIMARY_SKILL; bonus->subtype = PrimarySkill::ATTACK; - break; - case 2: - bonus->subtype = PrimarySkill::DEFENSE; - break; + bonus->val = 0; + bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); + bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getAttack(false), stepSize)); + result.push_back(bonus); + } + + { + std::shared_ptr bonus = std::make_shared(); + bonus->type = Bonus::PRIMARY_SKILL; + bonus->subtype = PrimarySkill::DEFENSE; + bonus->val = 0; + bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, false)); + bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize)); + result.push_back(bonus); } - bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE)); - result.push_back(bonus); - break; - default: - logMod->warn("Unknown hero specialty %d", spec.type); - break; } return result; @@ -708,41 +586,51 @@ void CHeroHandler::beforeValidate(JsonNode & object) } } -void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node) const +void CHeroHandler::afterLoadFinalization() +{ + for (auto const & functor : callAfterLoadFinalization) + functor(); + + callAfterLoadFinalization.clear(); +} + +void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node) { - int sid = hero->ID.getNum(); auto prepSpec = [=](std::shared_ptr bonus) { bonus->duration = Bonus::PERMANENT; bonus->source = Bonus::HERO_SPECIAL; - bonus->sid = sid; + bonus->sid = hero->getIndex(); return bonus; }; //new format, using bonus system const JsonNode & specialtyNode = node["specialty"]; - if(specialtyNode.getType() == JsonNode::JsonType::DATA_STRUCT) + if(specialtyNode.getType() != JsonNode::JsonType::DATA_STRUCT) { - //creature specialty - alias for simplicity - if(!specialtyNode["creature"].isNull()) - { - VLC->modh->identifiers.requestIdentifier("creature", specialtyNode["creature"], [hero](si32 creature) { - // use legacy format for delayed conversion (must have all creature data loaded, also for upgrades) - SSpecialtyInfo spec{}; - spec.type = 1; - spec.additionalinfo = creature; - hero->specDeprecated.push_back(spec); - }); - } - if(!specialtyNode["bonuses"].isNull()) - { - //proper new format - for(const auto & keyValue : specialtyNode["bonuses"].Struct()) - hero->specialty.push_back(prepSpec(JsonUtils::parseBonus(keyValue.second))); - } - } - else logMod->error("Unsupported speciality format for hero %s!", hero->getNameTranslated()); + return; + } + + //creature specialty - alias for simplicity + if(!specialtyNode["creature"].isNull()) + { + JsonNode creatureNode = specialtyNode["creature"]; + + std::function specialtyLoader = [creatureNode, hero, prepSpec] + { + VLC->modh->identifiers.requestIdentifier("creature", creatureNode, [hero, prepSpec](si32 creature) + { + for (const auto & bonus : createCreatureSpecialty(CreatureID(creature))) + hero->specialty.push_back(prepSpec(bonus)); + }); + }; + + callAfterLoadFinalization.push_back(specialtyLoader); + } + + for(const auto & keyValue : specialtyNode["bonuses"].Struct()) + hero->specialty.push_back(prepSpec(JsonUtils::parseBonus(keyValue.second))); } void CHeroHandler::loadExperience() @@ -848,64 +736,6 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod registerObject(scope, "hero", name, object->getIndex()); } -void CHeroHandler::afterLoadFinalization() -{ - for(auto & hero : objects) - { - for(const auto & bonus : hero->specialty) - { - bonus->sid = hero->getIndex(); - } - - if(!hero->specDeprecated.empty()) - { - logMod->debug("Converting specialty format for hero %s(%s)", hero->getNameTranslated(), FactionID::encode(hero->heroClass->faction)); - std::vector> convertedBonuses; - for(const SSpecialtyInfo & spec : hero->specDeprecated) - { - for(const std::shared_ptr & b : SpecialtyInfoToBonuses(spec, hero->ID.getNum())) - convertedBonuses.push_back(b); - } - hero->specDeprecated.clear(); - // store and create json for logging - std::vector specVec; - std::vector specNames; - for(const std::shared_ptr & bonus : convertedBonuses) - { - hero->specialty.push_back(bonus); - specVec.push_back(bonus->toJsonNode()); - // find fitting & unique bonus name - std::string bonusName = bonus->nameForBonus(); - if(vstd::contains(specNames, bonusName)) - { - int suffix = 2; - while(vstd::contains(specNames, bonusName + std::to_string(suffix))) - suffix++; - bonusName += std::to_string(suffix); - } - specNames.push_back(bonusName); - } - // log new format for easy copy-and-paste - JsonNode specNode(JsonNode::JsonType::DATA_STRUCT); - if(specVec.size() > 1) - { - JsonNode base = JsonUtils::intersect(specVec); - if(base.containsBaseData()) - { - specNode["base"] = base; - for(JsonNode & node : specVec) - node = JsonUtils::difference(node, base); - } - } - // add json for bonuses - specNode["bonuses"].Struct(); - for(int i = 0; i < specVec.size(); i++) - specNode["bonuses"][specNames[i]] = specVec[i]; - logMod->trace("\"specialty\" : %s", specNode.toJson(true)); - } - } -} - ui32 CHeroHandler::level (ui64 experience) const { return static_cast(boost::range::upper_bound(expPerLevel, experience) - std::begin(expPerLevel)); diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index 4121d5b03..a452b00c4 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -29,21 +29,6 @@ class CRandomGenerator; class JsonSerializeFormat; class BattleField; -struct SSpecialtyInfo -{ - si32 type; - si32 val; - si32 subtype; - si32 additionalinfo; - template void serialize(Handler &h, const int version) - { - h & type; - h & val; - h & subtype; - h & additionalinfo; - } -}; - class DLL_LINKAGE CHero : public HeroType { friend class CHeroHandler; @@ -72,7 +57,6 @@ public: CHeroClass * heroClass{}; std::vector > secSkillsInit; //initial secondary skills; first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert) - std::vector specDeprecated; BonusList specialty; std::set spells; bool haveSpellBook = false; @@ -132,9 +116,6 @@ public: } }; -// convert deprecated format -std::vector> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid = 0); - class DLL_LINKAGE CHeroClass : public HeroClass { friend class CHeroClassHandler; @@ -251,10 +232,12 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase> callAfterLoadFinalization; + public: CHeroClassHandler classes; diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 195d4bc93..7423b6d57 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -1047,7 +1047,7 @@ public: bool includeUpgrades = false; CCreatureTypeLimiter() = default; - CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades = true); + CCreatureTypeLimiter(const CCreature & creature_, bool IncludeUpgrades); void setCreature(const CreatureID & id); int limit(const BonusLimitationContext &context) const override; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 54e6308f9..042d737b1 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -510,9 +510,6 @@ void CGHeroInstance::initObj(CRandomGenerator & rand) //copy active (probably growing) bonuses from hero prototype to hero object for(const std::shared_ptr & b : type->specialty) addNewBonus(b); - for(SSpecialtyInfo & spec : type->specDeprecated) - for(const std::shared_ptr & b : SpecialtyInfoToBonuses(spec, type->getIndex())) - addNewBonus(b); //initialize bonuses recreateSecondarySkillsBonuses();