forked from mirror/openmw-tes3mp
Use the formula from the wiki for spell success.
This commit is contained in:
parent
4083c55848
commit
da85f3e575
2 changed files with 67 additions and 67 deletions
|
@ -1,6 +1,8 @@
|
|||
#ifndef MWMECHANICS_SPELLSUCCESS_H
|
||||
#define MWMECHANICS_SPELLSUCCESS_H
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
|
@ -27,92 +29,91 @@ namespace MWMechanics
|
|||
return schoolSkillMap[school];
|
||||
}
|
||||
|
||||
inline int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor)
|
||||
{
|
||||
NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
|
||||
// determine the spell's school
|
||||
// this is always the school where the player's respective skill is the least advanced
|
||||
// out of all the magic effects' schools
|
||||
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;
|
||||
int school = -1;
|
||||
int skillLevel = -1;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.begin();
|
||||
it != effects.end(); ++it)
|
||||
{
|
||||
const ESM::MagicEffect* effect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(it->mEffectID);
|
||||
int _school = effect->mData.mSchool;
|
||||
int _skillLevel = stats.getSkill (spellSchoolToSkill(_school)).getModified();
|
||||
|
||||
if (school == -1)
|
||||
{
|
||||
school = _school;
|
||||
skillLevel = _skillLevel;
|
||||
}
|
||||
else if (_skillLevel < skillLevel)
|
||||
{
|
||||
school = _school;
|
||||
skillLevel = _skillLevel;
|
||||
}
|
||||
}
|
||||
|
||||
return school;
|
||||
}
|
||||
|
||||
inline int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor)
|
||||
{
|
||||
const ESM::Spell* spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
||||
return getSpellSchool(spell, actor);
|
||||
}
|
||||
|
||||
|
||||
// UESP wiki / Morrowind/Spells:
|
||||
// Chance of success is (Spell's skill * 2 + Willpower / 5 + Luck / 10 - Spell cost - Sound magnitude) * (Current fatigue + Maximum Fatigue * 1.5) / Maximum fatigue * 2
|
||||
/**
|
||||
* @param spell spell to cast
|
||||
* @param actor calculate spell success chance for this actor (depends on actor's skills)
|
||||
* @param effectiveSchool the spell's effective school (relevant for skill progress) will be written here
|
||||
* @attention actor has to be an NPC and not a creature!
|
||||
* @return success chance from 0 to 100 (in percent)
|
||||
*/
|
||||
inline float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor)
|
||||
inline float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL)
|
||||
{
|
||||
if (spell->mData.mFlags & ESM::Spell::F_Always // spells with this flag always succeed (usually birthsign spells)
|
||||
|| spell->mData.mType == ESM::Spell::ST_Power) // powers always succeed, but can be cast only once per day
|
||||
return 100.0;
|
||||
|
||||
if (spell->mEffects.mList.size() == 0)
|
||||
return 0.0;
|
||||
|
||||
NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
|
||||
|
||||
int skillLevel = stats.getSkill (spellSchoolToSkill(getSpellSchool(spell, actor))).getModified();
|
||||
if (creatureStats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude)
|
||||
return 0;
|
||||
|
||||
// Sound magic effect (reduces spell casting chance)
|
||||
int soundMagnitude = creatureStats.getMagicEffects().get (MWMechanics::EffectKey (48)).mMagnitude;
|
||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
return 100;
|
||||
|
||||
int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
int luck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
int currentFatigue = creatureStats.getFatigue().getCurrent();
|
||||
int maxFatigue = creatureStats.getFatigue().getModified();
|
||||
int spellCost = spell->mData.mCost;
|
||||
if (spell->mData.mFlags & ESM::Spell::F_Always)
|
||||
return 100;
|
||||
|
||||
// There we go, all needed variables are there, lets go
|
||||
float chance = (float(skillLevel * 2) + float(willpower)/5.0 + float(luck)/ 10.0 - spellCost - soundMagnitude) * (float(currentFatigue + maxFatigue * 1.5)) / float(maxFatigue * 2.0);
|
||||
float y = FLT_MAX;
|
||||
float lowestSkill = 0;
|
||||
|
||||
chance = std::max(0.0f, std::min(100.0f, chance)); // clamp to 0 .. 100
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it)
|
||||
{
|
||||
float x = it->mDuration;
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(
|
||||
it->mEffectID);
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
|
||||
x = std::max(1.f, x);
|
||||
x *= 0.1 * magicEffect->mData.mBaseCost;
|
||||
x *= 0.5 * (it->mMagnMin + it->mMagnMax);
|
||||
x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
|
||||
if (it->mRange == ESM::RT_Target)
|
||||
x *= 1.5;
|
||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
"fEffectCostMult")->getFloat();
|
||||
x *= fEffectCostMult;
|
||||
|
||||
return chance;
|
||||
float s = 2 * stats.getSkill(spellSchoolToSkill(magicEffect->mData.mSchool)).getModified();
|
||||
if (s - x < y)
|
||||
{
|
||||
y = s - x;
|
||||
if (effectiveSchool)
|
||||
*effectiveSchool = magicEffect->mData.mSchool;
|
||||
lowestSkill = s;
|
||||
}
|
||||
}
|
||||
|
||||
inline float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor)
|
||||
|
||||
int castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).mMagnitude;
|
||||
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
|
||||
float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * stats.getFatigueTerm();
|
||||
if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player")
|
||||
castChance = 100;
|
||||
|
||||
return std::max(0.f, std::min(100.f, castChance));
|
||||
}
|
||||
|
||||
inline float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL)
|
||||
{
|
||||
const ESM::Spell* spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
||||
return getSpellSuccessChance(spell, actor);
|
||||
return getSpellSuccessChance(spell, actor, effectiveSchool);
|
||||
}
|
||||
|
||||
/// @note this only works for ST_Spell
|
||||
inline int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor)
|
||||
{
|
||||
int school = 0;
|
||||
getSpellSuccessChance(spellId, actor, &school);
|
||||
return school;
|
||||
}
|
||||
|
||||
/// @note this only works for ST_Spell
|
||||
inline int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor)
|
||||
{
|
||||
int school = 0;
|
||||
getSpellSuccessChance(spell, actor, &school);
|
||||
return school;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2105,8 +2105,7 @@ namespace MWWorld
|
|||
return;
|
||||
}
|
||||
|
||||
// Note: F_Always spells don't contribute to skill progress
|
||||
if (actor == getPlayer().getPlayer() && !(spell->mData.mFlags & ESM::Spell::F_Always))
|
||||
if (actor == getPlayer().getPlayer() && spell->mData.mType == ESM::Spell::ST_Spell)
|
||||
actor.getClass().skillUsageSucceeded(actor,
|
||||
MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(selectedSpell, actor)), 0);
|
||||
|
||||
|
|
Loading…
Reference in a new issue