mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-29 09:32:45 +00:00
Merge branch 'puddle' into 'master'
Give each reflect and spell absorption effect a chance to apply Closes #6255 and #6253 See merge request OpenMW/openmw!1279
This commit is contained in:
commit
4c81518abb
@ -48,6 +48,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);
|
||||
|
@ -542,7 +542,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;
|
||||
|
@ -3805,7 +3805,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…
x
Reference in New Issue
Block a user