1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-21 07:09:42 +00:00

Move explodeSpell out of World

This commit is contained in:
Evil Eye 2022-09-04 14:01:36 +02:00
parent 84ec78f0d6
commit 4eafe3696c
5 changed files with 101 additions and 111 deletions

View file

@ -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

View file

@ -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<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply;
int index = -1;
for (const ESM::ENAMstruct& effectInfo : effects.mList)
{
++index;
const ESM::MagicEffect* effect = world->getStore().get<ESM::MagicEffect>().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<ESM::Static>().find(effect->mArea);
else
areaStatic = world->getStore().get<ESM::Static>().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<float>(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<MWWorld::Ptr> objects;
static const int unitsPerFoot = ceil(Constants::UnitsPerFoot);
MWBase::Environment::get().getMechanicsManager()->getObjectsInRange(mHitPosition, static_cast<float>(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<std::size_t>(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())
{

View file

@ -27,6 +27,8 @@ namespace MWMechanics
void playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& 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);

View file

@ -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<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply;
int index = -1;
for (const ESM::ENAMstruct& effectInfo : effects.mList)
{
++index;
const ESM::MagicEffect* effect = mStore.get<ESM::MagicEffect>().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<ESM::Static>().find (effect->mArea);
else
areaStatic = mStore.get<ESM::Static>().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<float>(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<MWWorld::Ptr> objects;
MWBase::Environment::get().getMechanicsManager()->getObjectsInRange(
origin, feetToGameUnits(static_cast<float>(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<std::size_t>(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);

View file

@ -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