Generate physics collisions for projectiles (bug #3372)

Remove redundant now mHit field
recast_test_fix_c17
Andrei Kortunov 5 years ago committed by Frederic Chardon
parent c2933721c6
commit dc7b48e92e

@ -8,6 +8,7 @@
Bug #2473: Unable to overstock merchants
Bug #2798: Mutable ESM records
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
Bug #3372: Projectiles and magic bolts go through moving targets
Bug #3676: NiParticleColorModifier isn't applied properly
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
Bug #3789: Crash in visitEffectSources while in battle

@ -66,13 +66,13 @@ add_openmw_dir (mwworld
cells localscripts customdata inventorystore ptr actionopen actionread actionharvest
actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
contentloader esmloader actiontrap cellreflist cellref weather projectilemanager
cellpreloader datetimemanager
)
add_openmw_dir (mwphysics
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
closestnotmeconvexresultcallback raycasting mtphysics
)

@ -610,6 +610,8 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0;
virtual void manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos) = 0;
virtual void rest(double hours) = 0;
virtual void rechargeItems(double duration, bool activeOnly) = 0;

@ -70,7 +70,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
mCollisionObject->setCollisionShape(mShape.get());
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(this));
mCollisionObject->setUserPointer(this);
updateRotation();
updateScale();

@ -2,6 +2,14 @@
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
#include <components/misc/convert.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "collisiontype.hpp"
#include "projectile.hpp"
namespace MWPhysics
{
ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot)
@ -10,11 +18,23 @@ namespace MWPhysics
{
}
btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
{
if (convexResult.m_hitCollisionObject == mMe)
return btScalar(1);
if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile)
{
Projectile* projectileHolder = static_cast<Projectile*>(convexResult.m_hitCollisionObject->getUserPointer());
int projectileId = projectileHolder->getProjectileId();
PtrHolder* targetHolder = static_cast<PtrHolder*>(mMe->getUserPointer());
const MWWorld::Ptr target = targetHolder->getPtr();
osg::Vec3f pos = Misc::Convert::makeOsgVec3f(convexResult.m_hitPointLocal);
MWBase::Environment::get().getWorld()->manualProjectileHit(projectileId, target, pos);
return btScalar(1);
}
btVector3 hitNormalWorld;
if (normalInWorldSpace)
hitNormalWorld = convexResult.m_hitNormalLocal;

@ -6,13 +6,14 @@
#include "../mwworld/class.hpp"
#include "projectile.hpp"
#include "ptrholder.hpp"
namespace MWPhysics
{
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3& from, const btVector3& to)
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3& from, const btVector3& to, int projId)
: btCollisionWorld::ClosestRayResultCallback(from, to)
, mMe(me), mTargets(targets)
, mMe(me), mTargets(targets), mProjectileId(projId)
{
}
@ -29,6 +30,15 @@ namespace MWPhysics
return 1.f;
}
}
if (mProjectileId >= 0)
{
PtrHolder* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
Projectile* projectile = dynamic_cast<Projectile*>(holder);
if (projectile && projectile->getProjectileId() == mProjectileId)
return 1.f;
}
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
}
}

@ -12,12 +12,13 @@ namespace MWPhysics
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
{
public:
ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3& from, const btVector3& to);
ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*>& targets, const btVector3& from, const btVector3& to, int projId=-1);
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
private:
const btCollisionObject* mMe;
const std::vector<const btCollisionObject*> mTargets;
const int mProjectileId;
};
}

@ -24,7 +24,7 @@ namespace MWPhysics
mCollisionObject.reset(new btCollisionObject);
mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape());
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(this));
mCollisionObject->setUserPointer(this);
setScale(ptr.getCellRef().getScale());
setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude()));

