mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
added support for new specialty json format; old format is converted to bonuses with updaters
This commit is contained in:
parent
f867b1a37e
commit
3e0022be27
@ -118,20 +118,29 @@
|
||||
"type":"array",
|
||||
"description": "Description of hero specialty using bonus system",
|
||||
"items": {
|
||||
"type":"object",
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "bonuses" ],
|
||||
"properties":{
|
||||
"growsWithLevel" : {
|
||||
"type" : "boolean",
|
||||
"description" : "Specialty growth with level, so far only SECONDARY_SKILL_PREMY and PRIMATY SKILL with creature limiter can grow"
|
||||
"oneOf" : [
|
||||
{
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "bonuses" ],
|
||||
"properties" : {
|
||||
"growsWithLevel" : {
|
||||
"type" : "boolean",
|
||||
"description" : "Specialty growth with level. Deprecated, use bonuses with updaters instead."
|
||||
},
|
||||
"bonuses" : {
|
||||
"type" : "array",
|
||||
"description" : "List of bonuses",
|
||||
"items" : { "$ref" : "vcmi:bonus" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"bonuses": {
|
||||
"type":"array",
|
||||
"description": "List of bonuses",
|
||||
"items": { "$ref" : "vcmi:bonus" }
|
||||
{
|
||||
"type" : "object",
|
||||
"description" : "List of bonuses",
|
||||
"items" : { "$ref" : "vcmi:bonus" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"spellbook": {
|
||||
|
@ -379,8 +379,169 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
|
||||
}
|
||||
}
|
||||
|
||||
// convert deprecated format
|
||||
std::vector<std::shared_ptr<Bonus>> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid)
|
||||
{
|
||||
std::vector<std::shared_ptr<Bonus>> result;
|
||||
|
||||
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)
|
||||
{
|
||||
case 1: //creature specialty
|
||||
{
|
||||
const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter(specCreature, true));
|
||||
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
bonus->val = 1;
|
||||
result.push_back(bonus);
|
||||
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
int stepSize = specCreature.level ? specCreature.level : 5;
|
||||
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
bonus->updater.reset(new ScalingUpdater(specCreature.getAttack(false), stepSize));
|
||||
result.push_back(bonus);
|
||||
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
bonus->updater.reset(new ScalingUpdater(specCreature.getDefence(false), stepSize));
|
||||
result.push_back(bonus);
|
||||
}
|
||||
break;
|
||||
case 2: //secondary skill
|
||||
bonus->type = Bonus::SECONDARY_SKILL_PREMY;
|
||||
bonus->valType = Bonus::PERCENT_TO_BASE;
|
||||
bonus->subtype = spec.subtype;
|
||||
bonus->updater.reset(new ScalingUpdater(spec.val * 20));
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 3: //spell damage bonus, level dependent but calculated elsewhere
|
||||
bonus->type = Bonus::SPECIAL_SPELL_LEV;
|
||||
bonus->subtype = spec.subtype;
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 4: //creature stat boost
|
||||
switch (spec.subtype)
|
||||
{
|
||||
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:
|
||||
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->creatures[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)
|
||||
bonus->type = Bonus::SPECIAL_BLESS_DAMAGE;
|
||||
bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise
|
||||
bonus->additionalInfo = spec.additionalinfo; //damage factor
|
||||
result.push_back(bonus);
|
||||
break;
|
||||
case 7: //maxed mastery for spell
|
||||
bonus->type = Bonus::MAXED_SPELL;
|
||||
bonus->subtype = spec.subtype; //spell id
|
||||
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->creatures;
|
||||
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(auto cre_id : creatures[spec.subtype]->upgrades)
|
||||
{
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->subtype = cre_id;
|
||||
result.push_back(bonus);
|
||||
}
|
||||
}
|
||||
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:
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 2:
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
return bonus;
|
||||
};
|
||||
|
||||
//deprecated, used only for original spciealties
|
||||
for(const JsonNode &specialty : node["specialties"].Vector())
|
||||
{
|
||||
@ -391,19 +552,22 @@ void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node)
|
||||
spec.subtype = specialty["subtype"].Float();
|
||||
spec.additionalinfo = specialty["info"].Float();
|
||||
|
||||
hero->spec.push_back(spec); //put a copy of dummy
|
||||
for(std::shared_ptr<Bonus> bonus : SpecialtyInfoToBonuses(spec, sid))
|
||||
hero->specialty.push_back(bonus);
|
||||
}
|
||||
//new format, using bonus system
|
||||
for(const JsonNode &specialty : node["specialty"].Vector())
|
||||
for(const JsonNode & specialty : node["specialty"].Vector())
|
||||
{
|
||||
SSpecialtyBonus hs;
|
||||
hs.growsWithLevel = specialty["growsWithLevel"].Bool();
|
||||
for (const JsonNode & bonus : specialty["bonuses"].Vector())
|
||||
//deprecated new format
|
||||
if(!specialty["bonuses"].isNull())
|
||||
{
|
||||
auto b = JsonUtils::parseBonus(bonus);
|
||||
hs.bonuses.push_back (b);
|
||||
for (const JsonNode & bonus : specialty["bonuses"].Vector())
|
||||
hero->specialty.push_back(prepSpec(JsonUtils::parseBonus(bonus)));
|
||||
}
|
||||
else //proper new format
|
||||
{
|
||||
hero->specialty.push_back(prepSpec(JsonUtils::parseBonus(specialty)));
|
||||
}
|
||||
hero->specialty.push_back (hs); //now, how to get CGHeroInstance from it?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,8 +71,9 @@ 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> spec;
|
||||
std::vector<SSpecialtyBonus> specialty;
|
||||
std::vector<SSpecialtyInfo> specDeprecated;
|
||||
std::vector<SSpecialtyBonus> specialtyDeprecated;
|
||||
BonusList specialty;
|
||||
std::set<SpellID> spells;
|
||||
bool haveSpellBook;
|
||||
bool special; // hero is special and won't be placed in game (unless preset on map), e.g. campaign heroes
|
||||
@ -98,7 +99,8 @@ public:
|
||||
h & initialArmy;
|
||||
h & heroClass;
|
||||
h & secSkillsInit;
|
||||
h & spec;
|
||||
//h & specDeprecated;
|
||||
//h & specialtyDeprecated;
|
||||
h & specialty;
|
||||
h & spells;
|
||||
h & haveSpellBook;
|
||||
@ -120,6 +122,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// convert deprecated format
|
||||
std::vector<std::shared_ptr<Bonus>> SpecialtyInfoToBonuses(const SSpecialtyInfo & spec, int sid);
|
||||
|
||||
class DLL_LINKAGE CHeroClass
|
||||
{
|
||||
public:
|
||||
|
@ -827,6 +827,8 @@ void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||
assert(!vstd::contains(exportedBonuses, b));
|
||||
exportedBonuses.push_back(b);
|
||||
exportBonus(b);
|
||||
if(b->updater)
|
||||
b->updater->update(*b, *this);
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
@ -1563,7 +1565,11 @@ void LimiterList::add( TLimiterPtr limiter )
|
||||
limiters.push_back(limiter);
|
||||
}
|
||||
|
||||
bool ScalingUpdater::update(Bonus & b, const CBonusSystemNode & context)
|
||||
ScalingUpdater::ScalingUpdater(int valPer20, int stepSize) : valPer20(valPer20), stepSize(stepSize)
|
||||
{
|
||||
}
|
||||
|
||||
bool ScalingUpdater::update(Bonus & b, const CBonusSystemNode & context) const
|
||||
{
|
||||
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||
{
|
||||
|
@ -365,6 +365,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
||||
h & effectRange;
|
||||
h & limiter;
|
||||
h & propagator;
|
||||
h & updater;
|
||||
}
|
||||
|
||||
template <typename Ptr>
|
||||
@ -709,7 +710,7 @@ public:
|
||||
void popBonuses(const CSelector &s);
|
||||
///updates count of remaining turns and removes outdated bonuses by selector
|
||||
void reduceBonusDurations(const CSelector &s);
|
||||
//run update decorators
|
||||
//run updaters attached to bonuses
|
||||
void updateBonuses();
|
||||
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
||||
virtual std::string nodeName() const;
|
||||
@ -1014,7 +1015,7 @@ void BonusList::insert(const int position, InputIterator first, InputIterator la
|
||||
changed();
|
||||
}
|
||||
|
||||
// bonus decorators for updating bonuses based on events (e.g. hero gaining level)
|
||||
// observers for updating bonuses based on certain events (e.g. hero gaining level)
|
||||
|
||||
class DLL_LINKAGE IUpdater
|
||||
{
|
||||
@ -1028,8 +1029,10 @@ public:
|
||||
|
||||
struct DLL_LINKAGE ScalingUpdater : public IUpdater
|
||||
{
|
||||
int valPer20 = 0;
|
||||
int stepSize = 1;
|
||||
int valPer20;
|
||||
int stepSize;
|
||||
|
||||
ScalingUpdater(int valPer20, int stepSize = 1);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -1038,5 +1041,5 @@ struct DLL_LINKAGE ScalingUpdater : public IUpdater
|
||||
h & stepSize;
|
||||
}
|
||||
|
||||
bool update(Bonus & b, const CBonusSystemNode & context);
|
||||
bool update(Bonus & b, const CBonusSystemNode & context) const override;
|
||||
};
|
||||
|
@ -496,9 +496,8 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
|
||||
void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
blockVisit = true;
|
||||
auto hs = new HeroSpecial();
|
||||
hs->setNodeType(CBonusSystemNode::SPECIALTY);
|
||||
attachTo(hs); //do we ever need to detach it?
|
||||
specialty.setNodeType(CBonusSystemNode::SPECIALTY);
|
||||
attachTo(&specialty); //do we ever need to detach it?
|
||||
|
||||
if(!type)
|
||||
initHero(rand); //TODO: set up everything for prison before specialties are configured
|
||||
@ -514,246 +513,24 @@ void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||
appearance = customApp.get();
|
||||
}
|
||||
|
||||
for(const auto &spec : type->spec) //TODO: unfity with bonus system
|
||||
{
|
||||
auto bonus = std::make_shared<Bonus>();
|
||||
bonus->val = spec.val;
|
||||
bonus->sid = id.getNum(); //from the hero, specialty has no unique id
|
||||
bonus->duration = Bonus::PERMANENT;
|
||||
bonus->source = Bonus::HERO_SPECIAL;
|
||||
switch (spec.type)
|
||||
{
|
||||
case 1:// creature specialty
|
||||
{
|
||||
hs->growsWithLevel = true;
|
||||
|
||||
const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty
|
||||
|
||||
//bonus->additionalInfo = spec.additionalinfo; //creature id, should not be used again - this works only with limiter
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter (specCreature, true)); //with upgrades
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
hs->addNewBonus(bonus);
|
||||
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
hs->addNewBonus(bonus);
|
||||
//values will be calculated later
|
||||
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
bonus->val = 1; //+1 speed
|
||||
hs->addNewBonus(bonus);
|
||||
}
|
||||
break;
|
||||
case 2://secondary skill
|
||||
hs->growsWithLevel = true;
|
||||
bonus->type = Bonus::SPECIAL_SECONDARY_SKILL; //needs to be recalculated with level, based on this value
|
||||
bonus->valType = Bonus::BASE_NUMBER; // to receive nonzero value
|
||||
bonus->subtype = spec.subtype; //skill id
|
||||
bonus->val = spec.val; //value per level, in percent
|
||||
hs->addNewBonus(bonus);
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
|
||||
switch (spec.additionalinfo)
|
||||
{
|
||||
case 0: //normal
|
||||
bonus->valType = Bonus::PERCENT_TO_BASE;
|
||||
break;
|
||||
case 1: //when it's navigation or there's no 'base' at all
|
||||
bonus->valType = Bonus::PERCENT_TO_ALL;
|
||||
break;
|
||||
}
|
||||
bonus->type = Bonus::SECONDARY_SKILL_PREMY; //value will be calculated later
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 3://spell damage bonus, level dependent but calculated elsewhere
|
||||
bonus->type = Bonus::SPECIAL_SPELL_LEV;
|
||||
bonus->subtype = spec.subtype;
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 4://creature stat boost
|
||||
switch (spec.subtype)
|
||||
{
|
||||
case 1://attack
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 2://defense
|
||||
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://hp
|
||||
bonus->type = Bonus::STACK_HEALTH;
|
||||
break;
|
||||
case 5:
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
bonus->additionalInfo = spec.additionalinfo; //creature id
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
bonus->limiter.reset(new CCreatureTypeLimiter (*VLC->creh->creatures[spec.additionalinfo], true));
|
||||
hs->addNewBonus(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
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 6://damage bonus for bless (Adela)
|
||||
bonus->type = Bonus::SPECIAL_BLESS_DAMAGE;
|
||||
bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise
|
||||
bonus->additionalInfo = spec.additionalinfo; //damage factor
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 7://maxed mastery for spell
|
||||
bonus->type = Bonus::MAXED_SPELL;
|
||||
bonus->subtype = spec.subtype; //spell i
|
||||
hs->addNewBonus(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
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 9://upgrade creatures
|
||||
{
|
||||
const auto &creatures = VLC->creh->creatures;
|
||||
bonus->type = Bonus::SPECIAL_UPGRADE;
|
||||
bonus->subtype = spec.subtype; //base id
|
||||
bonus->additionalInfo = spec.additionalinfo; //target id
|
||||
hs->addNewBonus(bonus);
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
|
||||
for(auto cre_id : creatures[spec.subtype]->upgrades)
|
||||
{
|
||||
bonus->subtype = cre_id; //propagate for regular upgrades of base creature
|
||||
hs->addNewBonus(bonus);
|
||||
bonus = std::make_shared<Bonus>(*bonus);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10://resource generation
|
||||
bonus->type = Bonus::GENERATE_RESOURCE;
|
||||
bonus->subtype = spec.subtype;
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 11://starting skill with mastery (Adrienne)
|
||||
setSecSkillLevel(SecondarySkill(spec.val), spec.additionalinfo, true);
|
||||
break;
|
||||
case 12://army speed
|
||||
bonus->type = Bonus::STACKS_SPEED;
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
case 13://Dragon bonuses (Mutare)
|
||||
bonus->type = Bonus::PRIMARY_SKILL;
|
||||
bonus->valType = Bonus::ADDITIVE_VALUE;
|
||||
switch (spec.subtype)
|
||||
{
|
||||
case 1:
|
||||
bonus->subtype = PrimarySkill::ATTACK;
|
||||
break;
|
||||
case 2:
|
||||
bonus->subtype = PrimarySkill::DEFENSE;
|
||||
break;
|
||||
}
|
||||
bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE));
|
||||
hs->addNewBonus(bonus);
|
||||
break;
|
||||
default:
|
||||
logGlobal->warn("Unexpected hero %s specialty %d", type->name, spec.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
specialty.push_back(hs); //will it work?
|
||||
|
||||
for (auto hs2 : type->specialty) //copy active (probably growing) bonuses from hero prootype to hero object
|
||||
{
|
||||
auto hs = new HeroSpecial();
|
||||
attachTo(hs); //do we ever need to detach it?
|
||||
|
||||
hs->setNodeType(CBonusSystemNode::SPECIALTY);
|
||||
for (auto bonus : hs2.bonuses)
|
||||
{
|
||||
hs->addNewBonus (bonus);
|
||||
}
|
||||
hs->growsWithLevel = hs2.growsWithLevel;
|
||||
|
||||
specialty.push_back(hs); //will it work?
|
||||
}
|
||||
//copy active (probably growing) bonuses from hero prototype to hero object
|
||||
for(std::shared_ptr<Bonus> b : type->specialty)
|
||||
specialty.addNewBonus(b);
|
||||
//dito for old-style bonuses -> compatibility for old savegames
|
||||
for(SSpecialtyBonus & sb : type->specialtyDeprecated)
|
||||
for(std::shared_ptr<Bonus> b : sb.bonuses)
|
||||
specialty.addNewBonus(b);
|
||||
for(SSpecialtyInfo & spec : type->specDeprecated)
|
||||
for(std::shared_ptr<Bonus> b : SpecialtyInfoToBonuses(spec, type->ID.getNum()))
|
||||
specialty.addNewBonus(b);
|
||||
|
||||
//initialize bonuses
|
||||
recreateSecondarySkillsBonuses();
|
||||
Updatespecialty();
|
||||
updateBonuses();
|
||||
|
||||
mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
|
||||
type->name = name;
|
||||
}
|
||||
void CGHeroInstance::Updatespecialty() //TODO: calculate special value of bonuses on-the-fly?
|
||||
{
|
||||
for (auto hs : specialty)
|
||||
{
|
||||
if (hs->growsWithLevel)
|
||||
{
|
||||
//const auto &creatures = VLC->creh->creatures;
|
||||
|
||||
for(auto& b : hs->getBonusList())
|
||||
{
|
||||
switch (b->type)
|
||||
{
|
||||
case Bonus::SECONDARY_SKILL_PREMY:
|
||||
b->val = (hs->valOfBonuses(Bonus::SPECIAL_SECONDARY_SKILL, b->subtype) * level);
|
||||
break; //use only hero skills as bonuses to avoid feedback loop
|
||||
case Bonus::PRIMARY_SKILL: //for creatures, that is
|
||||
{
|
||||
const CCreature * cre = nullptr;
|
||||
int creLevel = 0;
|
||||
if (auto creatureLimiter = std::dynamic_pointer_cast<CCreatureTypeLimiter>(b->limiter)) //TODO: more general eveluation of bonuses?
|
||||
{
|
||||
cre = creatureLimiter->creature;
|
||||
creLevel = cre->level;
|
||||
if (!creLevel)
|
||||
{
|
||||
creLevel = 5; //treat ballista as tier 5
|
||||
}
|
||||
}
|
||||
else //no creature found, can't calculate value
|
||||
{
|
||||
logGlobal->warn("Primary skill specialty growth supported only with creature type limiters");
|
||||
break;
|
||||
}
|
||||
|
||||
double primSkillModifier = (int)(level / creLevel) / 20.0;
|
||||
int param;
|
||||
switch (b->subtype)
|
||||
{
|
||||
case PrimarySkill::ATTACK:
|
||||
param = cre->getPrimSkillLevel(PrimarySkill::ATTACK);
|
||||
break;
|
||||
case PrimarySkill::DEFENSE:
|
||||
param = cre->getPrimSkillLevel(PrimarySkill::DEFENSE);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
b->val = ceil(param * (1 + primSkillModifier)) - param; //yep, overcomplicated but matches original
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||
{
|
||||
@ -1211,11 +988,7 @@ int CGHeroInstance::maxSpellLevel() const
|
||||
void CGHeroInstance::deserializationFix()
|
||||
{
|
||||
artDeserializationFix(this);
|
||||
|
||||
for (auto hs : specialty)
|
||||
{
|
||||
attachTo (hs);
|
||||
}
|
||||
attachTo(&specialty);
|
||||
}
|
||||
|
||||
CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
||||
@ -1468,8 +1241,8 @@ void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
|
||||
}
|
||||
}
|
||||
|
||||
//specialty
|
||||
Updatespecialty();
|
||||
//specialty and other bonuses that scale with level
|
||||
updateBonuses();
|
||||
}
|
||||
|
||||
void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
|
||||
|
@ -108,7 +108,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<HeroSpecial*> specialty;
|
||||
std::vector<HeroSpecial*> specialtyDeprecated;
|
||||
CBonusSystemNode specialty;
|
||||
|
||||
struct DLL_LINKAGE SecondarySkillsInfo
|
||||
{
|
||||
@ -215,7 +216,6 @@ public:
|
||||
void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
|
||||
ui8 maxlevelsToMagicSchool() const;
|
||||
ui8 maxlevelsToWisdom() const;
|
||||
void Updatespecialty();
|
||||
void recreateSecondarySkillsBonuses();
|
||||
void updateSkill(SecondarySkill which, int val);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user