OpenMW
apps/openmw/mwmechanics/spellcasting.hpp
Go to the documentation of this file.
00001 #ifndef MWMECHANICS_SPELLSUCCESS_H
00002 #define MWMECHANICS_SPELLSUCCESS_H
00003 
00004 #include <cfloat>
00005 
00006 #include "../mwbase/world.hpp"
00007 #include "../mwbase/environment.hpp"
00008 
00009 #include "../mwworld/ptr.hpp"
00010 #include "../mwworld/class.hpp"
00011 #include "../mwmechanics/creaturestats.hpp"
00012 
00013 #include "../mwworld/esmstore.hpp"
00014 
00015 #include "npcstats.hpp"
00016 
00017 namespace MWMechanics
00018 {
00019     inline int spellSchoolToSkill(int school)
00020     {
00021         std::map<int, int> schoolSkillMap; // maps spell school to skill id
00022         schoolSkillMap[0] = 11; // alteration
00023         schoolSkillMap[1] = 13; // conjuration
00024         schoolSkillMap[3] = 12; // illusion
00025         schoolSkillMap[2] = 10; // destruction
00026         schoolSkillMap[4] = 14; // mysticism
00027         schoolSkillMap[5] = 15; // restoration
00028         assert(schoolSkillMap.find(school) != schoolSkillMap.end());
00029         return schoolSkillMap[school];
00030     }
00031 
00039     inline float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL)
00040     {
00041         NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
00042         CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
00043 
00044         if (creatureStats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude)
00045             return 0;
00046 
00047         float y = FLT_MAX;
00048         float lowestSkill = 0;
00049 
00050         for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it)
00051         {
00052             float x = it->mDuration;
00053             const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(
00054                         it->mEffectID);
00055             if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
00056                 x = std::max(1.f, x);
00057             x *= 0.1 * magicEffect->mData.mBaseCost;
00058             x *= 0.5 * (it->mMagnMin + it->mMagnMax);
00059             x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
00060             if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget)
00061                 x *= 1.5;
00062             static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
00063                         "fEffectCostMult")->getFloat();
00064             x *= fEffectCostMult;
00065 
00066             float s = 2 * stats.getSkill(spellSchoolToSkill(magicEffect->mData.mSchool)).getModified();
00067             if (s - x < y)
00068             {
00069                 y = s - x;
00070                 if (effectiveSchool)
00071                     *effectiveSchool = magicEffect->mData.mSchool;
00072                 lowestSkill = s;
00073             }
00074         }
00075 
00076         if (spell->mData.mType != ESM::Spell::ST_Spell)
00077             return 100;
00078 
00079         if (spell->mData.mFlags & ESM::Spell::F_Always)
00080             return 100;
00081 
00082         int castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).mMagnitude;
00083 
00084         int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
00085         int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
00086 
00087         float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * stats.getFatigueTerm();
00088         if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player")
00089             castChance = 100;
00090 
00091         return std::max(0.f, std::min(100.f, castChance));
00092     }
00093 
00094     inline float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL)
00095     {
00096         const ESM::Spell* spell =
00097             MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
00098         return getSpellSuccessChance(spell, actor, effectiveSchool);
00099     }
00100 
00101     inline int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor)
00102     {
00103         int school = 0;
00104         getSpellSuccessChance(spellId, actor, &school);
00105         return school;
00106     }
00107 
00108     inline int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor)
00109     {
00110         int school = 0;
00111         getSpellSuccessChance(spell, actor, &school);
00112         return school;
00113     }
00114 
00116     inline float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL)
00117     {
00118         const ESM::MagicEffect *magicEffect =
00119             MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
00120             effectId);
00121 
00122         const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
00123 
00124         float resisted = 0;
00125         if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
00126         {
00127 
00128             short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);
00129             short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId);
00130 
00131             float resistance = 0;
00132             if (resistanceEffect != -1)
00133                 resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude;
00134             if (weaknessEffect != -1)
00135                 resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude;
00136 
00137 
00138             float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
00139             float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
00140             float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
00141 
00142             // This makes spells that are easy to cast harder to resist and vice versa
00143             if (spell != NULL && caster.getClass().isActor())
00144             {
00145                 float castChance = getSpellSuccessChance(spell, caster);
00146                 if (castChance > 0)
00147                     x *= 50 / castChance;
00148             }
00149 
00150             float roll = static_cast<float>(std::rand()) / RAND_MAX * 100;
00151             if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
00152                 roll -= resistance;
00153 
00154             if (x <= roll)
00155                 x = 0;
00156             else
00157             {
00158                 if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
00159                     x = 100;
00160                 else
00161                     x = roll / std::min(x, 100.f);
00162             }
00163 
00164             x = std::min(x + resistance, 100.f);
00165 
00166             resisted = x;
00167         }
00168 
00169         return resisted;
00170     }
00171 
00172     inline float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL)
00173     {
00174         float resistance = getEffectResistance(effectId, actor, caster, spell);
00175         if (resistance >= 0)
00176             return 1 - resistance / 100.f;
00177         else
00178             return -(resistance-100) / 100.f;
00179     }
00180 
00181 
00182     class CastSpell
00183     {
00184     private:
00185         MWWorld::Ptr mCaster;
00186         MWWorld::Ptr mTarget;
00187     public:
00188         bool mStack;
00189         std::string mId; // ID of spell, potion, item etc
00190         std::string mSourceName; // Display name for spell, potion, etc
00191 
00192     public:
00193         CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
00194 
00195         bool cast (const ESM::Spell* spell);
00196         bool cast (const MWWorld::Ptr& item);
00197         bool cast (const ESM::Ingredient* ingredient);
00198         bool cast (const ESM::Potion* potion);
00199 
00201         bool cast (const std::string& id);
00202 
00203         void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
00204                       const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false);
00205 
00206         void applyInstantEffect (const MWWorld::Ptr& target, short effectId, float magnitude);
00207     };
00208 
00209 }
00210 
00211 #endif