OpenMW
|
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