Merge pull request #6477 from IvanSavenko/hero_spec_fix

Fix loading of secondary skill specialties
This commit is contained in:
Ivan Savenko
2025-12-19 16:06:08 +02:00
committed by GitHub
2 changed files with 35 additions and 3 deletions
+24 -3
View File
@@ -263,10 +263,9 @@ void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node) const
const JsonNode & skillNode = specialtyNode["secondary"];
int stepSize = specialtyNode["stepSize"].Integer();
LIBRARY->identifiers()->requestIdentifier("secondarySkill", skillNode, [this, hero, prepSpec, stepSize](si32 creature)
LIBRARY->identifiers()->requestIdentifier("secondarySkill", skillNode, [this, hero, stepSize](si32 skill)
{
for (const auto & bonus : createSecondarySkillSpecialty(SecondarySkill(creature), stepSize))
hero->specialty.push_back(prepSpec(bonus));
skillSpecialtiesToGenerate.push_back({hero->ID, SecondarySkill(skill), stepSize});
});
}
@@ -427,4 +426,26 @@ std::set<HeroTypeID> CHeroHandler::getDefaultAllowed() const
return result;
}
void CHeroHandler::afterLoadFinalization()
{
auto prepSpec = [](HeroTypeID hero, std::shared_ptr<Bonus> bonus)
{
bonus->duration = BonusDuration::PERMANENT;
bonus->source = BonusSource::HERO_SPECIAL;
bonus->sid = BonusSourceID(hero);
return bonus;
};
// Workaround for loading order issue
// To load secondary skill specialty, bonus ID's must be loaded first
// However, identifier request only guarantee that requested object itself is loaded
// Meaning, it is possible for skill ID to be resolved before bonus ID is resolved,
// leading to createSecondarySkillSpecialty creating copy of incomplete bonus
for (const auto & specialty : skillSpecialtiesToGenerate)
{
for (const auto & bonus : createSecondarySkillSpecialty(specialty.skill, specialty.stepSize))
objects.at(specialty.hero.getNum())->specialty.push_back(prepSpec(specialty.hero, bonus));
}
}
VCMI_LIB_NAMESPACE_END
+11
View File
@@ -25,6 +25,16 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero
/// consists of 196 values. Any higher levels require experience larger that TExpType can hold
std::vector<TExpType> expPerLevel;
struct SpecialtyToGenerate
{
HeroTypeID hero;
SecondarySkill skill;
int stepSize;
};
/// Helper field to generate specialties for heroes after loading is complete
mutable std::vector<SpecialtyToGenerate> skillSpecialtiesToGenerate;
/// helpers for loading to avoid huge load functions
void loadHeroArmy(CHero * hero, const JsonNode & node) const;
void loadHeroSkills(CHero * hero, const JsonNode & node) const;
@@ -45,6 +55,7 @@ public:
void beforeValidate(JsonNode & object) override;
void loadObject(std::string scope, std::string name, const JsonNode & data) override;
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
void afterLoadFinalization() override;
CHeroHandler();
~CHeroHandler();