@ -46,6 +46,8 @@
#include "collisiontype.hpp"
#include "actor.hpp"
#include "projectile.hpp"
#include "trace.h"
#include "object.hpp"
#include "heightfield.hpp"
@ -64,6 +66,7 @@ namespace MWPhysics
, mResourceSystem(resourceSystem)
, mDebugDrawEnabled(false)
, mTimeAccum(0.0f)
, mProjectileId(0)
, mWaterHeight(0)
, mWaterEnabled(false)
, mParentNode(parentNode)
@ -113,6 +116,10 @@ namespace MWPhysics
mObjects.clear();
mActors.clear();
for (ProjectileMap::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{
delete it->second;
}
}
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
@ -248,7 +255,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) const
PhysicsSystem::RayResult 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
{
if (from == to)
{
@ -285,7 +292,7 @@ namespace MWPhysics
}
}
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo);
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo, projId);
resultCallback.m_collisionFilterGroup = group;
resultCallback.m_collisionFilterMask = mask;
@ -298,7 +305,17 @@ namespace MWPhysics
result.mHitPos = Misc::Convert::toOsg(resultCallback.m_hitPointWorld);
result.mHitNormal = Misc::Convert::toOsg(resultCallback.m_hitNormalWorld);
if (PtrHolder* ptrHolder = static_cast<PtrHolder*>(resultCallback.m_collisionObject->getUserPointer()))
{
result.mHitObject = ptrHolder->getPtr();
if (group == CollisionType_Projectile)
{
Projectile* projectile = static_cast<Projectile*>(ptrHolder);
result.mProjectileId = projectile->getProjectileId();
}
else
result.mProjectileId = -1;
}
}
return result;
}
@ -502,6 +519,19 @@ namespace MWPhysics
}
}
void PhysicsSystem::removeProjectile(const int projectileId)
{
ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId);
if (foundProjectile != mProjectiles.end())
{
delete foundProjectile->second;
mProjectiles.erase(foundProjectile);
if (projectileId == mProjectileId)
mProjectileId--;
}
}
void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)
{
ObjectMap::iterator found = mObjects.find(old);
@ -572,6 +602,17 @@ namespace MWPhysics
}
}
void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position)
{
ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId);
if (foundProjectile != mProjectiles.end())
{
foundProjectile->second->setPosition(position);
mCollisionWorld->updateSingleAabb(foundProjectile->second->getCollisionObject());
return;
}
}
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
{
ObjectMap::iterator found = mObjects.find(ptr);
@ -632,6 +673,15 @@ namespace MWPhysics
mActors.emplace(ptr, std::move(actor));
}
int PhysicsSystem::addProjectile (const osg::Vec3f& position)
{
mProjectileId++;
Projectile* projectile = new Projectile(mProjectileId, position, mCollisionWorld);
mProjectiles.insert(std::make_pair(mProjectileId, projectile));
return mProjectileId;
}
bool PhysicsSystem::toggleCollisionMode()
{
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());

@ -56,6 +56,7 @@ namespace MWPhysics
class Object;
class Actor;
class PhysicsTaskScheduler;
class Projectile;
using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>;
@ -127,6 +128,10 @@ 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 osg::Vec3f& position);
void updateProjectile(const int projectileId, const osg::Vec3f &position);
void removeProjectile(const int projectileId);
void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
Actor* getActor(const MWWorld::Ptr& ptr);
@ -141,7 +146,6 @@ namespace MWPhysics
void updateRotation (const MWWorld::Ptr& ptr);
void updatePosition (const MWWorld::Ptr& ptr);
void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject);
void removeHeightField (int x, int y);
@ -169,10 +173,19 @@ namespace MWPhysics
/// \note Only Actor targets are supported at the moment.
float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override;
struct RayResult
{
bool mHit;
osg::Vec3f mHitPos;
osg::Vec3f mHitNormal;
MWWorld::Ptr mHitObject;
int mProjectileId;
};
/// @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) const override;
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const override;
RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;
@ -269,6 +282,9 @@ namespace MWPhysics
ActorMap mActors;
using ProjectileMap = std::map<int, Projectile *>;
ProjectileMap mProjectiles;
using HeightFieldMap = std::map<std::pair<int, int>, HeightField *>;
HeightFieldMap mHeightFields;
@ -279,6 +295,8 @@ namespace MWPhysics
float mTimeAccum;
int mProjectileId;
float mWaterHeight;
bool mWaterEnabled;

