Give each reflect and spell absorption effect a chance to apply

pull/3210/head
Evil Eye 3 years ago
parent 3006c496fc
commit 4ec927829f

@ -45,6 +45,8 @@
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
Bug #6197: Infinite Casting Loop
Bug #6253: Multiple instances of Reflect stack additively
Bug #6255: Reflect is different from vanilla
Bug #6258: Barter menu glitches out when modifying prices
Bug #6273: Respawning NPCs rotation is inconsistent
Bug #6282: Laura craft doesn't follow the player character

@ -94,7 +94,7 @@ add_openmw_dir (mwmechanics
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
character actors objects aistate trading weaponpriority spellpriority weapontype spellutil
spellabsorption spelleffects
spelleffects
)
add_openmw_dir (mwstate

@ -1,5 +1,7 @@
#include "activespells.hpp"
#include <optional>
#include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp>
@ -14,6 +16,8 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwrender/animation.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
@ -96,6 +100,11 @@ namespace MWMechanics
, mType(params.mType), mWorsenings(params.mWorsenings), mNextWorsening({params.mNextWorsening})
{}
ActiveSpells::ActiveSpellParams::ActiveSpellParams(const ActiveSpellParams& params, const MWWorld::Ptr& actor)
: mId(params.mId), mDisplayName(params.mDisplayName), mCasterActorId(actor.getClass().getCreatureStats(actor).getActorId())
, mSlot(params.mSlot), mType(params.mType), mWorsenings(-1)
{}
ESM::ActiveSpells::ActiveSpellParams ActiveSpells::ActiveSpellParams::toEsm() const
{
ESM::ActiveSpells::ActiveSpellParams params;
@ -220,10 +229,19 @@ namespace MWMechanics
{
const auto caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spellIt->mCasterActorId); //Maybe make this search outside active grid?
bool removedSpell = false;
std::optional<ActiveSpellParams> reflected;
for(auto it = spellIt->mEffects.begin(); it != spellIt->mEffects.end();)
{
bool remove = applyMagicEffect(ptr, caster, *spellIt, *it, duration);
if(remove)
auto result = applyMagicEffect(ptr, caster, *spellIt, *it, duration);
if(result == MagicApplicationResult::REFLECTED)
{
if(!reflected)
reflected = {*spellIt, ptr};
auto& reflectedEffect = reflected->mEffects.emplace_back(*it);
reflectedEffect.mFlags = ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
it = spellIt->mEffects.erase(it);
}
else if(result == MagicApplicationResult::REMOVED)
it = spellIt->mEffects.erase(it);
else
++it;
@ -231,6 +249,14 @@ namespace MWMechanics
if(removedSpell)
break;
}
if(reflected)
{
const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find("VFX_Reflect");
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if(animation && !reflectStatic->mModel.empty())
animation->addEffect("meshes\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, std::string());
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell(*reflected);
}
if(removedSpell)
continue;

@ -50,6 +50,8 @@ namespace MWMechanics
ActiveSpellParams(const MWWorld::ConstPtr& item, const ESM::Enchantment* enchantment, int slotIndex, const MWWorld::Ptr& actor);
ActiveSpellParams(const ActiveSpellParams& params, const MWWorld::Ptr& actor);
ESM::ActiveSpells::ActiveSpellParams toEsm() const;
friend class ActiveSpells;

@ -1,82 +0,0 @@
#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
{
float getProbability(const MWMechanics::ActiveSpells& activeSpells)
{
float probability = 0.f;
for(const auto& params : activeSpells)
{
for(const auto& effect : params.getEffects())
{
if(effect.mEffectId == ESM::MagicEffect::SpellAbsorption)
{
if(probability == 0.f)
probability = effect.mMagnitude / 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 - probability;
failProbability *= 1.f - effect.mMagnitude / 100;
probability = 1.f - failProbability;
}
}
}
}
return static_cast<int>(probability * 100);
}
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;
int chance = getProbability(stats.getActiveSpells());
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 = MWMechanics::calcSpellCost(*spell);
}
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;
}
}

