diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 54f5c16e65..edfb3eef79 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -597,10 +597,6 @@ namespace MWBase virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) = 0; - virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, - const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id, - const std::string& sourceName, const bool fromProjectile=false, int slot = 0) = 0; - virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; /// @see MWWorld::WeatherManager::isInStorm diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 97530ce293..a786d9723d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -39,6 +39,104 @@ namespace MWMechanics { } + void CastSpell::explodeSpell(const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore, ESM::RangeType rangeType) const + { + const auto world = MWBase::Environment::get().getWorld(); + const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); + std::map > toApply; + int index = -1; + for (const ESM::ENAMstruct& effectInfo : effects.mList) + { + ++index; + const ESM::MagicEffect* effect = world->getStore().get().find(effectInfo.mEffectID); + + if (effectInfo.mRange != rangeType || (effectInfo.mArea <= 0 && !ignore.isEmpty() && ignore.getClass().isActor())) + continue; // Not right range type, or not area effect and hit an actor + + if (mFromProjectile && effectInfo.mArea <= 0) + continue; // Don't play explosion for projectiles with 0-area effects + + if (!mFromProjectile && effectInfo.mRange == ESM::RT_Touch && !ignore.isEmpty() && !ignore.getClass().isActor() && !ignore.getClass().hasToolTip(ignore) + && (caster.isEmpty() || caster.getClass().isActor())) + continue; // Don't play explosion for touch spells on non-activatable objects except when spell is from a projectile enchantment or ExplodeSpell + + // Spawn the explosion orb effect + const ESM::Static* areaStatic; + if (!effect->mArea.empty()) + areaStatic = world->getStore().get().find(effect->mArea); + else + areaStatic = world->getStore().get().find("VFX_DefaultArea"); + + const std::string& texture = effect->mParticle; + + if (effectInfo.mArea <= 0) + { + if (effectInfo.mRange == ESM::RT_Target) + world->spawnEffect(Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel, vfs), texture, mHitPosition, 1.0f); + continue; + } + else + world->spawnEffect(Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel, vfs), texture, mHitPosition, static_cast(effectInfo.mArea * 2)); + + // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playSound3D(mHitPosition, effect->mAreaSound, 1.0f, 1.0f); + else + sndMgr->playSound3D(mHitPosition, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f); + } + // Get the actors in range of the effect + std::vector objects; + static const int unitsPerFoot = ceil(Constants::UnitsPerFoot); + MWBase::Environment::get().getMechanicsManager()->getObjectsInRange(mHitPosition, static_cast(effectInfo.mArea * unitsPerFoot), objects); + for (const MWWorld::Ptr& affected : objects) + { + // Ignore actors without collisions here, otherwise it will be possible to hit actors outside processing range. + if (affected.getClass().isActor() && !world->isActorCollisionEnabled(affected)) + continue; + + auto& list = toApply[affected]; + while (list.size() < static_cast(index)) + { + // Insert dummy effects to preserve indices + auto& dummy = list.emplace_back(effectInfo); + dummy.mRange = ESM::RT_Self; + assert(dummy.mRange != rangeType); + } + list.push_back(effectInfo); + } + } + + // Now apply the appropriate effects to each actor in range + for (auto& applyPair : toApply) + { + MWWorld::Ptr source = caster; + // Vanilla-compatible behaviour of never applying the spell to the caster + // (could be changed by mods later) + if (applyPair.first == caster) + continue; + + if (applyPair.first == ignore) + continue; + + if (source.isEmpty()) + source = applyPair.first; + + MWMechanics::CastSpell cast(source, applyPair.first); + cast.mHitPosition = mHitPosition; + cast.mId = mId; + cast.mSourceName = mSourceName; + cast.mSlot = mSlot; + ESM::EffectList effectsToApply; + effectsToApply.mList = applyPair.second; + cast.inflict(applyPair.first, caster, effectsToApply, rangeType, true); + } + } + void CastSpell::launchMagicBolt () { osg::Vec3f fallbackDirection(0, 1, 0); @@ -174,7 +272,7 @@ namespace MWMechanics } if (!exploded) - MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, target, range, mId, mSourceName, mFromProjectile, mSlot); + explodeSpell(effects, caster, target, range); if (!target.isEmpty()) { diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 835c3b6633..d31fb789e4 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -27,6 +27,8 @@ namespace MWMechanics void playSpellCastingEffects(const std::vector& effects); + void explodeSpell(const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore, ESM::RangeType rangeType) const; + public: std::string mId; // ID of spell, potion, item etc std::string mSourceName; // Display name for spell, potion, etc @@ -37,7 +39,6 @@ namespace MWMechanics int mSlot{0}; ESM::ActiveSpells::EffectType mType{ESM::ActiveSpells::Type_Temporary}; - public: CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const bool fromProjectile=false, const bool manualSpell=false); bool cast (const ESM::Spell* spell); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e1618b4b72..6e0dbf5fb7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3730,107 +3730,6 @@ namespace MWWorld mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX); } - void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType, - const std::string& id, const std::string& sourceName, const bool fromProjectile, int slot) - { - std::map > toApply; - int index = -1; - for (const ESM::ENAMstruct& effectInfo : effects.mList) - { - ++index; - const ESM::MagicEffect* effect = mStore.get().find(effectInfo.mEffectID); - - if (effectInfo.mRange != rangeType || (effectInfo.mArea <= 0 && !ignore.isEmpty() && ignore.getClass().isActor())) - continue; // Not right range type, or not area effect and hit an actor - - if (fromProjectile && effectInfo.mArea <= 0) - continue; // Don't play explosion for projectiles with 0-area effects - - if (!fromProjectile && effectInfo.mRange == ESM::RT_Touch && !ignore.isEmpty() && !ignore.getClass().isActor() && !ignore.getClass().hasToolTip(ignore) - && (caster.isEmpty() || caster.getClass().isActor())) - continue; // Don't play explosion for touch spells on non-activatable objects except when spell is from a projectile enchantment or ExplodeSpell - - // Spawn the explosion orb effect - const ESM::Static* areaStatic; - if (!effect->mArea.empty()) - areaStatic = mStore.get().find (effect->mArea); - else - areaStatic = mStore.get().find ("VFX_DefaultArea"); - - const std::string& texture = effect->mParticle; - - if (effectInfo.mArea <= 0) - { - if (effectInfo.mRange == ESM::RT_Target) - mRendering->spawnEffect( - Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel, mResourceSystem->getVFS()), - texture, origin, 1.0f); - continue; - } - else - mRendering->spawnEffect( - Misc::ResourceHelpers::correctMeshPath(areaStatic->mModel, mResourceSystem->getVFS()), - texture, origin, static_cast(effectInfo.mArea * 2)); - - // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) - static const std::string schools[] = { - "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" - }; - { - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(!effect->mAreaSound.empty()) - sndMgr->playSound3D(origin, effect->mAreaSound, 1.0f, 1.0f); - else - sndMgr->playSound3D(origin, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f); - } - // Get the actors in range of the effect - std::vector objects; - MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( - origin, feetToGameUnits(static_cast(effectInfo.mArea)), objects); - for (const Ptr& affected : objects) - { - // Ignore actors without collisions here, otherwise it will be possible to hit actors outside processing range. - if (affected.getClass().isActor() && !isActorCollisionEnabled(affected)) - continue; - - auto& list = toApply[affected]; - while (list.size() < static_cast(index)) - { - // Insert dummy effects to preserve indices - auto& dummy = list.emplace_back(effectInfo); - dummy.mRange = ESM::RT_Self; - assert(dummy.mRange != rangeType); - } - list.push_back(effectInfo); - } - } - - // Now apply the appropriate effects to each actor in range - for (auto& applyPair : toApply) - { - MWWorld::Ptr source = caster; - // Vanilla-compatible behaviour of never applying the spell to the caster - // (could be changed by mods later) - if (applyPair.first == caster) - continue; - - if (applyPair.first == ignore) - continue; - - if (source.isEmpty()) - source = applyPair.first; - - MWMechanics::CastSpell cast(source, applyPair.first); - cast.mHitPosition = origin; - cast.mId = id; - cast.mSourceName = sourceName; - cast.mSlot = slot; - ESM::EffectList effectsToApply; - effectsToApply.mList = applyPair.second; - cast.inflict(applyPair.first, caster, effectsToApply, rangeType, true); - } - } - void World::activate(const Ptr &object, const Ptr &actor) { breakInvisibility(actor); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d3d2ab82e1..72123ed332 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -692,10 +692,6 @@ namespace MWWorld void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) override; - void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore, - ESM::RangeType rangeType, const std::string& id, const std::string& sourceName, - const bool fromProjectile=false, int slot = 0) override; - void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) override; /// @see MWWorld::WeatherManager::isInStorm