diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 64d4d86c6..0bb9184d2 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -440,22 +440,11 @@ namespace MWGui for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { - float x = 0.5f * (it->mMagnMin + it->mMagnMax); + const ESM::ENAMstruct& effect = *it; - const ESM::MagicEffect* effect = - store.get().find(it->mEffectID); + y += std::max(1.f, MWMechanics::calcEffectCost(effect)); - x *= 0.1f * effect->mData.mBaseCost; - x *= 1 + it->mDuration; - x += 0.05f * std::max(1, it->mArea) * effect->mData.mBaseCost; - - float fEffectCostMult = - store.get().find("fEffectCostMult")->getFloat(); - - y += x * fEffectCostMult; - y = std::max(1.f,y); - - if (it->mRange == ESM::RT_Target) + if (effect.mRange == ESM::RT_Target) y *= 1.5; } @@ -475,8 +464,10 @@ namespace MWGui mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); - float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWMechanics::getPlayer()); - mSuccessChance->setCaption(MyGUI::utility::toString(int(chance))); + float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), NULL); + + int intChance = std::min(100, int(chance)); + mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); } // ------------------------------------------------------------------------------------------------ diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 3c20179a4..1408dce6b 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -593,16 +593,7 @@ namespace MWMechanics } } - rating *= ((effect.mMagnMin + effect.mMagnMax) * (effect.mDuration > 0 ? effect.mDuration : 1) + effect.mArea); - rating *= magicEffect->mData.mBaseCost; - - if (effect.mRange == ESM::RT_Target) - rating *= 1.5f; - - static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fEffectCostMult")->getFloat(); - - rating *= fEffectCostMult * 0.05; + rating *= calcEffectCost(effect); // Currently treating all "on target" or "on touch" effects to target the enemy actor. // Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors. @@ -619,6 +610,9 @@ namespace MWMechanics for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { rating += rateEffect(*it, actor, enemy); + + if (it->mRange == ESM::RT_Target) + rating *= 1.5f; } return rating; } diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index af814edb0..f655a68b4 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -1,4 +1,5 @@ #include "autocalcspell.hpp" +#include "spellcasting.hpp" #include #include @@ -255,27 +256,39 @@ namespace MWMechanics void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm) { + // Morrowind for some reason uses a formula slightly different from magicka cost calculation float minChance = std::numeric_limits::max(); const ESM::EffectList& effects = spell->mEffects; for (std::vector::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it) { const ESM::ENAMstruct& effect = *it; - float x = static_cast(effect.mDuration); - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) - x = std::max(1.f, x); - x *= 0.1f * magicEffect->mData.mBaseCost; - x *= 0.5f * (effect.mMagnMin + effect.mMagnMax); - x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost; - if (effect.mRange == ESM::RT_Target) - x *= 1.5f; + int minMagn = 1; + int maxMagn = 1; + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + minMagn = effect.mMagnMin; + maxMagn = effect.mMagnMax; + } - static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEffectCostMult")->getFloat(); + int duration = 0; + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + duration = effect.mDuration; + + static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() + .get().find("fEffectCostMult")->getFloat(); + + float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); + x *= 0.1 * magicEffect->mData.mBaseCost; + x *= 1 + duration; + x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost; x *= fEffectCostMult; + if (effect.mRange == ESM::RT_Target) + x *= 1.5f; + float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)]; if (s - x < minChance) { diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 5cfaccb84..7fde66ebe 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -44,8 +44,36 @@ namespace MWMechanics return schoolSkillMap[school]; } - float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap) + float calcEffectCost(const ESM::ENAMstruct& effect) + { + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); + + int minMagn = 1; + int maxMagn = 1; + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + minMagn = effect.mMagnMin; + maxMagn = effect.mMagnMax; + } + + int duration = 0; + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + duration = effect.mDuration; + + static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() + .get().find("fEffectCostMult")->getFloat(); + + float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); + x *= 0.1 * magicEffect->mData.mBaseCost; + x *= 1 + duration; + x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost; + + return x * fEffectCostMult; + } + + float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool) { + // Morrowind for some reason uses a formula slightly different from magicka cost calculation float y = std::numeric_limits::max(); float lowestSkill = 0; @@ -54,8 +82,10 @@ namespace MWMechanics float x = static_cast(it->mDuration); const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( it->mEffectID); + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) x = std::max(1.f, x); + x *= 0.1f * magicEffect->mData.mBaseCost; x *= 0.5f * (it->mMagnMin + it->mMagnMax); x *= it->mArea * 0.05f * magicEffect->mData.mBaseCost; @@ -75,6 +105,18 @@ namespace MWMechanics } } + CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + + float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); + + return castChance; + } + + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap) + { bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); CreatureStats& stats = actor.getClass().getCreatureStats(actor); @@ -98,10 +140,8 @@ namespace MWMechanics float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude(); - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - - float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2f * actorWillpower + 0.1f * actorLuck) * stats.getFatigueTerm(); + float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus; + castChance *= stats.getFatigueTerm(); if (!cap) return std::max(0.f, castChance); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 8e48681b6..9991c583d 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -1,6 +1,7 @@ #ifndef MWMECHANICS_SPELLSUCCESS_H #define MWMECHANICS_SPELLSUCCESS_H +#include #include #include "../mwworld/ptr.hpp" @@ -21,6 +22,8 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); + float calcEffectCost(const ESM::ENAMstruct& effect); + bool isSummoningEffect(int effectId); /** @@ -62,6 +65,7 @@ namespace MWMechanics bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor); + float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool); /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed /// @return Was the effect a tickable effect with a magnitude?