@ -1,17 +0,0 @@
#ifndef MWMECHANICS_SPELLABSORPTION_H
#define MWMECHANICS_SPELLABSORPTION_H
#include <string>
namespace MWWorld
{
class Ptr;
}
namespace MWMechanics
{
// Try to absorb a spell based on the magnitude of every Spell Absorption effect source on the target.
bool absorbSpell(const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
}
#endif

@ -22,38 +22,11 @@
#include "actorutil.hpp"
#include "aifollow.hpp"
#include "creaturestats.hpp"
#include "spellabsorption.hpp"
#include "spelleffects.hpp"
#include "spellutil.hpp"
#include "summoning.hpp"
#include "weapontype.hpp"
namespace
{
bool reflectEffect(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect,
const MWWorld::Ptr& caster, const MWWorld::Ptr& target, ESM::EffectList& reflectedEffects)
{
if (caster.isEmpty() || caster == target || !target.getClass().isActor())
return false;
bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful;
bool isUnreflectable = magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable;
if (!isHarmful || isUnreflectable)
return false;
float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude();
if (Misc::Rng::roll0to99() >= reflect)
return false;
const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Reflect");
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target);
if (animation && !reflectStatic->mModel.empty())
animation->addEffect("meshes\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, std::string());
reflectedEffects.mList.emplace_back(effect);
return true;
}
}
namespace MWMechanics
{
CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell)
@ -82,7 +55,7 @@ namespace MWMechanics
}
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
const ESM::EffectList &effects, ESM::RangeType range, bool exploded)
{
const bool targetIsActor = !target.isEmpty() && target.getClass().isActor();
if (targetIsActor)
@ -123,7 +96,6 @@ namespace MWMechanics
}
}
ESM::EffectList reflectedEffects;
ActiveSpells::ActiveSpellParams params(*this, caster);
bool castByPlayer = (!caster.isEmpty() && caster == getPlayer());
@ -136,9 +108,6 @@ namespace MWMechanics
// throughout the iteration of this spell's
// effects, we display a "can't re-cast" message
// Try absorbing the spell. Some handling must still happen for absorbed effects.
bool absorbed = absorbSpell(mId, caster, target);
int currentEffectIndex = 0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
!target.isEmpty() && effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex)
@ -167,19 +136,6 @@ namespace MWMechanics
&& (caster.isEmpty() || !caster.getClass().isActor()))
continue;
// Notify the target actor they've been hit
bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful;
if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful)
target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true);
// Avoid proceeding further for absorbed spells.
if (absorbed)
continue;
// Reflect harmful effects
if (!reflected && reflectEffect(*effectIt, magicEffect, caster, target, reflectedEffects))
continue;
ActiveSpells::ActiveEffect effect;
effect.mEffectId = effectIt->mEffectID;
effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
@ -189,13 +145,8 @@ namespace MWMechanics
effect.mTimeLeft = 0.f;
effect.mEffectIndex = currentEffectIndex;
effect.mFlags = ESM::ActiveEffect::Flag_None;
// Avoid applying harmful effects to the player in god mode
if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState() && isHarmful)
{
effect.mMinMagnitude = 0;
effect.mMaxMagnitude = 0;
}
if(mManualSpell)
effect.mFlags |= ESM::ActiveEffect::Flag_Ignore_Reflect;
bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);
effect.mDuration = hasDuration ? static_cast<float>(effectIt->mDuration) : 1.f;
@ -209,14 +160,14 @@ namespace MWMechanics
// add to list of active effects, to apply in next frame
params.getEffects().emplace_back(effect);
bool effectAffectsHealth = isHarmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth;
bool effectAffectsHealth = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth;
if (castByPlayer && target != caster && targetIsActor && effectAffectsHealth)
{
// If player is attempting to cast a harmful spell on or is healing a living target, show the target's HP bar.
MWBase::Environment::get().getWindowManager()->setEnemy(target);
}
if (targetIsActor || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
if (!targetIsActor && magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
{
playEffects(target, *magicEffect);
}
@ -227,9 +178,6 @@ namespace MWMechanics
if (!target.isEmpty())
{
if (!reflectedEffects.mList.empty())
inflict(caster, target, reflectedEffects, range, true, exploded);
if (!params.getEffects().empty())
{
if(targetIsActor)
@ -237,6 +185,7 @@ namespace MWMechanics
else
{
// Apply effects instantly. We can ignore effect deletion since the entire params object gets deleted afterwards anyway
// and we can ignore reflection since non-actors cannot reflect spells
for(auto& effect : params.getEffects())
applyMagicEffect(target, caster, params, effect, 0.f);
}

@ -62,7 +62,7 @@ namespace MWMechanics
/// @note \a target can be any type of object, not just actors.
/// @note \a caster can be any type of object, or even an empty object.
void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false);
const ESM::EffectList& effects, ESM::RangeType range, bool exploded=false);
};
void playEffects(const MWWorld::Ptr& target, const ESM::MagicEffect& magicEffect, bool playNonLooping = true);

@ -16,6 +16,7 @@
#include "../mwmechanics/aifollow.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/spellresistance.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "../mwmechanics/summoning.hpp"
#include "../mwrender/animation.hpp"
@ -261,6 +262,97 @@ namespace
return false;
}
void absorbSpell(const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
{
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 = MWMechanics::calcSpellCost(*spell);
}
else
{
const ESM::Enchantment* enchantment = esmStore.get<ESM::Enchantment>().search(spellId);
if (enchantment)
spellCost = MWMechanics::getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), caster);
}
// Magicka is increased by the cost of the spell
auto& stats = target.getClass().getCreatureStats(target);
auto magicka = stats.getMagicka();
magicka.setCurrent(magicka.getCurrent() + spellCost);
stats.setMagicka(magicka);
}
MWMechanics::MagicApplicationResult applyProtections(const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
const MWMechanics::ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, const ESM::MagicEffect* magicEffect)
{
auto& stats = target.getClass().getCreatureStats(target);
auto& magnitudes = stats.getMagicEffects();
// Apply reflect and spell absorption
if(target != caster && spellParams.getType() != ESM::ActiveSpells::Type_Enchantment && spellParams.getType() != ESM::ActiveSpells::Type_Permanent)
{
bool canReflect = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable) &&
!(effect.mFlags & ESM::ActiveEffect::Flag_Ignore_Reflect) && magnitudes.get(ESM::MagicEffect::Reflect).getMagnitude() > 0.f;
bool canAbsorb = !(effect.mFlags & ESM::ActiveEffect::Flag_Ignore_SpellAbsorption) && magnitudes.get(ESM::MagicEffect::SpellAbsorption).getMagnitude() > 0.f;
if(canReflect || canAbsorb)
{
for(const auto& activeParam : stats.getActiveSpells())
{
for(const auto& activeEffect : activeParam.getEffects())
{
if(!(activeEffect.mFlags & ESM::ActiveEffect::Flag_Applied))
continue;
if(activeEffect.mEffectId == ESM::MagicEffect::Reflect)
{
if(canReflect && Misc::Rng::roll0to99() < activeEffect.mMagnitude)
{
return MWMechanics::MagicApplicationResult::REFLECTED;
}
}
else if(activeEffect.mEffectId == ESM::MagicEffect::SpellAbsorption)
{
if(canAbsorb && Misc::Rng::roll0to99() < activeEffect.mMagnitude)
{
absorbSpell(spellParams.getId(), caster, target);
return MWMechanics::MagicApplicationResult::REMOVED;
}
}
}
}
}
}
// Notify the target actor they've been hit
bool isHarmful = magicEffect->mData.mFlags & ESM::MagicEffect::Harmful;
if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful)
target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true);
// Apply resistances
if(!(effect.mFlags & ESM::ActiveEffect::Flag_Ignore_Resistances))
{
const ESM::Spell* spell = nullptr;
if(spellParams.getType() == ESM::ActiveSpells::Type_Temporary)
spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellParams.getId());
float magnitudeMult = MWMechanics::getEffectMultiplier(effect.mEffectId, target, caster, spell, &magnitudes);
if (magnitudeMult == 0)
{
// Fully resisted, show message
if (target == MWMechanics::getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}");
else if (caster == MWMechanics::getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
return MWMechanics::MagicApplicationResult::REMOVED;
}
effect.mMinMagnitude *= magnitudeMult;
effect.mMaxMagnitude *= magnitudeMult;
}
return MWMechanics::MagicApplicationResult::APPLIED;
}
static const std::map<int, std::string> sBoundItemsMap{
{ESM::MagicEffect::BoundBattleAxe, "sMagicBoundBattleAxeID"},
{ESM::MagicEffect::BoundBoots, "sMagicBoundBootsID"},
@ -682,7 +774,7 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
}
}
bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt)
MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt)
{
const auto world = MWBase::Environment::get().getWorld();
bool invalid = false;
@ -698,13 +790,13 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
}
if(target == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
return false;
return MagicApplicationResult::APPLIED;
}
else if(effect.mEffectId == ESM::MagicEffect::Levitate && !world->isLevitationEnabled())
{
if(target == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox ("#{sLevitateDisabled}");
return true;
return MagicApplicationResult::REMOVED;
}
const auto* magicEffect = world->getStore().get<ESM::MagicEffect>().find(effect.mEffectId);
if(effect.mFlags & ESM::ActiveEffect::Flag_Applied)
@ -712,10 +804,10 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
if(magicEffect->mData.mFlags & ESM::MagicEffect::Flags::AppliedOnce)
{
effect.mTimeLeft -= dt;
return false;
return MagicApplicationResult::APPLIED;
}
else if(!dt)
return false;
return MagicApplicationResult::APPLIED;
}
if(effect.mEffectId == ESM::MagicEffect::Lock)
{
@ -771,28 +863,19 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
}
else
{
auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects();
if(spellParams.getType() != ESM::ActiveSpells::Type_Ability && !(effect.mFlags & (ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Ignore_Resistances)))
auto& stats = target.getClass().getCreatureStats(target);
auto& magnitudes = stats.getMagicEffects();
if(spellParams.getType() != ESM::ActiveSpells::Type_Ability && !(effect.mFlags & ESM::ActiveEffect::Flag_Applied))
{
const ESM::Spell* spell = nullptr;
if(spellParams.getType() == ESM::ActiveSpells::Type_Temporary)
spell = world->getStore().get<ESM::Spell>().search(spellParams.getId());
float magnitudeMult = getEffectMultiplier(effect.mEffectId, target, caster, spell, &magnitudes);
if (magnitudeMult == 0)
{
// Fully resisted, show message
if (target == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}");
else if (caster == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
return true;
}
effect.mMinMagnitude *= magnitudeMult;
effect.mMaxMagnitude *= magnitudeMult;
MagicApplicationResult result = applyProtections(target, caster, spellParams, effect, magicEffect);
if(result != MagicApplicationResult::APPLIED)
return result;
}
float oldMagnitude = 0.f;
if(effect.mFlags & ESM::ActiveEffect::Flag_Applied)
oldMagnitude = effect.mMagnitude;
else if(spellParams.getType() == ESM::ActiveSpells::Type_Consumable || spellParams.getType() == ESM::ActiveSpells::Type_Temporary)
playEffects(target, *magicEffect);
float magnitude = roll(effect);
//Note that there's an early out for Flag_Applied AppliedOnce effects so we don't have to exclude them here
effect.mMagnitude = magnitude;
@ -810,7 +893,7 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
effect.mMagnitude = oldMagnitude;
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
effect.mTimeLeft -= dt;
return false;
return MagicApplicationResult::APPLIED;
}
}
if(effect.mEffectId == ESM::MagicEffect::Corprus)
@ -835,7 +918,7 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
if(recalculateMagicka)
target.getClass().getCreatureStats(target).recalculateMagicka();
return false;
return MagicApplicationResult::APPLIED;
}
void removeMagicEffect(const MWWorld::Ptr& target, ActiveSpells::ActiveSpellParams& spellParams, const ESM::ActiveEffect& effect)

