mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +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",
|
"type":"array",
|
||||||
"description": "Description of hero specialty using bonus system",
|
"description": "Description of hero specialty using bonus system",
|
||||||
"items": {
|
"items": {
|
||||||
"type":"object",
|
"oneOf" : [
|
||||||
"additionalProperties" : false,
|
{
|
||||||
"required" : [ "bonuses" ],
|
"type" : "object",
|
||||||
"properties":{
|
"additionalProperties" : false,
|
||||||
"growsWithLevel" : {
|
"required" : [ "bonuses" ],
|
||||||
"type" : "boolean",
|
"properties" : {
|
||||||
"description" : "Specialty growth with level, so far only SECONDARY_SKILL_PREMY and PRIMATY SKILL with creature limiter can grow"
|
"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",
|
"type" : "object",
|
||||||
"description": "List of bonuses",
|
"description" : "List of bonuses",
|
||||||
"items": { "$ref" : "vcmi:bonus" }
|
"items" : { "$ref" : "vcmi:bonus" }
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spellbook": {
|
"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)
|
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
|
//deprecated, used only for original spciealties
|
||||||
for(const JsonNode &specialty : node["specialties"].Vector())
|
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.subtype = specialty["subtype"].Float();
|
||||||
spec.additionalinfo = specialty["info"].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
|
//new format, using bonus system
|
||||||
for(const JsonNode &specialty : node["specialty"].Vector())
|
for(const JsonNode & specialty : node["specialty"].Vector())
|
||||||
{
|
{
|
||||||
SSpecialtyBonus hs;
|
//deprecated new format
|
||||||
hs.growsWithLevel = specialty["growsWithLevel"].Bool();
|
if(!specialty["bonuses"].isNull())
|
||||||
for (const JsonNode & bonus : specialty["bonuses"].Vector())
|
|
||||||
{
|
{
|
||||||
auto b = JsonUtils::parseBonus(bonus);
|
for (const JsonNode & bonus : specialty["bonuses"].Vector())
|
||||||
hs.bonuses.push_back (b);
|
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;
|
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<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<SSpecialtyInfo> specDeprecated;
|
||||||
std::vector<SSpecialtyBonus> specialty;
|
std::vector<SSpecialtyBonus> specialtyDeprecated;
|
||||||
|
BonusList specialty;
|
||||||
std::set<SpellID> spells;
|
std::set<SpellID> spells;
|
||||||
bool haveSpellBook;
|
bool haveSpellBook;
|
||||||
bool special; // hero is special and won't be placed in game (unless preset on map), e.g. campaign heroes
|
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 & initialArmy;
|
||||||
h & heroClass;
|
h & heroClass;
|
||||||
h & secSkillsInit;
|
h & secSkillsInit;
|
||||||
h & spec;
|
//h & specDeprecated;
|
||||||
|
//h & specialtyDeprecated;
|
||||||
h & specialty;
|
h & specialty;
|
||||||
h & spells;
|
h & spells;
|
||||||
h & haveSpellBook;
|
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
|
class DLL_LINKAGE CHeroClass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -827,6 +827,8 @@ void CBonusSystemNode::addNewBonus(const std::shared_ptr<Bonus>& b)
|
|||||||
assert(!vstd::contains(exportedBonuses, b));
|
assert(!vstd::contains(exportedBonuses, b));
|
||||||
exportedBonuses.push_back(b);
|
exportedBonuses.push_back(b);
|
||||||
exportBonus(b);
|
exportBonus(b);
|
||||||
|
if(b->updater)
|
||||||
|
b->updater->update(*b, *this);
|
||||||
CBonusSystemNode::treeHasChanged();
|
CBonusSystemNode::treeHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1563,7 +1565,11 @@ void LimiterList::add( TLimiterPtr limiter )
|
|||||||
limiters.push_back(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)
|
if(context.getNodeType() == CBonusSystemNode::HERO)
|
||||||
{
|
{
|
||||||
|
@ -365,6 +365,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
|
|||||||
h & effectRange;
|
h & effectRange;
|
||||||
h & limiter;
|
h & limiter;
|
||||||
h & propagator;
|
h & propagator;
|
||||||
|
h & updater;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Ptr>
|
template <typename Ptr>
|
||||||
@ -709,7 +710,7 @@ public:
|
|||||||
void popBonuses(const CSelector &s);
|
void popBonuses(const CSelector &s);
|
||||||
///updates count of remaining turns and removes outdated bonuses by selector
|
///updates count of remaining turns and removes outdated bonuses by selector
|
||||||
void reduceBonusDurations(const CSelector &s);
|
void reduceBonusDurations(const CSelector &s);
|
||||||
//run update decorators
|
//run updaters attached to bonuses
|
||||||
void updateBonuses();
|
void updateBonuses();
|
||||||
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, bool description) const {return "";}; //description or bonus name
|
||||||
virtual std::string nodeName() const;
|
virtual std::string nodeName() const;
|
||||||
@ -1014,7 +1015,7 @@ void BonusList::insert(const int position, InputIterator first, InputIterator la
|
|||||||
changed();
|
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
|
class DLL_LINKAGE IUpdater
|
||||||
{
|
{
|
||||||
@ -1028,8 +1029,10 @@ public:
|
|||||||
|
|
||||||
struct DLL_LINKAGE ScalingUpdater : public IUpdater
|
struct DLL_LINKAGE ScalingUpdater : public IUpdater
|
||||||
{
|
{
|
||||||
int valPer20 = 0;
|
int valPer20;
|
||||||
int stepSize = 1;
|
int stepSize;
|
||||||
|
|
||||||
|
ScalingUpdater(int valPer20, int stepSize = 1);
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
@ -1038,5 +1041,5 @@ struct DLL_LINKAGE ScalingUpdater : public IUpdater
|
|||||||
h & stepSize;
|
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)
|
void CGHeroInstance::initObj(CRandomGenerator & rand)
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
auto hs = new HeroSpecial();
|
specialty.setNodeType(CBonusSystemNode::SPECIALTY);
|
||||||
hs->setNodeType(CBonusSystemNode::SPECIALTY);
|
attachTo(&specialty); //do we ever need to detach it?
|
||||||
attachTo(hs); //do we ever need to detach it?
|
|
||||||
|
|
||||||
if(!type)
|
if(!type)
|
||||||
initHero(rand); //TODO: set up everything for prison before specialties are configured
|
initHero(rand); //TODO: set up everything for prison before specialties are configured
|
||||||
@ -514,246 +513,24 @@ void CGHeroInstance::initObj(CRandomGenerator & rand)
|
|||||||
appearance = customApp.get();
|
appearance = customApp.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto &spec : type->spec) //TODO: unfity with bonus system
|
//copy active (probably growing) bonuses from hero prototype to hero object
|
||||||
{
|
for(std::shared_ptr<Bonus> b : type->specialty)
|
||||||
auto bonus = std::make_shared<Bonus>();
|
specialty.addNewBonus(b);
|
||||||
bonus->val = spec.val;
|
//dito for old-style bonuses -> compatibility for old savegames
|
||||||
bonus->sid = id.getNum(); //from the hero, specialty has no unique id
|
for(SSpecialtyBonus & sb : type->specialtyDeprecated)
|
||||||
bonus->duration = Bonus::PERMANENT;
|
for(std::shared_ptr<Bonus> b : sb.bonuses)
|
||||||
bonus->source = Bonus::HERO_SPECIAL;
|
specialty.addNewBonus(b);
|
||||||
switch (spec.type)
|
for(SSpecialtyInfo & spec : type->specDeprecated)
|
||||||
{
|
for(std::shared_ptr<Bonus> b : SpecialtyInfoToBonuses(spec, type->ID.getNum()))
|
||||||
case 1:// creature specialty
|
specialty.addNewBonus(b);
|
||||||
{
|
|
||||||
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?
|
|
||||||
}
|
|
||||||
|
|
||||||
//initialize bonuses
|
//initialize bonuses
|
||||||
recreateSecondarySkillsBonuses();
|
recreateSecondarySkillsBonuses();
|
||||||
Updatespecialty();
|
updateBonuses();
|
||||||
|
|
||||||
mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
|
mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one
|
||||||
type->name = name;
|
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()
|
void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||||
{
|
{
|
||||||
@ -1211,11 +988,7 @@ int CGHeroInstance::maxSpellLevel() const
|
|||||||
void CGHeroInstance::deserializationFix()
|
void CGHeroInstance::deserializationFix()
|
||||||
{
|
{
|
||||||
artDeserializationFix(this);
|
artDeserializationFix(this);
|
||||||
|
attachTo(&specialty);
|
||||||
for (auto hs : specialty)
|
|
||||||
{
|
|
||||||
attachTo (hs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
CBonusSystemNode * CGHeroInstance::whereShouldBeAttached(CGameState *gs)
|
||||||
@ -1468,8 +1241,8 @@ void CGHeroInstance::levelUp(std::vector<SecondarySkill> skills)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//specialty
|
//specialty and other bonuses that scale with level
|
||||||
Updatespecialty();
|
updateBonuses();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
|
void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
|
||||||
|
@ -108,7 +108,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<HeroSpecial*> specialty;
|
std::vector<HeroSpecial*> specialtyDeprecated;
|
||||||
|
CBonusSystemNode specialty;
|
||||||
|
|
||||||
struct DLL_LINKAGE SecondarySkillsInfo
|
struct DLL_LINKAGE SecondarySkillsInfo
|
||||||
{
|
{
|
||||||
@ -215,7 +216,6 @@ public:
|
|||||||
void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
|
void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
|
||||||
ui8 maxlevelsToMagicSchool() const;
|
ui8 maxlevelsToMagicSchool() const;
|
||||||
ui8 maxlevelsToWisdom() const;
|
ui8 maxlevelsToWisdom() const;
|
||||||
void Updatespecialty();
|
|
||||||
void recreateSecondarySkillsBonuses();
|
void recreateSecondarySkillsBonuses();
|
||||||
void updateSkill(SecondarySkill which, int val);
|
void updateSkill(SecondarySkill which, int val);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user