summaryrefslogtreecommitdiff
path: root/apps/openmw/mwmechanics/spellabsorption.cpp
blob: bab290fdab521bf4784508c11eac59bac506b943 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "spellabsorption.hpp"

#include <components/misc/rng.hpp>

#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"

#include "../mwrender/animation.hpp"

#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"

#include "creaturestats.hpp"
#include "spellutil.hpp"

namespace MWMechanics
{

    class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor
    {
    public:
        float mProbability{0.f};

        GetAbsorptionProbability() = default;

        void visit (MWMechanics::EffectKey key, int /*effectIndex*/,
                            const std::string& /*sourceName*/, const std::string& /*sourceId*/, int /*casterActorId*/,
                            float magnitude, float /*remainingTime*/, float /*totalTime*/) override
        {
            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;
                }
            }
        }
    };

    bool absorbSpell (const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
    {
        if (spellId.empty() || target.isEmpty() || caster == target || !target.getClass().isActor())
            return false;

        CreatureStats& stats = target.getClass().getCreatureStats(target);
        if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() <= 0.f)
            return false;

        GetAbsorptionProbability check;
        stats.getActiveSpells().visitEffectSources(check);
        stats.getSpells().visitEffectSources(check);
        if (target.getClass().hasInventoryStore(target))
            target.getClass().getInventoryStore(target).visitEffectSources(check);

        int chance = check.mProbability * 100;
        if (Misc::Rng::roll0to99() >= chance)
            return false;

        const auto& esmStore = MWBase::Environment::get().getWorld()->getStore();
        const ESM::Static* absorbStatic = esmStore.get<ESM::Static>().find("VFX_Absorb");
        MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);
        if (animation && !absorbStatic->mModel.empty())
            animation->addEffect( "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, std::string());
        const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(spellId);
        int spellCost = 0;
        if (spell)
        {
            spellCost = spell->mData.mCost;
        }
        else
        {
            const ESM::Enchantment* enchantment = esmStore.get<ESM::Enchantment>().search(spellId);
            if (enchantment)
                spellCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), caster);
        }

        // Magicka is increased by the cost of the spell
        DynamicStat<float> magicka = stats.getMagicka();
        magicka.setCurrent(magicka.getCurrent() + spellCost);
        stats.setMagicka(magicka);
        return true;
    }

}