@ -10,8 +10,13 @@
namespace MWMechanics
{
enum class MagicApplicationResult
{
APPLIED, REMOVED, REFLECTED
};
// Applies a tick of a single effect. Returns true if the effect should be removed immediately
bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt);
MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt);
// Undoes permanent effects created by ESM::MagicEffect::AppliedOnce
void onMagicEffectRemoved(const MWWorld::Ptr& target, ActiveSpells::ActiveSpellParams& spell, const ESM::ActiveEffect& effect);

@ -104,8 +104,8 @@ void EsmLoader::load(const boost::filesystem::path& filepath, int& index)
effect.mMagnitude = magnitude;
effect.mMinMagnitude = magnitude;
effect.mMaxMagnitude = magnitude;
// Prevent recalculation of resistances
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances;
// Prevent recalculation of resistances and don't reflect or absorb the effect
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances | ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
}
else
{
@ -172,8 +172,8 @@ void EsmLoader::load(const boost::filesystem::path& filepath, int& index)
effect.mDuration = -1;
effect.mTimeLeft = -1;
effect.mEffectIndex = static_cast<int>(effectIndex);
// Prevent recalculation of resistances
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances;
// Prevent recalculation of resistances and don't reflect or absorb the effect
effect.mFlags = ESM::ActiveEffect::Flag_Ignore_Resistances | ESM::ActiveEffect::Flag_Ignore_Reflect | ESM::ActiveEffect::Flag_Ignore_SpellAbsorption;
params.mEffects.emplace_back(effect);
}
auto [begin, end] = equippedItems.equal_range(id);

@ -546,7 +546,7 @@ namespace MWWorld
cast.mId = magicBoltState.mSpellId;
cast.mSourceName = magicBoltState.mSourceName;
cast.mSlot = magicBoltState.mSlot;
cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, true);
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, target, ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName, false, magicBoltState.mSlot);
magicBoltState.mToDelete = true;

@ -3791,7 +3791,7 @@ namespace MWWorld
cast.mSlot = slot;
ESM::EffectList effectsToApply;
effectsToApply.mList = applyPair.second;
cast.inflict(applyPair.first, caster, effectsToApply, rangeType, false, true);
cast.inflict(applyPair.first, caster, effectsToApply, rangeType, true);
}
}

@ -22,7 +22,9 @@ namespace ESM
Flag_None = 0,
Flag_Applied = 1 << 0,
Flag_Remove = 1 << 1,
Flag_Ignore_Resistances = 1 << 2
Flag_Ignore_Resistances = 1 << 2,
Flag_Ignore_Reflect = 1 << 3,
Flag_Ignore_SpellAbsorption = 1 << 4
};
int mEffectId;

Loading…
Cancel
Save