mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-03 00:46:55 +02:00
More flexible way of spell school access
This commit is contained in:
@ -31,6 +31,42 @@
|
|||||||
namespace SpellConfig
|
namespace SpellConfig
|
||||||
{
|
{
|
||||||
static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
|
static const std::string LEVEL_NAMES[] = {"none", "basic", "advanced", "expert"};
|
||||||
|
|
||||||
|
static const SpellSchoolInfo SCHOOL[4] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
ESpellSchool::AIR,
|
||||||
|
Bonus::AIR_SPELL_DMG_PREMY,
|
||||||
|
Bonus::AIR_IMMUNITY,
|
||||||
|
"air",
|
||||||
|
SecondarySkill::AIR_MAGIC,
|
||||||
|
Bonus::AIR_SPELLS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ESpellSchool::FIRE,
|
||||||
|
Bonus::FIRE_SPELL_DMG_PREMY,
|
||||||
|
Bonus::FIRE_IMMUNITY,
|
||||||
|
"fire",
|
||||||
|
SecondarySkill::FIRE_MAGIC,
|
||||||
|
Bonus::FIRE_SPELLS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ESpellSchool::WATER,
|
||||||
|
Bonus::WATER_SPELL_DMG_PREMY,
|
||||||
|
Bonus::WATER_IMMUNITY,
|
||||||
|
"water",
|
||||||
|
SecondarySkill::WATER_MAGIC,
|
||||||
|
Bonus::WATER_SPELLS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ESpellSchool::EARTH,
|
||||||
|
Bonus::EARTH_SPELL_DMG_PREMY,
|
||||||
|
Bonus::EARTH_IMMUNITY,
|
||||||
|
"earth",
|
||||||
|
SecondarySkill::EARTH_MAGIC,
|
||||||
|
Bonus::EARTH_SPELLS
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
///CSpell::LevelInfo
|
///CSpell::LevelInfo
|
||||||
@ -75,14 +111,13 @@ bool CSpell::isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const
|
|||||||
|
|
||||||
bool inTome = false;
|
bool inTome = false;
|
||||||
|
|
||||||
for(const SpellSchoolInfo & cnf : SPELL_SCHOOL_CONFIG)
|
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
if(school.at(cnf.id) && caster->hasBonusOfType(cnf.knoledgeBonus))
|
if(school.at(cnf.id) && caster->hasBonusOfType(cnf.knoledgeBonus))
|
||||||
{
|
{
|
||||||
inTome = true;
|
inTome = stop = true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (isSpecialSpell())
|
if (isSpecialSpell())
|
||||||
{
|
{
|
||||||
@ -118,14 +153,11 @@ ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance* caster, const
|
|||||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
|
ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
|
||||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id.toEnum())) / 100.0;
|
ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id.toEnum())) / 100.0;
|
||||||
|
|
||||||
for(const SpellSchoolInfo & cnf : SPELL_SCHOOL_CONFIG)
|
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
if(school.at(cnf.id))
|
ret *= (100.0 + caster->valOfBonuses(cnf.damagePremyBonus)) / 100.0;
|
||||||
{
|
stop = true; //only bonus from one school is used
|
||||||
ret *= (100.0 + caster->valOfBonuses(cnf.damagePremyBonus)) / 100.0;
|
});
|
||||||
break; //only bonus from one school is used
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
|
if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
|
||||||
ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, id.toEnum()) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
|
ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, id.toEnum()) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
|
||||||
@ -149,16 +181,16 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec
|
|||||||
{
|
{
|
||||||
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
||||||
|
|
||||||
for(const SpellSchoolInfo & cnf : SPELL_SCHOOL_CONFIG)
|
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
if(school.at(cnf.id) && affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id))
|
if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id))
|
||||||
{
|
{
|
||||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id);
|
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id);
|
||||||
ret /= 100;
|
ret /= 100;
|
||||||
break; //only bonus from one school is used
|
stop = true;//only bonus from one school is used
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
//general spell dmg reduction
|
//general spell dmg reduction
|
||||||
if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1))
|
if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1))
|
||||||
{
|
{
|
||||||
@ -236,6 +268,22 @@ CSpell::TargetInfo CSpell::getTargetInfo(const int level) const
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSpell::forEachSchool(const std::function<void(const SpellSchoolInfo &, bool &)>& cb) const
|
||||||
|
{
|
||||||
|
bool stop = false;
|
||||||
|
for(const SpellSchoolInfo & cnf : SpellConfig::SCHOOL)
|
||||||
|
{
|
||||||
|
if(school.at(cnf.id))
|
||||||
|
{
|
||||||
|
cb(cnf, stop);
|
||||||
|
|
||||||
|
if(stop)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CSpell::isCombatSpell() const
|
bool CSpell::isCombatSpell() const
|
||||||
{
|
{
|
||||||
return combatSpell;
|
return combatSpell;
|
||||||
@ -400,23 +448,30 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj)
|
|||||||
|
|
||||||
//6. Check elemental immunities
|
//6. Check elemental immunities
|
||||||
|
|
||||||
for(const SpellSchoolInfo & cnf : SPELL_SCHOOL_CONFIG)
|
ESpellCastProblem::ESpellCastProblem tmp = ESpellCastProblem::NOT_DECIDED;
|
||||||
|
|
||||||
|
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
if(school.at(cnf.id))
|
auto element = cnf.immunityBonus;
|
||||||
|
|
||||||
|
if(obj->hasBonusOfType(element, 0)) //always resist if immune to all spells altogether
|
||||||
{
|
{
|
||||||
auto element = cnf.immunityBonus;
|
tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
|
stop = true;
|
||||||
if(obj->hasBonusOfType(element, 0)) //always resist if immune to all spells altogether
|
}
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
else if(!isPositive()) //negative or indifferent
|
||||||
else if(!isPositive()) //negative or indifferent
|
{
|
||||||
|
if((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
|
||||||
{
|
{
|
||||||
if((isDamageSpell() && obj->hasBonusOfType(element, 2)) || obj->hasBonusOfType(element, 1))
|
tmp = ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
stop = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
if(tmp != ESpellCastProblem::NOT_DECIDED)
|
||||||
|
return tmp;
|
||||||
|
|
||||||
TBonusListPtr levelImmunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
|
TBonusListPtr levelImmunities = obj->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
|
||||||
|
|
||||||
if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id)
|
if(obj->hasBonusOfType(Bonus::SPELL_IMMUNITY, id)
|
||||||
@ -691,7 +746,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
|
|||||||
|
|
||||||
const auto schoolNames = json["school"];
|
const auto schoolNames = json["school"];
|
||||||
|
|
||||||
for(const SpellSchoolInfo & info : SPELL_SCHOOL_CONFIG)
|
for(const SpellSchoolInfo & info : SpellConfig::SCHOOL)
|
||||||
{
|
{
|
||||||
spell->school[info.id] = schoolNames[info.jsonName].Bool();
|
spell->school[info.id] = schoolNames[info.jsonName].Bool();
|
||||||
}
|
}
|
||||||
|
@ -40,42 +40,6 @@ struct SpellSchoolInfo
|
|||||||
Bonus::BonusType knoledgeBonus;
|
Bonus::BonusType knoledgeBonus;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const SpellSchoolInfo SPELL_SCHOOL_CONFIG[4] =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
ESpellSchool::AIR,
|
|
||||||
Bonus::AIR_SPELL_DMG_PREMY,
|
|
||||||
Bonus::AIR_IMMUNITY,
|
|
||||||
"air",
|
|
||||||
SecondarySkill::AIR_MAGIC,
|
|
||||||
Bonus::AIR_SPELLS
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ESpellSchool::FIRE,
|
|
||||||
Bonus::FIRE_SPELL_DMG_PREMY,
|
|
||||||
Bonus::FIRE_IMMUNITY,
|
|
||||||
"fire",
|
|
||||||
SecondarySkill::FIRE_MAGIC,
|
|
||||||
Bonus::FIRE_SPELLS
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ESpellSchool::WATER,
|
|
||||||
Bonus::WATER_SPELL_DMG_PREMY,
|
|
||||||
Bonus::WATER_IMMUNITY,
|
|
||||||
"water",
|
|
||||||
SecondarySkill::WATER_MAGIC,
|
|
||||||
Bonus::WATER_SPELLS
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ESpellSchool::EARTH,
|
|
||||||
Bonus::EARTH_SPELL_DMG_PREMY,
|
|
||||||
Bonus::EARTH_IMMUNITY,
|
|
||||||
"earth",
|
|
||||||
SecondarySkill::EARTH_MAGIC,
|
|
||||||
Bonus::EARTH_SPELLS
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///callback to be provided by server
|
///callback to be provided by server
|
||||||
class DLL_LINKAGE SpellCastEnvironment
|
class DLL_LINKAGE SpellCastEnvironment
|
||||||
{
|
{
|
||||||
@ -244,6 +208,13 @@ public:
|
|||||||
|
|
||||||
si32 getProbability(const TFaction factionId) const;
|
si32 getProbability(const TFaction factionId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls cb for each school this spell belongs to
|
||||||
|
*
|
||||||
|
* Set stop to true to abort looping
|
||||||
|
*/
|
||||||
|
void forEachSchool(const std::function<void (const SpellSchoolInfo &, bool &)> & cb) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns resource name of icon for SPELL_IMMUNITY bonus
|
* Returns resource name of icon for SPELL_IMMUNITY bonus
|
||||||
*/
|
*/
|
||||||
|
@ -864,20 +864,17 @@ ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSc
|
|||||||
{
|
{
|
||||||
si16 skill = -1; //skill level
|
si16 skill = -1; //skill level
|
||||||
|
|
||||||
for(const SpellSchoolInfo & cnf : SPELL_SCHOOL_CONFIG)
|
spell->forEachSchool([&, this](const SpellSchoolInfo & cnf, bool & stop)
|
||||||
{
|
{
|
||||||
if(spell->school.at(cnf.id))
|
int thisSchool = std::max<int>(getSecSkillLevel(cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id)));
|
||||||
{
|
if(thisSchool > skill)
|
||||||
int thisSchool = std::max<int>(getSecSkillLevel(cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id)));
|
{
|
||||||
if(thisSchool > skill)
|
skill = thisSchool;
|
||||||
{
|
if(outSelectedSchool)
|
||||||
skill = thisSchool;
|
*outSelectedSchool = (ui8)cnf.id;
|
||||||
if(outSelectedSchool)
|
}
|
||||||
*outSelectedSchool = (ui8)cnf.id;
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
|
vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
|
||||||
vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
|
vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
|
||||||
if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero specialty (Daremyth, Melodia)
|
if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero specialty (Daremyth, Melodia)
|
||||||
|
Reference in New Issue
Block a user