@ -0,0 +1,64 @@
#include "projectile.hpp"
#include <BulletCollision/CollisionShapes/btSphereShape.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/resource/bulletshape.hpp>
#include <components/debug/debuglog.hpp>
#include <components/misc/convert.hpp>
#include "../mwworld/class.hpp"
#include "collisiontype.hpp"
namespace MWPhysics
{
Projectile::Projectile(int projectileId, const osg::Vec3f& position, btCollisionWorld* world)
: mCollisionWorld(world)
{
mProjectileId = projectileId;
mShape.reset(new btSphereShape(1.f));
mConvexShape = static_cast<btConvexShape*>(mShape.get());
mCollisionObject.reset(new btCollisionObject);
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
mCollisionObject->setCollisionShape(mShape.get());
mCollisionObject->setUserPointer(this);
setPosition(position);
const int collisionMask = CollisionType_World | CollisionType_HeightMap |
CollisionType_Actor | CollisionType_Door | CollisionType_Water;
mCollisionWorld->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask);
}
Projectile::~Projectile()
{
if (mCollisionObject.get())
mCollisionWorld->removeCollisionObject(mCollisionObject.get());
}
void Projectile::updateCollisionObjectPosition()
{
btTransform tr = mCollisionObject->getWorldTransform();
// osg::Vec3f scaledTranslation = mRotation * mMeshTranslation;
// osg::Vec3f newPosition = scaledTranslation + mPosition;
tr.setOrigin(Misc::Convert::toBullet(mPosition));
mCollisionObject->setWorldTransform(tr);
}
void Projectile::setPosition(const osg::Vec3f &position)
{
mPosition = position;
updateCollisionObjectPosition();
}
osg::Vec3f Projectile::getPosition() const
{
return mPosition;
}
}

@ -0,0 +1,68 @@
#ifndef OPENMW_MWPHYSICS_PROJECTILE_H
#define OPENMW_MWPHYSICS_PROJECTILE_H
#include <memory>
#include "ptrholder.hpp"
#include <osg/Vec3f>
#include <osg/Quat>
#include <osg/ref_ptr>
class btCollisionWorld;
class btCollisionShape;
class btCollisionObject;
class btConvexShape;
namespace Resource
{
class BulletShape;
}
namespace MWPhysics
{
class Projectile : public PtrHolder
{
public:
Projectile(const int projectileId, const osg::Vec3f& position, btCollisionWorld* world);
~Projectile();
btConvexShape* getConvexShape() const { return mConvexShape; }
void updateCollisionObjectPosition();
void setPosition(const osg::Vec3f& position);
osg::Vec3f getPosition() const;
btCollisionObject* getCollisionObject() const
{
return mCollisionObject.get();
}
int getProjectileId() const
{
return mProjectileId;
}
private:
std::unique_ptr<btCollisionShape> mShape;
btConvexShape* mConvexShape;
std::unique_ptr<btCollisionObject> mCollisionObject;
osg::Vec3f mPosition;
btCollisionWorld* mCollisionWorld;
Projectile(const Projectile&);
Projectile& operator=(const Projectile&);
int mProjectileId;
};
}
#endif

@ -5,6 +5,9 @@
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/CollisionShapes/btConvexShape.h>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "collisiontype.hpp"
#include "actor.hpp"
#include "closestnotmeconvexresultcallback.hpp"

