mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-01 00:45:34 +00:00
Use btCollisionObject* instead of MWWorld::Ptr inside of Projectile
collision handling and castRay() to avoid calling getPtr(). It is a step forward removing the mutex inside of PtrHolder. Do the same for DeepestNotMeContactTestResultCallback. It is used only for not-ranged combat for now, but do it anyway for parity with all other callback. This way, once the PtrHolder mutex is gone one will not have to worry about wether it is safe to use the callback in a specific context. To avoid use-after-free with projectile / projectile collision, defer deletion of projectile. Since instead of storing a copy of target Ptr we have a pointer to its collision object, we can't delete projectiles until after we finished iterating over the loops.
This commit is contained in:
parent
e88b94d0b0
commit
07fa1803f7
9 changed files with 90 additions and 84 deletions
|
@ -77,10 +77,8 @@ namespace MWPhysics
|
||||||
auto* projectileHolder = static_cast<Projectile*>(convexResult.m_hitCollisionObject->getUserPointer());
|
auto* projectileHolder = static_cast<Projectile*>(convexResult.m_hitCollisionObject->getUserPointer());
|
||||||
if (!projectileHolder->isActive())
|
if (!projectileHolder->isActive())
|
||||||
return btScalar(1);
|
return btScalar(1);
|
||||||
auto* targetHolder = static_cast<PtrHolder*>(mMe->getUserPointer());
|
if (projectileHolder->isValidTarget(mMe))
|
||||||
const MWWorld::Ptr target = targetHolder->getPtr();
|
projectileHolder->hit(mMe, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal);
|
||||||
if (projectileHolder->isValidTarget(target))
|
|
||||||
projectileHolder->hit(target, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal);
|
|
||||||
return btScalar(1);
|
return btScalar(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "collisiontype.hpp"
|
||||||
#include "ptrholder.hpp"
|
#include "ptrholder.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
|
@ -19,17 +20,14 @@ namespace MWPhysics
|
||||||
|
|
||||||
btScalar ClosestNotMeRayResultCallback::addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
|
btScalar ClosestNotMeRayResultCallback::addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
|
||||||
{
|
{
|
||||||
if (rayResult.m_collisionObject == mMe)
|
const auto* hitObject = rayResult.m_collisionObject;
|
||||||
|
if (hitObject == mMe)
|
||||||
return 1.f;
|
return 1.f;
|
||||||
|
|
||||||
if (!mTargets.empty())
|
if (hitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor && !mTargets.empty())
|
||||||
{
|
{
|
||||||
if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))
|
if ((std::find(mTargets.begin(), mTargets.end(), hitObject) == mTargets.end()))
|
||||||
{
|
return 1.f;
|
||||||
auto* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
|
|
||||||
if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())
|
|
||||||
return 1.f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "collisiontype.hpp"
|
||||||
#include "ptrholder.hpp"
|
#include "ptrholder.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
|
@ -23,14 +24,10 @@ namespace MWPhysics
|
||||||
const btCollisionObject* collisionObject = col1Wrap->m_collisionObject;
|
const btCollisionObject* collisionObject = col1Wrap->m_collisionObject;
|
||||||
if (collisionObject != mMe)
|
if (collisionObject != mMe)
|
||||||
{
|
{
|
||||||
if (!mTargets.empty())
|
if (collisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor && !mTargets.empty())
|
||||||
{
|
{
|
||||||
if ((std::find(mTargets.begin(), mTargets.end(), collisionObject) == mTargets.end()))
|
if ((std::find(mTargets.begin(), mTargets.end(), collisionObject) == mTargets.end()))
|
||||||
{
|
return 0.f;
|
||||||
PtrHolder* holder = static_cast<PtrHolder*>(collisionObject->getUserPointer());
|
|
||||||
if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())
|
|
||||||
return 0.f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
|
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
|
||||||
|
|
|
@ -635,19 +635,7 @@ namespace MWPhysics
|
||||||
if (btFrom == btTo)
|
if (btFrom == btTo)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto casterPtr = projectile->getCaster();
|
ProjectileConvexCallback resultCallback(projectile->getCasterCollisionObject(), projectile->getCollisionObject(), btFrom, btTo, projectile);
|
||||||
const auto* caster = [this,&casterPtr]() -> const btCollisionObject*
|
|
||||||
{
|
|
||||||
const Actor* actor = getActor(casterPtr);
|
|
||||||
if (actor)
|
|
||||||
return actor->getCollisionObject();
|
|
||||||
const Object* object = getObject(casterPtr);
|
|
||||||
if (object)
|
|
||||||
return object->getCollisionObject();
|
|
||||||
return nullptr;
|
|
||||||
}();
|
|
||||||
|
|
||||||
ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile);
|
|
||||||
resultCallback.m_collisionFilterMask = 0xff;
|
resultCallback.m_collisionFilterMask = 0xff;
|
||||||
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
|
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "actor.hpp"
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "mtphysics.hpp"
|
#include "mtphysics.hpp"
|
||||||
|
#include "object.hpp"
|
||||||
#include "projectile.hpp"
|
#include "projectile.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
|
@ -17,7 +19,7 @@ Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, f
|
||||||
: mCanCrossWaterSurface(canCrossWaterSurface)
|
: mCanCrossWaterSurface(canCrossWaterSurface)
|
||||||
, mCrossedWaterSurface(false)
|
, mCrossedWaterSurface(false)
|
||||||
, mActive(true)
|
, mActive(true)
|
||||||
, mCaster(caster)
|
, mHitTarget(nullptr)
|
||||||
, mWaterHitPosition(std::nullopt)
|
, mWaterHitPosition(std::nullopt)
|
||||||
, mPhysics(physicssystem)
|
, mPhysics(physicssystem)
|
||||||
, mTaskScheduler(scheduler)
|
, mTaskScheduler(scheduler)
|
||||||
|
@ -32,6 +34,7 @@ Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, f
|
||||||
mCollisionObject->setUserPointer(this);
|
mCollisionObject->setUserPointer(this);
|
||||||
|
|
||||||
setPosition(position);
|
setPosition(position);
|
||||||
|
setCaster(caster);
|
||||||
|
|
||||||
const int collisionMask = CollisionType_World | CollisionType_HeightMap |
|
const int collisionMask = CollisionType_World | CollisionType_HeightMap |
|
||||||
CollisionType_Actor | CollisionType_Door | CollisionType_Water | CollisionType_Projectile;
|
CollisionType_Actor | CollisionType_Door | CollisionType_Water | CollisionType_Projectile;
|
||||||
|
@ -77,54 +80,67 @@ bool Projectile::canTraverseWater() const
|
||||||
return mCanCrossWaterSurface;
|
return mCanCrossWaterSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Projectile::hit(const MWWorld::Ptr& target, btVector3 pos, btVector3 normal)
|
void Projectile::hit(const btCollisionObject* target, btVector3 pos, btVector3 normal)
|
||||||
{
|
{
|
||||||
if (!mActive.load(std::memory_order_acquire))
|
bool active = true;
|
||||||
|
if (!mActive.compare_exchange_strong(active, false, std::memory_order_relaxed) || !active)
|
||||||
return;
|
return;
|
||||||
std::scoped_lock lock(mMutex);
|
|
||||||
mHitTarget = target;
|
mHitTarget = target;
|
||||||
mHitPosition = pos;
|
mHitPosition = pos;
|
||||||
mHitNormal = normal;
|
mHitNormal = normal;
|
||||||
mActive.store(false, std::memory_order_release);
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr Projectile::getTarget() const
|
||||||
|
{
|
||||||
|
assert(!mActive);
|
||||||
|
auto* target = static_cast<PtrHolder*>(mHitTarget->getUserPointer());
|
||||||
|
return target ? target->getPtr() : MWWorld::Ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr Projectile::getCaster() const
|
MWWorld::Ptr Projectile::getCaster() const
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mMutex);
|
|
||||||
return mCaster;
|
return mCaster;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Projectile::setCaster(const MWWorld::Ptr& caster)
|
void Projectile::setCaster(const MWWorld::Ptr& caster)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mMutex);
|
|
||||||
mCaster = caster;
|
mCaster = caster;
|
||||||
|
mCasterColObj = [this,&caster]() -> const btCollisionObject*
|
||||||
|
{
|
||||||
|
const Actor* actor = mPhysics->getActor(caster);
|
||||||
|
if (actor)
|
||||||
|
return actor->getCollisionObject();
|
||||||
|
const Object* object = mPhysics->getObject(caster);
|
||||||
|
if (object)
|
||||||
|
return object->getCollisionObject();
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Projectile::setValidTargets(const std::vector<MWWorld::Ptr>& targets)
|
void Projectile::setValidTargets(const std::vector<MWWorld::Ptr>& targets)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mMutex);
|
std::scoped_lock lock(mMutex);
|
||||||
mValidTargets = targets;
|
mValidTargets.clear();
|
||||||
|
for (const auto& ptr : targets)
|
||||||
|
{
|
||||||
|
const auto* physicActor = mPhysics->getActor(ptr);
|
||||||
|
if (physicActor)
|
||||||
|
mValidTargets.push_back(physicActor->getCollisionObject());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Projectile::isValidTarget(const MWWorld::Ptr& target) const
|
bool Projectile::isValidTarget(const btCollisionObject* target) const
|
||||||
{
|
{
|
||||||
|
assert(target);
|
||||||
std::scoped_lock lock(mMutex);
|
std::scoped_lock lock(mMutex);
|
||||||
if (mCaster == target)
|
if (mCasterColObj == target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (target.isEmpty() || mValidTargets.empty())
|
if (mValidTargets.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool validTarget = false;
|
return std::any_of(mValidTargets.begin(), mValidTargets.end(),
|
||||||
for (const auto& targetActor : mValidTargets)
|
[target](const btCollisionObject* actor) { return target == actor; });
|
||||||
{
|
|
||||||
if (targetActor == target)
|
|
||||||
{
|
|
||||||
validTarget = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return validTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<btVector3> Projectile::getWaterHitPosition()
|
std::optional<btVector3> Projectile::getWaterHitPosition()
|
||||||
|
|
|
@ -47,21 +47,21 @@ namespace MWPhysics
|
||||||
return mActive.load(std::memory_order_acquire);
|
return mActive.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr getTarget() const
|
MWWorld::Ptr getTarget() const;
|
||||||
{
|
|
||||||
assert(!mActive);
|
|
||||||
return mHitTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
MWWorld::Ptr getCaster() const;
|
MWWorld::Ptr getCaster() const;
|
||||||
void setCaster(const MWWorld::Ptr& caster);
|
void setCaster(const MWWorld::Ptr& caster);
|
||||||
|
const btCollisionObject* getCasterCollisionObject() const
|
||||||
|
{
|
||||||
|
return mCasterColObj;
|
||||||
|
}
|
||||||
|
|
||||||
bool canTraverseWater() const;
|
bool canTraverseWater() const;
|
||||||
|
|
||||||
void hit(const MWWorld::Ptr& target, btVector3 pos, btVector3 normal);
|
void hit(const btCollisionObject* target, btVector3 pos, btVector3 normal);
|
||||||
|
|
||||||
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
||||||
bool isValidTarget(const MWWorld::Ptr& target) const;
|
bool isValidTarget(const btCollisionObject* target) const;
|
||||||
|
|
||||||
std::optional<btVector3> getWaterHitPosition();
|
std::optional<btVector3> getWaterHitPosition();
|
||||||
void setWaterHitPosition(btVector3 pos);
|
void setWaterHitPosition(btVector3 pos);
|
||||||
|
@ -76,13 +76,14 @@ namespace MWPhysics
|
||||||
bool mCrossedWaterSurface;
|
bool mCrossedWaterSurface;
|
||||||
std::atomic<bool> mActive;
|
std::atomic<bool> mActive;
|
||||||
MWWorld::Ptr mCaster;
|
MWWorld::Ptr mCaster;
|
||||||
MWWorld::Ptr mHitTarget;
|
const btCollisionObject* mCasterColObj;
|
||||||
|
const btCollisionObject* mHitTarget;
|
||||||
std::optional<btVector3> mWaterHitPosition;
|
std::optional<btVector3> mWaterHitPosition;
|
||||||
osg::Vec3f mPosition;
|
osg::Vec3f mPosition;
|
||||||
btVector3 mHitPosition;
|
btVector3 mHitPosition;
|
||||||
btVector3 mHitNormal;
|
btVector3 mHitNormal;
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> mValidTargets;
|
std::vector<const btCollisionObject*> mValidTargets;
|
||||||
|
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
|
@ -8,41 +10,41 @@
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
ProjectileConvexCallback::ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj)
|
ProjectileConvexCallback::ProjectileConvexCallback(const btCollisionObject* caster, const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj)
|
||||||
: btCollisionWorld::ClosestConvexResultCallback(from, to)
|
: btCollisionWorld::ClosestConvexResultCallback(from, to)
|
||||||
, mMe(me), mProjectile(proj)
|
, mCaster(caster)
|
||||||
|
, mMe(me)
|
||||||
|
, mProjectile(proj)
|
||||||
{
|
{
|
||||||
assert(mProjectile);
|
assert(mProjectile);
|
||||||
}
|
}
|
||||||
|
|
||||||
btScalar ProjectileConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace)
|
btScalar ProjectileConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace)
|
||||||
{
|
{
|
||||||
|
const auto* hitObject = result.m_hitCollisionObject;
|
||||||
// don't hit the caster
|
// don't hit the caster
|
||||||
if (result.m_hitCollisionObject == mMe)
|
if (hitObject == mCaster)
|
||||||
return 1.f;
|
return 1.f;
|
||||||
|
|
||||||
// don't hit the projectile
|
// don't hit the projectile
|
||||||
if (result.m_hitCollisionObject == mProjectile->getCollisionObject())
|
if (hitObject == mMe)
|
||||||
return 1.f;
|
return 1.f;
|
||||||
|
|
||||||
btCollisionWorld::ClosestConvexResultCallback::addSingleResult(result, normalInWorldSpace);
|
btCollisionWorld::ClosestConvexResultCallback::addSingleResult(result, normalInWorldSpace);
|
||||||
switch (result.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
switch (hitObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
||||||
{
|
{
|
||||||
case CollisionType_Actor:
|
case CollisionType_Actor:
|
||||||
{
|
{
|
||||||
auto* target = static_cast<Actor*>(result.m_hitCollisionObject->getUserPointer());
|
if (!mProjectile->isValidTarget(hitObject))
|
||||||
if (!mProjectile->isValidTarget(target->getPtr()))
|
|
||||||
return 1.f;
|
return 1.f;
|
||||||
mProjectile->hit(target->getPtr(), result.m_hitPointLocal, result.m_hitNormalLocal);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CollisionType_Projectile:
|
case CollisionType_Projectile:
|
||||||
{
|
{
|
||||||
auto* target = static_cast<Projectile*>(result.m_hitCollisionObject->getUserPointer());
|
auto* target = static_cast<Projectile*>(hitObject->getUserPointer());
|
||||||
if (!mProjectile->isValidTarget(target->getCaster()))
|
if (!mProjectile->isValidTarget(target->getCasterCollisionObject()))
|
||||||
return 1.f;
|
return 1.f;
|
||||||
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
target->hit(mMe, m_hitPointWorld, m_hitNormalWorld);
|
||||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CollisionType_Water:
|
case CollisionType_Water:
|
||||||
|
@ -50,17 +52,10 @@ namespace MWPhysics
|
||||||
mProjectile->setWaterHitPosition(m_hitPointWorld);
|
mProjectile->setWaterHitPosition(m_hitPointWorld);
|
||||||
if (mProjectile->canTraverseWater())
|
if (mProjectile->canTraverseWater())
|
||||||
return 1.f;
|
return 1.f;
|
||||||
mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
auto* target = static_cast<PtrHolder*>(result.m_hitCollisionObject->getUserPointer());
|
|
||||||
auto ptr = target ? target->getPtr() : MWWorld::Ptr();
|
|
||||||
mProjectile->hit(ptr, m_hitPointWorld, m_hitNormalWorld);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mProjectile->hit(hitObject, m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
|
||||||
return result.m_hitFraction;
|
return result.m_hitFraction;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,12 @@ namespace MWPhysics
|
||||||
class ProjectileConvexCallback : public btCollisionWorld::ClosestConvexResultCallback
|
class ProjectileConvexCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj);
|
ProjectileConvexCallback(const btCollisionObject* caster, const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj);
|
||||||
|
|
||||||
btScalar addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace) override;
|
btScalar addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const btCollisionObject* mCaster;
|
||||||
const btCollisionObject* mMe;
|
const btCollisionObject* mMe;
|
||||||
Projectile* mProjectile;
|
Projectile* mProjectile;
|
||||||
};
|
};
|
||||||
|
|
|
@ -521,7 +521,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
||||||
cleanupProjectile(projectileState);
|
projectileState.mToDelete = true;
|
||||||
}
|
}
|
||||||
for (auto& magicBoltState : mMagicBolts)
|
for (auto& magicBoltState : mMagicBolts)
|
||||||
{
|
{
|
||||||
|
@ -550,7 +550,19 @@ namespace MWWorld
|
||||||
cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
|
cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
|
||||||
|
|
||||||
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, target, ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, target, ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
||||||
cleanupMagicBolt(magicBoltState);
|
magicBoltState.mToDelete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& projectileState : mProjectiles)
|
||||||
|
{
|
||||||
|
if (projectileState.mToDelete)
|
||||||
|
cleanupProjectile(projectileState);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& magicBoltState : mMagicBolts)
|
||||||
|
{
|
||||||
|
if (magicBoltState.mToDelete)
|
||||||
|
cleanupMagicBolt(magicBoltState);
|
||||||
}
|
}
|
||||||
mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }),
|
mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }),
|
||||||
mProjectiles.end());
|
mProjectiles.end());
|
||||||
|
|
Loading…
Reference in a new issue