mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Centralize creature spell bonuses handling
* unified heal and damage calculation * apply SPELL_DAMAGE_REDUCTION, MORE_DAMAGE_FROM_SPELL for Fairy dragon
This commit is contained in:
parent
67bd698019
commit
baba3a46e1
@ -28,16 +28,10 @@ void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment* env,
|
||||
{
|
||||
StacksHealedOrResurrected::HealInfo hi;
|
||||
hi.stackID = (attackedCre)->ID;
|
||||
if (parameters.casterStack) //casted by creature
|
||||
{
|
||||
hi.healedHP = attackedCre->calculateHealedHealthPoints(hpGained, resurrect);
|
||||
}
|
||||
else
|
||||
{
|
||||
int stackHPgained = parameters.casterHero->getSpellBonus(owner, hpGained, attackedCre);
|
||||
hi.healedHP = attackedCre->calculateHealedHealthPoints(stackHPgained, resurrect);
|
||||
}
|
||||
|
||||
int stackHPgained = hpGained;
|
||||
if(ctx.caster != nullptr)
|
||||
stackHPgained = ctx.caster->getSpellBonus(owner, hpGained, attackedCre);
|
||||
hi.healedHP = attackedCre->calculateHealedHealthPoints(stackHPgained, resurrect);
|
||||
hi.lowLevelResurrection = (healLevel == EHealLevel::RESURRECT);
|
||||
shr.healedStacks.push_back(hi);
|
||||
}
|
||||
@ -45,23 +39,11 @@ void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment* env,
|
||||
env->sendAndApply(&shr);
|
||||
}
|
||||
|
||||
int HealingSpellMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, const SpellCastContext& ctx) const
|
||||
int HealingSpellMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, SpellCastContext& ctx) const
|
||||
{
|
||||
if(parameters.casterStack)
|
||||
{
|
||||
int unitSpellPower = parameters.casterStack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, owner->id.toEnum());
|
||||
if(unitSpellPower)
|
||||
return parameters.casterStack->count * unitSpellPower; //Archangel
|
||||
else //Faerie Dragon-like effect - commanders(?)
|
||||
{
|
||||
int spellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * parameters.casterStack->count / 100;
|
||||
return spellPower * owner->power + owner->getPower(ctx.effectLevel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return parameters.usedSpellPower * owner->power + owner->getPower(ctx.effectLevel); //???
|
||||
}
|
||||
if(ctx.effectValue != 0)
|
||||
return ctx.effectValue; //Archangel
|
||||
return owner->calculateRawEffectValue(ctx.effectLevel, ctx.effectPower); //???
|
||||
}
|
||||
|
||||
///AntimagicMechanics
|
||||
@ -411,7 +393,7 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, Bat
|
||||
obstacle->casterSide = parameters.casterSide;
|
||||
obstacle->ID = owner->id;
|
||||
obstacle->spellLevel = parameters.spellLvl;
|
||||
obstacle->casterSpellPower = parameters.usedSpellPower;
|
||||
obstacle->casterSpellPower = ctx.effectPower;
|
||||
obstacle->uniqueID = obstacleIdToGive++;
|
||||
|
||||
BattleObstaclePlaced bop;
|
||||
@ -583,13 +565,13 @@ void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, Ba
|
||||
env->sendAndApply(&bsr);
|
||||
}
|
||||
|
||||
int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, const SpellCastContext& ctx) const
|
||||
int SacrificeMechanics::calculateHealedHP(const SpellCastEnvironment* env, const BattleSpellCastParameters& parameters, SpellCastContext& ctx) const
|
||||
{
|
||||
int res = 0;
|
||||
if(nullptr == parameters.selectedStack)
|
||||
env->complain("No stack to sacrifice.");
|
||||
else
|
||||
res = (parameters.usedSpellPower + parameters.selectedStack->MaxHealth() + owner->getPower(ctx.effectLevel)) * parameters.selectedStack->count;
|
||||
res = (ctx.effectPower + parameters.selectedStack->MaxHealth() + owner->getPower(ctx.effectLevel)) * parameters.selectedStack->count;
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -644,7 +626,7 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battl
|
||||
//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
|
||||
int percentBonus = parameters.casterHero ? parameters.casterHero->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0;
|
||||
|
||||
bsa.amount = parameters.usedSpellPower
|
||||
bsa.amount = ctx.effectPower
|
||||
* owner->getPower(parameters.spellLvl)
|
||||
* (100 + percentBonus) / 100.0; //new feature - percentage bonus
|
||||
if(bsa.amount)
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
HealingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
virtual int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const SpellCastContext & ctx) const;
|
||||
virtual int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
||||
virtual EHealLevel getHealLevel(int effectLevel) const = 0;
|
||||
};
|
||||
|
||||
@ -130,7 +130,7 @@ public:
|
||||
ESpellCastProblem::ESpellCastProblem canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const override;
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, const SpellCastContext & ctx) const override;
|
||||
int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
///all rising spells but SACRIFICE
|
||||
|
@ -325,7 +325,28 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
||||
StacksInjured si;
|
||||
SpellCastContext ctx(attackedCres, sc, si);
|
||||
ctx.effectLevel = calculateEffectLevel(parameters);
|
||||
ctx.effectPower = parameters.usedSpellPower;
|
||||
ctx.enchantPower = parameters.usedSpellPower;
|
||||
ctx.effectValue = 0;
|
||||
if(parameters.casterStack)
|
||||
ctx.caster = parameters.casterStack;
|
||||
else if(parameters.mode == ECastingMode::HERO_CASTING)
|
||||
ctx.caster = parameters.cb->battleGetFightingHero(parameters.casterSide);
|
||||
|
||||
if(parameters.casterStack && parameters.mode != ECastingMode::MAGIC_MIRROR)
|
||||
{
|
||||
auto enchantPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
|
||||
if(ctx.enchantPower == 0)
|
||||
ctx.enchantPower = enchantPower;
|
||||
//Fairy Dragon, etc.
|
||||
int effectPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * parameters.casterStack->count / 100;
|
||||
if(ctx.effectPower == 0)
|
||||
ctx.effectPower = effectPower;
|
||||
//Archangel, etc
|
||||
int unitSpellPower = parameters.casterStack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, owner->id.toEnum());
|
||||
if(unitSpellPower != 0)
|
||||
ctx.effectValue = parameters.casterStack->count * unitSpellPower;
|
||||
}
|
||||
applyBattleEffects(env, parameters, ctx);
|
||||
|
||||
env->sendAndApply(&sc);
|
||||
@ -526,25 +547,16 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
//applying effects
|
||||
if(owner->isOffensiveSpell())
|
||||
{
|
||||
int spellDamage = 0;
|
||||
if(parameters.casterStack && parameters.mode != ECastingMode::MAGIC_MIRROR)
|
||||
{
|
||||
int unitSpellPower = parameters.casterStack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, owner->id.toEnum());
|
||||
if(unitSpellPower)
|
||||
spellDamage = parameters.casterStack->count * unitSpellPower; //TODO: handle immunities
|
||||
else //Faerie Dragon
|
||||
{
|
||||
parameters.usedSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * parameters.casterStack->count / 100;
|
||||
}
|
||||
}
|
||||
int spellDamage = ctx.effectValue;
|
||||
|
||||
int chainLightningModifier = 0;
|
||||
for(auto & attackedCre : ctx.attackedCres)
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
if(spellDamage)
|
||||
bsa.damageAmount = spellDamage >> chainLightningModifier;
|
||||
if(spellDamage != 0)
|
||||
bsa.damageAmount = owner->adjustRawDamage(parameters.casterHero, attackedCre, spellDamage) >> chainLightningModifier;
|
||||
else
|
||||
bsa.damageAmount = owner->calculateDamage(parameters.casterHero, attackedCre, ctx.effectLevel, parameters.usedSpellPower) >> chainLightningModifier;
|
||||
bsa.damageAmount = owner->calculateDamage(parameters.casterHero, attackedCre, ctx.effectLevel, ctx.effectPower) >> chainLightningModifier;
|
||||
|
||||
ctx.sc.dmgToDisplay += bsa.damageAmount;
|
||||
|
||||
@ -563,14 +575,9 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
||||
|
||||
if(owner->hasEffects())
|
||||
{
|
||||
int stackSpellPower = 0;
|
||||
if(parameters.casterStack && parameters.mode != ECastingMode::MAGIC_MIRROR)
|
||||
{
|
||||
stackSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
|
||||
}
|
||||
SetStackEffect sse;
|
||||
//get default spell duration (spell power with bonuses for heroes)
|
||||
int duration = calculateDuration(parameters.casterHero, stackSpellPower ? stackSpellPower : parameters.usedSpellPower);
|
||||
int duration = calculateDuration(parameters.casterHero, ctx.enchantPower);
|
||||
//generate actual stack bonuses
|
||||
{
|
||||
int maxDuration = 0;
|
||||
|
@ -19,11 +19,23 @@ class StacksInjured;
|
||||
struct SpellCastContext
|
||||
{
|
||||
SpellCastContext(std::vector<const CStack *> & attackedCres, BattleSpellCast & sc, StacksInjured & si):
|
||||
attackedCres(attackedCres), sc(sc), si(si){};
|
||||
attackedCres(attackedCres), sc(sc), si(si),
|
||||
effectLevel(0),effectPower(0),enchantPower(0),effectValue(0)
|
||||
{
|
||||
};
|
||||
std::vector<const CStack *> & attackedCres;
|
||||
BattleSpellCast & sc;
|
||||
StacksInjured & si;
|
||||
StacksInjured & si;
|
||||
///spell school level to use for effects
|
||||
int effectLevel;
|
||||
///actual spell-power affecting effect values
|
||||
int effectPower;
|
||||
///actual spell-power affecting effect duration
|
||||
int enchantPower;
|
||||
///for FairyDragon-like casting, if !=0 ignores effectPower
|
||||
int effectValue;
|
||||
///stack or hero
|
||||
const ISpellCaster * caster;
|
||||
};
|
||||
|
||||
enum class ESpellCastResult
|
||||
|
@ -133,47 +133,10 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
|
||||
|
||||
ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const
|
||||
{
|
||||
ui32 ret = 0; //value to return
|
||||
|
||||
//check if spell really does damage - if not, return 0
|
||||
if(!isDamageSpell())
|
||||
return 0;
|
||||
|
||||
ret = usedSpellPower * power;
|
||||
ret += getPower(spellSchoolLevel);
|
||||
|
||||
//affected creature-specific part
|
||||
if(nullptr != affectedCreature)
|
||||
{
|
||||
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
||||
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
||||
{
|
||||
if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id))
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id);
|
||||
ret /= 100;
|
||||
stop = true;//only bonus from one school is used
|
||||
}
|
||||
});
|
||||
|
||||
//general spell dmg reduction
|
||||
if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1))
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
|
||||
ret /= 100;
|
||||
}
|
||||
|
||||
//dmg increasing
|
||||
if(affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, id))
|
||||
{
|
||||
ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, id.toEnum());
|
||||
ret /= 100;
|
||||
}
|
||||
}
|
||||
|
||||
if(nullptr != caster) //todo: make sure that caster always present
|
||||
ret = caster->getSpellBonus(this, ret, affectedCreature);
|
||||
return ret;
|
||||
return adjustRawDamage(caster, affectedCreature, calculateRawEffectValue(spellSchoolLevel, usedSpellPower));
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CSpell::canBeCasted(const CBattleInfoCallback * cb, PlayerColor player) const
|
||||
@ -395,6 +358,49 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneAt(const CBattleInfoCallbac
|
||||
return ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
int CSpell::adjustRawDamage(const ISpellCaster * caster, const CStack * affectedCreature, int rawDamage) const
|
||||
{
|
||||
int ret = rawDamage;
|
||||
//affected creature-specific part
|
||||
if(nullptr != affectedCreature)
|
||||
{
|
||||
//applying protections - when spell has more then one elements, only one protection should be applied (I think)
|
||||
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
||||
{
|
||||
if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id))
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, (ui8)cnf.id);
|
||||
ret /= 100;
|
||||
stop = true;//only bonus from one school is used
|
||||
}
|
||||
});
|
||||
|
||||
//general spell dmg reduction
|
||||
if(affectedCreature->hasBonusOfType(Bonus::SPELL_DAMAGE_REDUCTION, -1))
|
||||
{
|
||||
ret *= affectedCreature->valOfBonuses(Bonus::SPELL_DAMAGE_REDUCTION, -1);
|
||||
ret /= 100;
|
||||
}
|
||||
|
||||
//dmg increasing
|
||||
if(affectedCreature->hasBonusOfType(Bonus::MORE_DAMAGE_FROM_SPELL, id))
|
||||
{
|
||||
ret *= 100 + affectedCreature->valOfBonuses(Bonus::MORE_DAMAGE_FROM_SPELL, id.toEnum());
|
||||
ret /= 100;
|
||||
}
|
||||
}
|
||||
|
||||
if(nullptr != caster) //todo: make sure that caster always present
|
||||
ret = caster->getSpellBonus(this, ret, affectedCreature);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int CSpell::calculateRawEffectValue(int effectLevel, int effectPower) const
|
||||
{
|
||||
return effectPower * power + getPower(effectLevel);
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CSpell::isImmuneBy(const IBonusBearer* obj) const
|
||||
{
|
||||
//todo: use new bonus API
|
||||
|
@ -218,9 +218,6 @@ public:
|
||||
///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * caster, ECastingMode::ECastingMode mode, BattleHex destination) const;
|
||||
|
||||
//internal, for use only by Mechanics classes
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
|
||||
|
||||
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
|
||||
ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
|
||||
|
||||
@ -292,11 +289,17 @@ public:
|
||||
///implementation of BattleSpellCast applying
|
||||
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
|
||||
|
||||
public:
|
||||
///Client logic.
|
||||
|
||||
public://Client logic.
|
||||
void prepareBattleLog(const CBattleInfoCallback * cb, const BattleSpellCast * packet, std::vector<std::string> & logLines) const;
|
||||
|
||||
public://internal, for use only by Mechanics classes
|
||||
///applies caster`s secondary skills and affectedCreature`s to raw damage
|
||||
int adjustRawDamage(const ISpellCaster * caster, const CStack * affectedCreature, int rawDamage) const;
|
||||
///returns raw damage or healed HP
|
||||
int calculateRawEffectValue(int effectLevel, int effectPower) const;
|
||||
///generic immunity calculation
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
|
||||
|
||||
private:
|
||||
void setIsOffensive(const bool val);
|
||||
void setIsRising(const bool val);
|
||||
|
@ -19,14 +19,14 @@
|
||||
void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//calculating dmg to display
|
||||
ctx.sc.dmgToDisplay = parameters.usedSpellPower;
|
||||
ctx.sc.dmgToDisplay = ctx.effectPower;
|
||||
|
||||
for(auto & attackedCre : ctx.attackedCres) //no immunities
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= BattleStackAttacked::SPELL_EFFECT;
|
||||
bsa.spellID = owner->id;
|
||||
bsa.damageAmount = parameters.usedSpellPower; //damage times the number of attackers
|
||||
bsa.damageAmount = ctx.effectPower; //damage times the number of attackers
|
||||
bsa.stackAttacked = (attackedCre)->ID;
|
||||
bsa.attackerID = -1;
|
||||
(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
|
||||
@ -38,7 +38,7 @@ void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment *
|
||||
void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//calculating dmg to display
|
||||
ctx.sc.dmgToDisplay = parameters.usedSpellPower;
|
||||
ctx.sc.dmgToDisplay = ctx.effectPower;
|
||||
if(!ctx.attackedCres.empty())
|
||||
vstd::amin(ctx.sc.dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack
|
||||
|
||||
@ -47,7 +47,7 @@ void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, B
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= BattleStackAttacked::SPELL_EFFECT;
|
||||
bsa.spellID = owner->id;
|
||||
bsa.damageAmount = parameters.usedSpellPower * (attackedCre)->valOfBonuses(Bonus::STACK_HEALTH);
|
||||
bsa.damageAmount = ctx.effectPower * (attackedCre)->valOfBonuses(Bonus::STACK_HEALTH);//todo: move here all DeathStare calculation
|
||||
bsa.stackAttacked = (attackedCre)->ID;
|
||||
bsa.attackerID = -1;
|
||||
(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
|
||||
|
Loading…
Reference in New Issue
Block a user