diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c0217da..9fa330e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -143,6 +143,7 @@ Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Bug #4678: Crash in ESP parser when SCVR has no variable names + Bug #4684: Spell Absorption is additive Bug #4685: Missing sound causes an exception inside Say command Bug #4689: Default creature soundgen entries are not used Feature #912: Editor: Add missing icons to UniversalId tables diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a91aa2f31..4a74e1647 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -324,6 +324,34 @@ namespace MWMechanics return true; } + class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor + { + public: + float mProbability; + + GetAbsorptionProbability(const MWWorld::Ptr& actor) + : mProbability(0.f){} + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1) + { + if (key.mId == ESM::MagicEffect::SpellAbsorption) + { + if (mProbability == 0.f) + mProbability = magnitude / 100; + else + { + // If there are different sources of SpellAbsorption effect, multiply failing probability for all effects. + // Real absorption probability will be the (1 - total fail chance) in this case. + float failProbability = 1.f - mProbability; + failProbability *= 1.f - magnitude / 100; + mProbability = 1.f - failProbability; + } + } + } + }; + CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell) : mCaster(caster) , mTarget(target) @@ -444,12 +472,18 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->setEnemy(target); // Try absorbing if it's a spell - // NOTE: Vanilla does this once per spell absorption effect source instead of adding the % from all sources together, not sure - // if that is worth replicating. + // NOTE: Vanilla does this once per spell absorption effect source instead of adding the % from all sources together, so use the same approach here bool absorbed = false; if (spell && caster != target && target.getClass().isActor()) { - float absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude(); + GetAbsorptionProbability check(target); + MWMechanics::CreatureStats& stats = target.getClass().getCreatureStats(target); + stats.getActiveSpells().visitEffectSources(check); + stats.getSpells().visitEffectSources(check); + if (target.getClass().hasInventoryStore(target)) + target.getClass().getInventoryStore(target).visitEffectSources(check); + + int absorb = check.mProbability * 100; absorbed = (Misc::Rng::roll0to99() < absorb); if (absorbed) {