@ -302,6 +302,7 @@ namespace MWWorld
MWWorld::Ptr ptr = ref.getPtr();
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
@ -312,7 +313,8 @@ namespace MWWorld
if (sound)
state.mSounds.push_back(sound);
}
state.mProjectileId = mPhysics->addProjectile(pos);
mMagicBolts.push_back(state);
}
@ -325,7 +327,6 @@ namespace MWWorld
state.mIdArrow = projectile.getCellRef().getRefId();
state.mCasterHandle = actor;
state.mAttackStrength = attackStrength;
int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;
@ -336,6 +337,7 @@ namespace MWWorld
if (!ptr.getClass().getEnchantment(ptr).empty())
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
state.mProjectileId = mPhysics->addProjectile(pos);
mProjectiles.push_back(state);
}
@ -416,6 +418,8 @@ namespace MWWorld
it->mNode->setPosition(newPos);
mPhysics->updateProjectile(it->mProjectileId, newPos);
update(*it, duration);
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
@ -425,7 +429,7 @@ namespace MWWorld
// Check for impact
// TODO: use a proper btRigidBody / btGhostObject?
MWPhysics::RayCastingResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, it->mProjectileId);
bool hit = false;
if (result.mHit)
@ -433,7 +437,7 @@ namespace MWWorld
hit = true;
if (result.mHitObject.isEmpty())
{
// terrain
// terrain or projectile
}
else
{
@ -456,11 +460,7 @@ namespace MWWorld
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, result.mHitObject,
ESM::RT_Target, it->mSpellId, it->mSourceName);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++)
sndMgr->stopSound(it->mSounds.at(soundIter));
mParent->removeChild(it->mNode);
cleanupMagicBolt(*it);
it = mMagicBolts.erase(it);
continue;
@ -491,6 +491,8 @@ namespace MWWorld
it->mNode->setPosition(newPos);
mPhysics->updateProjectile(it->mProjectileId, newPos);
update(*it, duration);
MWWorld::Ptr caster = it->getCaster();
@ -502,15 +504,14 @@ namespace MWWorld
// Check for impact
// TODO: use a proper btRigidBody / btGhostObject?
MWPhysics::RayCastingResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, it->mProjectileId);
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos);
if (result.mHit || underwater)
{
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow);
// Try to get a Ptr to the bow that was used. It might no longer exist.
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow);
MWWorld::Ptr bow = projectileRef.getPtr();
if (!caster.isEmpty() && it->mIdArrow != it->mBowId)
{
@ -529,8 +530,9 @@ namespace MWWorld
if (underwater)
mRendering->emitWaterRipple(newPos);
mParent->removeChild(it->mNode);
cleanupProjectile(*it);
it = mProjectiles.erase(it);
continue;
}
@ -538,14 +540,105 @@ namespace MWWorld
}
}
void ProjectileManager::manualHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos)
{
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{
if (it->mProjectileId == projectileId)
{
MWWorld::Ptr caster = it->getCaster();
if (caster == target)
return;
if (!isValidTarget(caster, target))
return;
if (caster.isEmpty())
caster = target;
// Try to get a Ptr to the bow that was used. It might no longer exist.
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow);
MWWorld::Ptr bow = projectileRef.getPtr();
if (!caster.isEmpty() && it->mIdArrow != it->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(), it->mBowId))
bow = *invIt;
}
it->mHitPosition = pos;
cleanupProjectile(*it);
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, it->mAttackStrength);
mProjectiles.erase(it);
return;
}
}
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
{
if (it->mProjectileId == projectileId)
{
MWWorld::Ptr caster = it->getCaster();
if (caster == target)
return;
if (!isValidTarget(caster, target))
return;
it->mHitPosition = pos;
cleanupMagicBolt(*it);
MWMechanics::CastSpell cast(caster, target);
cast.mHitPosition = pos;
cast.mId = it->mSpellId;
cast.mSourceName = it->mSourceName;
cast.mStack = false;
cast.inflict(target, caster, it->mEffects, ESM::RT_Target, false, true);
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, target, ESM::RT_Target, it->mSpellId, it->mSourceName);
mMagicBolts.erase(it);
return;
}
}
}
bool ProjectileManager::isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
{
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
std::vector<MWWorld::Ptr> targetActors;
if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())
{
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
if (!targetActors.empty())
{
bool validTarget = false;
for (MWWorld::Ptr& targetActor : targetActors)
{
if (targetActor == target)
{
validTarget = true;
break;
}
}
return validTarget;
}
}
return true;
}
void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state)
{
mParent->removeChild(state.mNode);
mPhysics->removeProjectile(state.mProjectileId);
}
void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)
{
mParent->removeChild(state.mNode);
mPhysics->removeProjectile(state.mProjectileId);
for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)
{
MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter));
@ -626,9 +719,10 @@ namespace MWWorld
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
state.mProjectileId = mPhysics->addProjectile(osg::Vec3f(esm.mPosition));
}
catch(...)
{
@ -672,6 +766,7 @@ 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(osg::Vec3f(esm.mPosition));
}
catch(...)
{

@ -56,6 +56,8 @@ namespace MWWorld
void update(float dt);
void manualHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos);
/// Removes all current projectiles. Should be called when switching to a new worldspace.
void clear();
@ -76,6 +78,9 @@ namespace MWWorld
std::shared_ptr<MWRender::EffectAnimationTime> mEffectAnimationTime;
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.
@ -125,6 +130,8 @@ namespace MWWorld
void moveProjectiles(float dt);
void moveMagicBolts(float dt);
bool isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
void 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);

@ -843,6 +843,11 @@ namespace MWWorld
}
}
void World::manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos)
{
mProjectileManager->manualHit(projectileId, target, pos);
}
void World::disable (const Ptr& reference)
{
if (!reference.getRefData().isEnabled())

@ -584,6 +584,8 @@ namespace MWWorld
RestPermitted canRest() const override;
///< check if the player is allowed to rest
void manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos);
void rest(double hours) override;
void rechargeItems(double duration, bool activeOnly) override;

Loading…
Cancel
Save