mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-01 04:45:34 +00:00
Merge branch 'projectile_physics' into 'master'
Precise projectile physics (closes #4201) Closes #4201 See merge request OpenMW/openmw!550
This commit is contained in:
commit
d2c5de5211
17 changed files with 260 additions and 226 deletions
|
@ -21,6 +21,7 @@
|
|||
Bug #4039: Multiple followers should have the same following distance
|
||||
Bug #4055: Local scripts don't inherit variables from their base record
|
||||
Bug #4083: Door animation freezes when colliding with actors
|
||||
Bug #4201: Projectile-projectile collision
|
||||
Bug #4247: Cannot walk up stairs in Ebonheart docks
|
||||
Bug #4363: Editor: Defect in Clone Function for Dialogue Info records
|
||||
Bug #4447: Actor collision capsule shape allows looking through some walls
|
||||
|
|
|
@ -73,7 +73,7 @@ add_openmw_dir (mwworld
|
|||
add_openmw_dir (mwphysics
|
||||
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
||||
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
|
||||
closestnotmeconvexresultcallback raycasting mtphysics contacttestwrapper
|
||||
actorconvexcallback raycasting mtphysics contacttestwrapper projectileconvexcallback
|
||||
)
|
||||
|
||||
add_openmw_dir (mwclass
|
||||
|
|
|
@ -60,7 +60,10 @@ namespace MWMechanics
|
|||
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
|
||||
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
|
||||
{
|
||||
if (!target.isEmpty() && target.getClass().isActor())
|
||||
if (target.isEmpty())
|
||||
return;
|
||||
|
||||
if (target.getClass().isActor())
|
||||
{
|
||||
// Early-out for characters that have departed.
|
||||
const auto& stats = target.getClass().getCreatureStats(target);
|
||||
|
@ -82,7 +85,7 @@ namespace MWMechanics
|
|||
return;
|
||||
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (mId);
|
||||
if (spell && !target.isEmpty() && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight))
|
||||
if (spell && target.getClass().isActor() && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight))
|
||||
{
|
||||
int requiredResistance = (spell->mData.mType == ESM::Spell::ST_Disease) ?
|
||||
ESM::MagicEffect::ResistCommonDisease
|
||||
|
@ -105,13 +108,13 @@ namespace MWMechanics
|
|||
// This is required for Weakness effects in a spell to apply to any subsequent effects in the spell.
|
||||
// Otherwise, they'd only apply after the whole spell was added.
|
||||
MagicEffects targetEffects;
|
||||
if (!target.isEmpty() && target.getClass().isActor())
|
||||
if (target.getClass().isActor())
|
||||
targetEffects += target.getClass().getCreatureStats(target).getMagicEffects();
|
||||
|
||||
bool castByPlayer = (!caster.isEmpty() && caster == getPlayer());
|
||||
|
||||
ActiveSpells targetSpells;
|
||||
if (!target.isEmpty() && target.getClass().isActor())
|
||||
if (target.getClass().isActor())
|
||||
targetSpells = target.getClass().getCreatureStats(target).getActiveSpells();
|
||||
|
||||
bool canCastAnEffect = false; // For bound equipment.If this remains false
|
||||
|
@ -123,7 +126,7 @@ namespace MWMechanics
|
|||
|
||||
int currentEffectIndex = 0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
||||
!target.isEmpty() && effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex)
|
||||
effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex)
|
||||
{
|
||||
if (effectIt->mRange != range)
|
||||
continue;
|
||||
|
@ -267,7 +270,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Re-casting a summon effect will remove the creature from previous castings of that effect.
|
||||
if (isSummoningEffect(effectIt->mEffectID) && !target.isEmpty() && target.getClass().isActor())
|
||||
if (isSummoningEffect(effectIt->mEffectID) && target.getClass().isActor())
|
||||
{
|
||||
CreatureStats& targetStats = target.getClass().getCreatureStats(target);
|
||||
ESM::SummonKey key(effectIt->mEffectID, mId, currentEffectIndex);
|
||||
|
@ -310,18 +313,16 @@ namespace MWMechanics
|
|||
if (!exploded)
|
||||
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, target, range, mId, mSourceName, mFromProjectile);
|
||||
|
||||
if (!target.isEmpty()) {
|
||||
if (!reflectedEffects.mList.empty())
|
||||
inflict(caster, target, reflectedEffects, range, true, exploded);
|
||||
if (!reflectedEffects.mList.empty())
|
||||
inflict(caster, target, reflectedEffects, range, true, exploded);
|
||||
|
||||
if (!appliedLastingEffects.empty())
|
||||
{
|
||||
int casterActorId = -1;
|
||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||
mSourceName, casterActorId);
|
||||
}
|
||||
if (!appliedLastingEffects.empty())
|
||||
{
|
||||
int casterActorId = -1;
|
||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||
mSourceName, casterActorId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "closestnotmeconvexresultcallback.hpp"
|
||||
#include "actorconvexcallback.hpp"
|
||||
#include "collisiontype.hpp"
|
||||
#include "contacttestwrapper.h"
|
||||
|
||||
|
@ -31,13 +31,13 @@ namespace MWPhysics
|
|||
}
|
||||
};
|
||||
|
||||
ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world)
|
||||
ActorConvexCallback::ActorConvexCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world)
|
||||
: btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),
|
||||
mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot), mWorld(world)
|
||||
{
|
||||
}
|
||||
|
||||
btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
|
||||
btScalar ActorConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
|
||||
{
|
||||
if (convexResult.m_hitCollisionObject == mMe)
|
||||
return btScalar(1);
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef OPENMW_MWPHYSICS_CLOSESTNOTMECONVEXRESULTCALLBACK_H
|
||||
#define OPENMW_MWPHYSICS_CLOSESTNOTMECONVEXRESULTCALLBACK_H
|
||||
#ifndef OPENMW_MWPHYSICS_ACTORCONVEXCALLBACK_H
|
||||
#define OPENMW_MWPHYSICS_ACTORCONVEXCALLBACK_H
|
||||
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||
|
||||
|
@ -7,10 +7,10 @@ class btCollisionObject;
|
|||
|
||||
namespace MWPhysics
|
||||
{
|
||||
class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||
class ActorConvexCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||
{
|
||||
public:
|
||||
ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world);
|
||||
ActorConvexCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world);
|
||||
|
||||
btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) override;
|
||||
|
|
@ -7,16 +7,13 @@
|
|||
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "actor.hpp"
|
||||
#include "collisiontype.hpp"
|
||||
#include "projectile.hpp"
|
||||
#include "ptrholder.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to, Projectile* proj)
|
||||
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to)
|
||||
: btCollisionWorld::ClosestRayResultCallback(from, to)
|
||||
, mMe(me), mTargets(std::move(targets)), mProjectile(proj)
|
||||
, mMe(me), mTargets(std::move(targets))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -25,9 +22,6 @@ namespace MWPhysics
|
|||
if (rayResult.m_collisionObject == mMe)
|
||||
return 1.f;
|
||||
|
||||
if (mProjectile && rayResult.m_collisionObject == mProjectile->getCollisionObject())
|
||||
return 1.f;
|
||||
|
||||
if (!mTargets.empty())
|
||||
{
|
||||
if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))
|
||||
|
@ -38,27 +32,6 @@ namespace MWPhysics
|
|||
}
|
||||
}
|
||||
|
||||
btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||
if (mProjectile)
|
||||
{
|
||||
switch (rayResult.m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
||||
{
|
||||
case CollisionType_Actor:
|
||||
{
|
||||
auto* target = static_cast<Actor*>(rayResult.m_collisionObject->getUserPointer());
|
||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
break;
|
||||
}
|
||||
case CollisionType_Projectile:
|
||||
{
|
||||
auto* target = static_cast<Projectile*>(rayResult.m_collisionObject->getUserPointer());
|
||||
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rayResult.m_hitFraction;
|
||||
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,13 @@ namespace MWPhysics
|
|||
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
||||
{
|
||||
public:
|
||||
ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to, Projectile* proj=nullptr);
|
||||
ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to);
|
||||
|
||||
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
|
||||
|
||||
private:
|
||||
const btCollisionObject* mMe;
|
||||
const std::vector<const btCollisionObject*> mTargets;
|
||||
Projectile* mProjectile;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "deepestnotmecontacttestresultcallback.hpp"
|
||||
#include "closestnotmerayresultcallback.hpp"
|
||||
#include "contacttestresultcallback.hpp"
|
||||
#include "projectileconvexcallback.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "movementsolver.hpp"
|
||||
#include "mtphysics.hpp"
|
||||
|
@ -246,7 +247,7 @@ namespace MWPhysics
|
|||
return 0.f;
|
||||
}
|
||||
|
||||
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group, int projId) const
|
||||
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group) const
|
||||
{
|
||||
if (from == to)
|
||||
{
|
||||
|
@ -283,7 +284,7 @@ namespace MWPhysics
|
|||
}
|
||||
}
|
||||
|
||||
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo, getProjectile(projId));
|
||||
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo);
|
||||
resultCallback.m_collisionFilterGroup = group;
|
||||
resultCallback.m_collisionFilterMask = mask;
|
||||
|
||||
|
@ -580,15 +581,44 @@ namespace MWPhysics
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position)
|
||||
void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position) const
|
||||
{
|
||||
ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId);
|
||||
if (foundProjectile != mProjectiles.end())
|
||||
{
|
||||
foundProjectile->second->setPosition(position);
|
||||
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
||||
const auto foundProjectile = mProjectiles.find(projectileId);
|
||||
assert(foundProjectile != mProjectiles.end());
|
||||
auto* projectile = foundProjectile->second.get();
|
||||
|
||||
btVector3 btFrom = Misc::Convert::toBullet(projectile->getPosition());
|
||||
btVector3 btTo = Misc::Convert::toBullet(position);
|
||||
|
||||
if (btFrom == btTo)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto casterPtr = projectile->getCaster();
|
||||
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;
|
||||
}();
|
||||
assert(caster);
|
||||
|
||||
ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile);
|
||||
resultCallback.m_collisionFilterMask = 0xff;
|
||||
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
|
||||
|
||||
const btQuaternion btrot = btQuaternion::getIdentity();
|
||||
btTransform from_ (btrot, btFrom);
|
||||
btTransform to_ (btrot, btTo);
|
||||
|
||||
mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback);
|
||||
|
||||
const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(resultCallback.m_hitPointWorld);
|
||||
projectile->setPosition(newpos);
|
||||
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
||||
}
|
||||
|
||||
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
|
||||
|
@ -651,10 +681,10 @@ namespace MWPhysics
|
|||
mActors.emplace(ptr, std::move(actor));
|
||||
}
|
||||
|
||||
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position)
|
||||
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canTraverseWater)
|
||||
{
|
||||
mProjectileId++;
|
||||
auto projectile = std::make_shared<Projectile>(mProjectileId, caster, position, mTaskScheduler.get(), this);
|
||||
auto projectile = std::make_shared<Projectile>(caster, position, radius, canTraverseWater, mTaskScheduler.get(), this);
|
||||
mProjectiles.emplace(mProjectileId, std::move(projectile));
|
||||
|
||||
return mProjectileId;
|
||||
|
@ -863,7 +893,7 @@ namespace MWPhysics
|
|||
mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight));
|
||||
mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get());
|
||||
mTaskScheduler->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water,
|
||||
CollisionType_Actor);
|
||||
CollisionType_Actor|CollisionType_Projectile);
|
||||
}
|
||||
|
||||
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
|
||||
|
|
|
@ -124,8 +124,8 @@ namespace MWPhysics
|
|||
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
|
||||
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
|
||||
|
||||
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position);
|
||||
void updateProjectile(const int projectileId, const osg::Vec3f &position);
|
||||
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canTraverseWater);
|
||||
void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
|
||||
void removeProjectile(const int projectileId);
|
||||
|
||||
void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
||||
|
@ -174,7 +174,7 @@ namespace MWPhysics
|
|||
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.
|
||||
RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),
|
||||
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const override;
|
||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const override;
|
||||
|
||||
RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;
|
||||
|
||||
|
|
|
@ -13,19 +13,22 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "collisiontype.hpp"
|
||||
#include "memory"
|
||||
#include "mtphysics.hpp"
|
||||
#include "projectile.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
Projectile::Projectile(int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
||||
: mActive(true)
|
||||
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
||||
: mCanCrossWaterSurface(canCrossWaterSurface)
|
||||
, mCrossedWaterSurface(false)
|
||||
, mActive(true)
|
||||
, mCaster(caster)
|
||||
, mWaterHitPosition(std::nullopt)
|
||||
, mPhysics(physicssystem)
|
||||
, mTaskScheduler(scheduler)
|
||||
, mProjectileId(projectileId)
|
||||
{
|
||||
mShape.reset(new btSphereShape(1.f));
|
||||
mShape = std::make_unique<btSphereShape>(radius);
|
||||
mConvexShape = static_cast<btConvexShape*>(mShape.get());
|
||||
|
||||
mCollisionObject = std::make_unique<btCollisionObject>();
|
||||
|
@ -67,6 +70,17 @@ void Projectile::setPosition(const osg::Vec3f &position)
|
|||
mTransformUpdatePending = true;
|
||||
}
|
||||
|
||||
osg::Vec3f Projectile::getPosition() const
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
return Misc::Convert::toOsg(mLocalTransform.getOrigin());
|
||||
}
|
||||
|
||||
bool Projectile::canTraverseWater() const
|
||||
{
|
||||
return mCanCrossWaterSurface;
|
||||
}
|
||||
|
||||
void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)
|
||||
{
|
||||
if (!mActive.load(std::memory_order_acquire))
|
||||
|
@ -78,12 +92,6 @@ void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)
|
|||
mActive.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void Projectile::activate()
|
||||
{
|
||||
assert(!mActive);
|
||||
mActive.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
MWWorld::Ptr Projectile::getCaster() const
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
|
@ -108,21 +116,32 @@ bool Projectile::isValidTarget(const MWWorld::Ptr& target) const
|
|||
if (mCaster == target)
|
||||
return false;
|
||||
|
||||
if (!mValidTargets.empty())
|
||||
if (target.isEmpty() || mValidTargets.empty())
|
||||
return true;
|
||||
|
||||
bool validTarget = false;
|
||||
for (const auto& targetActor : mValidTargets)
|
||||
{
|
||||
bool validTarget = false;
|
||||
for (const auto& targetActor : mValidTargets)
|
||||
if (targetActor == target)
|
||||
{
|
||||
if (targetActor == target)
|
||||
{
|
||||
validTarget = true;
|
||||
break;
|
||||
}
|
||||
validTarget = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return validTarget;
|
||||
}
|
||||
return true;
|
||||
return validTarget;
|
||||
}
|
||||
|
||||
std::optional<btVector3> Projectile::getWaterHitPosition()
|
||||
{
|
||||
return std::exchange(mWaterHitPosition, std::nullopt);
|
||||
}
|
||||
|
||||
void Projectile::setWaterHitPosition(btVector3 pos)
|
||||
{
|
||||
if (mCrossedWaterSurface)
|
||||
return;
|
||||
mCrossedWaterSurface = true;
|
||||
mWaterHitPosition = pos;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "components/misc/convert.hpp"
|
||||
|
||||
|
@ -32,7 +33,7 @@ namespace MWPhysics
|
|||
class Projectile final : public PtrHolder
|
||||
{
|
||||
public:
|
||||
Projectile(const int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
|
||||
Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
|
||||
~Projectile() override;
|
||||
|
||||
btConvexShape* getConvexShape() const { return mConvexShape; }
|
||||
|
@ -40,17 +41,13 @@ namespace MWPhysics
|
|||
void commitPositionChange();
|
||||
|
||||
void setPosition(const osg::Vec3f& position);
|
||||
osg::Vec3f getPosition() const;
|
||||
|
||||
btCollisionObject* getCollisionObject() const
|
||||
{
|
||||
return mCollisionObject.get();
|
||||
}
|
||||
|
||||
int getProjectileId() const
|
||||
{
|
||||
return mProjectileId;
|
||||
}
|
||||
|
||||
bool isActive() const
|
||||
{
|
||||
return mActive.load(std::memory_order_acquire);
|
||||
|
@ -65,18 +62,16 @@ namespace MWPhysics
|
|||
MWWorld::Ptr getCaster() const;
|
||||
void setCaster(MWWorld::Ptr caster);
|
||||
|
||||
osg::Vec3f getHitPos() const
|
||||
{
|
||||
assert(!mActive);
|
||||
return Misc::Convert::toOsg(mHitPosition);
|
||||
}
|
||||
bool canTraverseWater() const;
|
||||
|
||||
void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal);
|
||||
void activate();
|
||||
|
||||
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
||||
bool isValidTarget(const MWWorld::Ptr& target) const;
|
||||
|
||||
std::optional<btVector3> getWaterHitPosition();
|
||||
void setWaterHitPosition(btVector3 pos);
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<btCollisionShape> mShape;
|
||||
|
@ -85,9 +80,12 @@ namespace MWPhysics
|
|||
std::unique_ptr<btCollisionObject> mCollisionObject;
|
||||
btTransform mLocalTransform;
|
||||
bool mTransformUpdatePending;
|
||||
bool mCanCrossWaterSurface;
|
||||
bool mCrossedWaterSurface;
|
||||
std::atomic<bool> mActive;
|
||||
MWWorld::Ptr mCaster;
|
||||
MWWorld::Ptr mHitTarget;
|
||||
std::optional<btVector3> mWaterHitPosition;
|
||||
btVector3 mHitPosition;
|
||||
btVector3 mHitNormal;
|
||||
|
||||
|
@ -95,15 +93,11 @@ namespace MWPhysics
|
|||
|
||||
mutable std::mutex mMutex;
|
||||
|
||||
osg::Vec3f mPosition;
|
||||
|
||||
PhysicsSystem *mPhysics;
|
||||
PhysicsTaskScheduler *mTaskScheduler;
|
||||
|
||||
Projectile(const Projectile&);
|
||||
Projectile& operator=(const Projectile&);
|
||||
|
||||
int mProjectileId;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
67
apps/openmw/mwphysics/projectileconvexcallback.cpp
Normal file
67
apps/openmw/mwphysics/projectileconvexcallback.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "actor.hpp"
|
||||
#include "collisiontype.hpp"
|
||||
#include "projectile.hpp"
|
||||
#include "projectileconvexcallback.hpp"
|
||||
#include "ptrholder.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
ProjectileConvexCallback::ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj)
|
||||
: btCollisionWorld::ClosestConvexResultCallback(from, to)
|
||||
, mMe(me), mProjectile(proj)
|
||||
{
|
||||
assert(mProjectile);
|
||||
}
|
||||
|
||||
btScalar ProjectileConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace)
|
||||
{
|
||||
// don't hit the caster
|
||||
if (result.m_hitCollisionObject == mMe)
|
||||
return 1.f;
|
||||
|
||||
// don't hit the projectile
|
||||
if (result.m_hitCollisionObject == mProjectile->getCollisionObject())
|
||||
return 1.f;
|
||||
|
||||
btCollisionWorld::ClosestConvexResultCallback::addSingleResult(result, normalInWorldSpace);
|
||||
switch (result.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
||||
{
|
||||
case CollisionType_Actor:
|
||||
{
|
||||
auto* target = static_cast<Actor*>(result.m_hitCollisionObject->getUserPointer());
|
||||
if (!mProjectile->isValidTarget(target->getPtr()))
|
||||
return 1.f;
|
||||
mProjectile->hit(target->getPtr(), result.m_hitPointLocal, result.m_hitNormalLocal);
|
||||
break;
|
||||
}
|
||||
case CollisionType_Projectile:
|
||||
{
|
||||
auto* target = static_cast<Projectile*>(result.m_hitCollisionObject->getUserPointer());
|
||||
if (!mProjectile->isValidTarget(target->getCaster()))
|
||||
return 1.f;
|
||||
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
break;
|
||||
}
|
||||
case CollisionType_Water:
|
||||
{
|
||||
mProjectile->setWaterHitPosition(m_hitPointWorld);
|
||||
if (mProjectile->canTraverseWater())
|
||||
return 1.f;
|
||||
mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result.m_hitFraction;
|
||||
}
|
||||
|
||||
}
|
||||
|
27
apps/openmw/mwphysics/projectileconvexcallback.hpp
Normal file
27
apps/openmw/mwphysics/projectileconvexcallback.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H
|
||||
#define OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||
|
||||
class btCollisionObject;
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
class Projectile;
|
||||
|
||||
class ProjectileConvexCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||
{
|
||||
public:
|
||||
ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj);
|
||||
|
||||
btScalar addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace) override;
|
||||
|
||||
private:
|
||||
const btCollisionObject* mMe;
|
||||
Projectile* mProjectile;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -29,7 +29,7 @@ namespace MWPhysics
|
|||
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.
|
||||
virtual RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(),
|
||||
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const = 0;
|
||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const = 0;
|
||||
|
||||
virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const = 0;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "collisiontype.hpp"
|
||||
#include "actor.hpp"
|
||||
#include "closestnotmeconvexresultcallback.hpp"
|
||||
#include "actorconvexcallback.hpp"
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star
|
|||
to.setOrigin(btend);
|
||||
|
||||
const btVector3 motion = btstart-btend;
|
||||
ClosestNotMeConvexResultCallback newTraceCallback(actor, motion, btScalar(0.0), world);
|
||||
ActorConvexCallback newTraceCallback(actor, motion, btScalar(0.0), world);
|
||||
// Inherit the actor's collision group and mask
|
||||
newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup;
|
||||
newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask;
|
||||
|
@ -62,7 +62,7 @@ void ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const
|
|||
btTransform to(trans.getBasis(), btend);
|
||||
|
||||
const btVector3 motion = btstart-btend;
|
||||
ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionObject(), motion, btScalar(0.0), world);
|
||||
ActorConvexCallback newTraceCallback(actor->getCollisionObject(), motion, btScalar(0.0), world);
|
||||
// Inherit the actor's collision group and mask
|
||||
newTraceCallback.m_collisionFilterGroup = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterGroup;
|
||||
newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <iomanip>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
@ -43,6 +44,7 @@
|
|||
|
||||
#include "../mwsound/sound.hpp"
|
||||
|
||||
#include "../mwphysics/collisiontype.hpp"
|
||||
#include "../mwphysics/physicssystem.hpp"
|
||||
#include "../mwphysics/projectile.hpp"
|
||||
|
||||
|
@ -187,7 +189,7 @@ namespace MWWorld
|
|||
};
|
||||
|
||||
|
||||
void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient,
|
||||
float ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient,
|
||||
bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture)
|
||||
{
|
||||
state.mNode = new osg::PositionAttitudeTransform;
|
||||
|
@ -251,6 +253,7 @@ namespace MWWorld
|
|||
state.mNode->accept(assignVisitor);
|
||||
|
||||
MWRender::overrideFirstRootTexture(texture, mResourceSystem, projectile);
|
||||
return projectile->getBound().radius();
|
||||
}
|
||||
|
||||
void ProjectileManager::update(State& state, float duration)
|
||||
|
@ -305,7 +308,7 @@ namespace MWWorld
|
|||
|
||||
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
||||
|
||||
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
|
||||
const auto radius = createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
|
||||
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
for (const std::string &soundid : state.mSoundIds)
|
||||
|
@ -316,7 +319,7 @@ namespace MWWorld
|
|||
state.mSounds.push_back(sound);
|
||||
}
|
||||
|
||||
state.mProjectileId = mPhysics->addProjectile(caster, pos);
|
||||
state.mProjectileId = mPhysics->addProjectile(caster, pos, radius, false);
|
||||
state.mToDelete = false;
|
||||
mMagicBolts.push_back(state);
|
||||
}
|
||||
|
@ -340,7 +343,7 @@ namespace MWWorld
|
|||
if (!ptr.getClass().getEnchantment(ptr).empty())
|
||||
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
||||
|
||||
state.mProjectileId = mPhysics->addProjectile(actor, pos);
|
||||
state.mProjectileId = mPhysics->addProjectile(actor, pos, 1.f, true);
|
||||
state.mToDelete = false;
|
||||
mProjectiles.push_back(state);
|
||||
}
|
||||
|
@ -407,15 +410,7 @@ namespace MWWorld
|
|||
float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;
|
||||
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
||||
direction.normalize();
|
||||
osg::Vec3f pos(magicBoltState.mNode->getPosition());
|
||||
osg::Vec3f newPos = pos + direction * duration * speed;
|
||||
|
||||
for (const auto& sound : magicBoltState.mSounds)
|
||||
sound->setPosition(newPos);
|
||||
|
||||
magicBoltState.mNode->setPosition(newPos);
|
||||
|
||||
mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos);
|
||||
osg::Vec3f newPos = projectile->getPosition() + direction * duration * speed;
|
||||
|
||||
update(magicBoltState, duration);
|
||||
|
||||
|
@ -425,41 +420,7 @@ namespace MWWorld
|
|||
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
||||
projectile->setValidTargets(targetActors);
|
||||
|
||||
// Check for impact
|
||||
// TODO: use a proper btRigidBody / btGhostObject?
|
||||
const auto result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, magicBoltState.mProjectileId);
|
||||
|
||||
bool hit = false;
|
||||
if (result.mHit)
|
||||
{
|
||||
hit = true;
|
||||
if (result.mHitObject.isEmpty())
|
||||
{
|
||||
// terrain or projectile
|
||||
}
|
||||
else
|
||||
{
|
||||
MWMechanics::CastSpell cast(caster, result.mHitObject);
|
||||
cast.mHitPosition = pos;
|
||||
cast.mId = magicBoltState.mSpellId;
|
||||
cast.mSourceName = magicBoltState.mSourceName;
|
||||
cast.mStack = false;
|
||||
cast.inflict(result.mHitObject, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
|
||||
mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
||||
}
|
||||
}
|
||||
|
||||
// Explodes when hitting water
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos))
|
||||
hit = true;
|
||||
|
||||
if (hit)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, result.mHitObject,
|
||||
ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
||||
|
||||
cleanupMagicBolt(magicBoltState);
|
||||
}
|
||||
mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -467,9 +428,6 @@ namespace MWWorld
|
|||
{
|
||||
for (auto& projectileState : mProjectiles)
|
||||
{
|
||||
if (projectileState.mToDelete)
|
||||
continue;
|
||||
|
||||
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||
if (!projectile->isActive())
|
||||
continue;
|
||||
|
@ -477,8 +435,7 @@ namespace MWWorld
|
|||
// simulating aerodynamics at all
|
||||
projectileState.mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration;
|
||||
|
||||
osg::Vec3f pos(projectileState.mNode->getPosition());
|
||||
osg::Vec3f newPos = pos + projectileState.mVelocity * duration;
|
||||
osg::Vec3f newPos = projectile->getPosition() + projectileState.mVelocity * duration;
|
||||
|
||||
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.
|
||||
if (!projectileState.mThrown)
|
||||
|
@ -488,10 +445,6 @@ namespace MWWorld
|
|||
projectileState.mNode->setAttitude(orient);
|
||||
}
|
||||
|
||||
projectileState.mNode->setPosition(newPos);
|
||||
|
||||
mPhysics->updateProjectile(projectileState.mProjectileId, newPos);
|
||||
|
||||
update(projectileState, duration);
|
||||
|
||||
MWWorld::Ptr caster = projectileState.getCaster();
|
||||
|
@ -502,36 +455,7 @@ namespace MWWorld
|
|||
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
||||
projectile->setValidTargets(targetActors);
|
||||
|
||||
// Check for impact
|
||||
// TODO: use a proper btRigidBody / btGhostObject?
|
||||
const auto result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, projectileState.mProjectileId);
|
||||
|
||||
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos);
|
||||
|
||||
if (result.mHit || underwater)
|
||||
{
|
||||
// Try to get a Ptr to the bow that was used. It might no longer exist.
|
||||
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), projectileState.mIdArrow);
|
||||
MWWorld::Ptr bow = projectileRef.getPtr();
|
||||
if (!caster.isEmpty() && projectileState.mIdArrow != projectileState.mBowId)
|
||||
{
|
||||
MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
|
||||
MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
|
||||
bow = *invIt;
|
||||
}
|
||||
|
||||
if (caster.isEmpty())
|
||||
caster = result.mHitObject;
|
||||
|
||||
MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHit ? result.mHitPos : newPos, projectileState.mAttackStrength);
|
||||
mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
||||
|
||||
if (underwater)
|
||||
mRendering->emitWaterRipple(newPos);
|
||||
|
||||
cleanupProjectile(projectileState);
|
||||
}
|
||||
mPhysics->updateProjectile(projectileState.mProjectileId, newPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -543,17 +467,19 @@ namespace MWWorld
|
|||
continue;
|
||||
|
||||
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||
|
||||
if (const auto hitWaterPos = projectile->getWaterHitPosition())
|
||||
mRendering->emitWaterRipple(Misc::Convert::toOsg(*hitWaterPos));
|
||||
|
||||
const auto pos = projectile->getPosition();
|
||||
projectileState.mNode->setPosition(pos);
|
||||
|
||||
if (projectile->isActive())
|
||||
continue;
|
||||
|
||||
const auto target = projectile->getTarget();
|
||||
const auto pos = projectile->getHitPos();
|
||||
MWWorld::Ptr caster = projectileState.getCaster();
|
||||
auto caster = projectileState.getCaster();
|
||||
assert(target != caster);
|
||||
if (!projectile->isValidTarget(target))
|
||||
{
|
||||
projectile->activate();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (caster.isEmpty())
|
||||
caster = target;
|
||||
|
@ -569,9 +495,8 @@ namespace MWWorld
|
|||
bow = *invIt;
|
||||
}
|
||||
|
||||
projectileState.mHitPosition = pos;
|
||||
cleanupProjectile(projectileState);
|
||||
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
||||
cleanupProjectile(projectileState);
|
||||
}
|
||||
for (auto& magicBoltState : mMagicBolts)
|
||||
{
|
||||
|
@ -579,20 +504,18 @@ namespace MWWorld
|
|||
continue;
|
||||
|
||||
auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
||||
|
||||
const auto pos = projectile->getPosition();
|
||||
magicBoltState.mNode->setPosition(pos);
|
||||
for (const auto& sound : magicBoltState.mSounds)
|
||||
sound->setPosition(pos);
|
||||
|
||||
if (projectile->isActive())
|
||||
continue;
|
||||
const auto target = projectile->getTarget();
|
||||
const auto pos = projectile->getHitPos();
|
||||
MWWorld::Ptr caster = magicBoltState.getCaster();
|
||||
assert(target != caster);
|
||||
if (!projectile->isValidTarget(target))
|
||||
{
|
||||
projectile->activate();
|
||||
continue;
|
||||
}
|
||||
|
||||
magicBoltState.mHitPosition = pos;
|
||||
cleanupMagicBolt(magicBoltState);
|
||||
const auto target = projectile->getTarget();
|
||||
const auto caster = magicBoltState.getCaster();
|
||||
assert(target != caster);
|
||||
|
||||
MWMechanics::CastSpell cast(caster, target);
|
||||
cast.mHitPosition = pos;
|
||||
|
@ -602,6 +525,7 @@ namespace MWWorld
|
|||
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);
|
||||
cleanupMagicBolt(magicBoltState);
|
||||
}
|
||||
mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }),
|
||||
mProjectiles.end());
|
||||
|
@ -702,7 +626,7 @@ namespace MWWorld
|
|||
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
||||
|
||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
|
||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), 1.f, true);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -747,7 +671,6 @@ namespace MWWorld
|
|||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
|
||||
MWWorld::Ptr ptr = ref.getPtr();
|
||||
model = ptr.getClass().getModel(ptr);
|
||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -755,7 +678,8 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
||||
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
|
||||
const auto radius = createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
|
||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), radius, false);
|
||||
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
for (const std::string &soundid : state.mSoundIds)
|
||||
|
|
|
@ -80,8 +80,6 @@ namespace MWWorld
|
|||
int mActorId;
|
||||
int mProjectileId;
|
||||
|
||||
osg::Vec3f mHitPosition;
|
||||
|
||||
// TODO: this will break when the game is saved and reloaded, since there is currently
|
||||
// no way to write identifiers for non-actors to a savegame.
|
||||
MWWorld::Ptr mCasterHandle;
|
||||
|
@ -132,7 +130,7 @@ namespace MWWorld
|
|||
void moveProjectiles(float dt);
|
||||
void moveMagicBolts(float dt);
|
||||
|
||||
void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient,
|
||||
float createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient,
|
||||
bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = "");
|
||||
void update (State& state, float duration);
|
||||
|
||||
|
|
Loading…
Reference in a new issue