mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
Refactoring of hero specialty loading code:
- removed no longer used code - follow creature upgrade chains (e.g. third upgrades)
This commit is contained in:
parent
bdc7b38b0f
commit
8d0d5341db
@ -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> bonus, std::vector<std::shared_ptr<Bonus>> &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<int> 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);
|
||||
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);
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
bonus->updater.reset(new GrowsWithLevelUpdater(specCreature.getDefense(false), stepSize));
|
||||
result.push_back(bonus);
|
||||
}
|
||||
}
|
||||
|
||||
// convert deprecated format
|
||||
std::vector<std::shared_ptr<Bonus>> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid)
|
||||
/// creates standard H3 hero specialty for creatures
|
||||
static std::vector<std::shared_ptr<Bonus>> createCreatureSpecialty(CreatureID baseCreatureID)
|
||||
{
|
||||
std::vector<std::shared_ptr<Bonus>> result;
|
||||
std::set<CreatureID> targets;
|
||||
targets.insert(baseCreatureID);
|
||||
|
||||
std::shared_ptr<Bonus> bonus = std::make_shared<Bonus>();
|
||||
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<CreatureID> 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> bonus = std::make_shared<Bonus>();
|
||||
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<HasAnotherBonusLimiter>(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<Bonus> upgradeUpgradedVersion = std::make_shared<Bonus>(*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> bonus = std::make_shared<Bonus>();
|
||||
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> bonus = std::make_shared<Bonus>();
|
||||
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)
|
||||
{
|
||||
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<void()> 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<std::shared_ptr<Bonus>> convertedBonuses;
|
||||
for(const SSpecialtyInfo & spec : hero->specDeprecated)
|
||||
{
|
||||
for(const std::shared_ptr<Bonus> & b : SpecialtyInfoToBonuses(spec, hero->ID.getNum()))
|
||||
convertedBonuses.push_back(b);
|
||||
}
|
||||
hero->specDeprecated.clear();
|
||||
// store and create json for logging
|
||||
std::vector<JsonNode> specVec;
|
||||
std::vector<std::string> specNames;
|
||||
for(const std::shared_ptr<Bonus> & 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<ui32>(boost::range::upper_bound(expPerLevel, experience) - std::begin(expPerLevel));
|
||||
|
@ -29,21 +29,6 @@ class CRandomGenerator;
|
||||
class JsonSerializeFormat;
|
||||
class BattleField;
|
||||
|
||||
struct SSpecialtyInfo
|
||||
{
|
||||
si32 type;
|
||||
si32 val;
|
||||
si32 subtype;
|
||||
si32 additionalinfo;
|
||||
template <typename Handler> 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<std::pair<SecondarySkill, ui8> > secSkillsInit; //initial secondary skills; first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert)
|
||||
std::vector<SSpecialtyInfo> specDeprecated;
|
||||
BonusList specialty;
|
||||
std::set<SpellID> spells;
|
||||
bool haveSpellBook = false;
|
||||
@ -132,9 +116,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// convert deprecated format
|
||||
std::vector<std::shared_ptr<Bonus>> 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<HeroTypeID, HeroType, CHero
|
||||
/// helpers for loading to avoid huge load functions
|
||||
void loadHeroArmy(CHero * hero, const JsonNode & node) const;
|
||||
void loadHeroSkills(CHero * hero, const JsonNode & node) const;
|
||||
void loadHeroSpecialty(CHero * hero, const JsonNode & node) const;
|
||||
void loadHeroSpecialty(CHero * hero, const JsonNode & node);
|
||||
|
||||
void loadExperience();
|
||||
|
||||
std::vector<std::function<void()>> callAfterLoadFinalization;
|
||||
|
||||
public:
|
||||
CHeroClassHandler classes;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<Bonus> & b : type->specialty)
|
||||
addNewBonus(b);
|
||||
for(SSpecialtyInfo & spec : type->specDeprecated)
|
||||
for(const std::shared_ptr<Bonus> & b : SpecialtyInfoToBonuses(spec, type->getIndex()))
|
||||
addNewBonus(b);
|
||||
|
||||
//initialize bonuses
|
||||
recreateSecondarySkillsBonuses();
|
||||
|
Loading…
x
Reference in New Issue
Block a user