Generate physics collisions for projectiles (bug #3372)

Remove redundant now mHit field
pull/593/head
Andrei Kortunov 6 years ago committed by Frederic Chardon
parent c2933721c6
commit dc7b48e92e

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

@ -66,13 +66,13 @@ add_openmw_dir (mwworld
cells localscripts customdata inventorystore ptr actionopen actionread actionharvest cells localscripts customdata inventorystore ptr actionopen actionread actionharvest
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor 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 cellpreloader datetimemanager
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
closestnotmeconvexresultcallback raycasting mtphysics closestnotmeconvexresultcallback raycasting mtphysics
) )

@ -610,6 +610,8 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0; 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 rest(double hours) = 0;
virtual void rechargeItems(double duration, bool activeOnly) = 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->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
mCollisionObject->setActivationState(DISABLE_DEACTIVATION); mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
mCollisionObject->setCollisionShape(mShape.get()); mCollisionObject->setCollisionShape(mShape.get());
mCollisionObject->setUserPointer(static_cast<PtrHolder*>(this)); mCollisionObject->setUserPointer(this);
updateRotation(); updateRotation();
updateScale(); updateScale();

@ -2,6 +2,14 @@
#include <BulletCollision/CollisionDispatch/btCollisionObject.h> #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 namespace MWPhysics
{ {
ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot) 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) if (convexResult.m_hitCollisionObject == mMe)
return btScalar(1); 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; btVector3 hitNormalWorld;
if (normalInWorldSpace) if (normalInWorldSpace)
hitNormalWorld = convexResult.m_hitNormalLocal; hitNormalWorld = convexResult.m_hitNormalLocal;

@ -6,13 +6,14 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "projectile.hpp"
#include "ptrholder.hpp" #include "ptrholder.hpp"
namespace MWPhysics 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) : btCollisionWorld::ClosestRayResultCallback(from, to)
, mMe(me), mTargets(targets) , mMe(me), mTargets(targets), mProjectileId(projId)
{ {
} }
@ -29,6 +30,15 @@ namespace MWPhysics
return 1.f; 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); return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
} }
} }

@ -12,12 +12,13 @@ namespace MWPhysics
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
{ {
public: 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; btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
private: private:
const btCollisionObject* mMe; const btCollisionObject* mMe;
const std::vector<const btCollisionObject*> mTargets; const std::vector<const btCollisionObject*> mTargets;
const int mProjectileId;
}; };
} }

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

@ -46,6 +46,8 @@
#include "collisiontype.hpp" #include "collisiontype.hpp"
#include "actor.hpp" #include "actor.hpp"
#include "projectile.hpp"
#include "trace.h" #include "trace.h"
#include "object.hpp" #include "object.hpp"
#include "heightfield.hpp" #include "heightfield.hpp"
@ -64,6 +66,7 @@ namespace MWPhysics
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)
, mDebugDrawEnabled(false) , mDebugDrawEnabled(false)
, mTimeAccum(0.0f) , mTimeAccum(0.0f)
, mProjectileId(0)
, mWaterHeight(0) , mWaterHeight(0)
, mWaterEnabled(false) , mWaterEnabled(false)
, mParentNode(parentNode) , mParentNode(parentNode)
@ -113,6 +116,10 @@ namespace MWPhysics
mObjects.clear(); mObjects.clear();
mActors.clear(); mActors.clear();
for (ProjectileMap::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{
delete it->second;
}
} }
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue) void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
@ -248,7 +255,7 @@ namespace MWPhysics
return 0.f; 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) 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_collisionFilterGroup = group;
resultCallback.m_collisionFilterMask = mask; resultCallback.m_collisionFilterMask = mask;
@ -298,7 +305,17 @@ namespace MWPhysics
result.mHitPos = Misc::Convert::toOsg(resultCallback.m_hitPointWorld); result.mHitPos = Misc::Convert::toOsg(resultCallback.m_hitPointWorld);
result.mHitNormal = Misc::Convert::toOsg(resultCallback.m_hitNormalWorld); result.mHitNormal = Misc::Convert::toOsg(resultCallback.m_hitNormalWorld);
if (PtrHolder* ptrHolder = static_cast<PtrHolder*>(resultCallback.m_collisionObject->getUserPointer())) if (PtrHolder* ptrHolder = static_cast<PtrHolder*>(resultCallback.m_collisionObject->getUserPointer()))
{
result.mHitObject = ptrHolder->getPtr(); result.mHitObject = ptrHolder->getPtr();
if (group == CollisionType_Projectile)
{
Projectile* projectile = static_cast<Projectile*>(ptrHolder);
result.mProjectileId = projectile->getProjectileId();
}
else
result.mProjectileId = -1;
}
} }
return result; 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) void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated)
{ {
ObjectMap::iterator found = mObjects.find(old); 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) void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
{ {
ObjectMap::iterator found = mObjects.find(ptr); ObjectMap::iterator found = mObjects.find(ptr);
@ -632,6 +673,15 @@ namespace MWPhysics
mActors.emplace(ptr, std::move(actor)); 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() bool PhysicsSystem::toggleCollisionMode()
{ {
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer()); ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());

@ -56,6 +56,7 @@ namespace MWPhysics
class Object; class Object;
class Actor; class Actor;
class PhysicsTaskScheduler; class PhysicsTaskScheduler;
class Projectile;
using ActorMap = std::map<MWWorld::ConstPtr, std::shared_ptr<Actor>>; 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 addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); 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); void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
Actor* getActor(const MWWorld::Ptr& ptr); Actor* getActor(const MWWorld::Ptr& ptr);
@ -141,7 +146,6 @@ namespace MWPhysics
void updateRotation (const MWWorld::Ptr& ptr); void updateRotation (const MWWorld::Ptr& ptr);
void updatePosition (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 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); void removeHeightField (int x, int y);
@ -169,10 +173,19 @@ namespace MWPhysics
/// \note Only Actor targets are supported at the moment. /// \note Only Actor targets are supported at the moment.
float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override; 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. /// @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(), 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>(), 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; RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;
@ -269,6 +282,9 @@ namespace MWPhysics
ActorMap mActors; ActorMap mActors;
using ProjectileMap = std::map<int, Projectile *>;
ProjectileMap mProjectiles;
using HeightFieldMap = std::map<std::pair<int, int>, HeightField *>; using HeightFieldMap = std::map<std::pair<int, int>, HeightField *>;
HeightFieldMap mHeightFields; HeightFieldMap mHeightFields;
@ -279,6 +295,8 @@ namespace MWPhysics
float mTimeAccum; float mTimeAccum;
int mProjectileId;
float mWaterHeight; float mWaterHeight;
bool mWaterEnabled; 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/CollisionDispatch/btCollisionWorld.h>
#include <BulletCollision/CollisionShapes/btConvexShape.h> #include <BulletCollision/CollisionShapes/btConvexShape.h>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "collisiontype.hpp" #include "collisiontype.hpp"
#include "actor.hpp" #include "actor.hpp"
#include "closestnotmeconvexresultcallback.hpp" #include "closestnotmeconvexresultcallback.hpp"

@ -302,6 +302,7 @@ namespace MWWorld
MWWorld::Ptr ptr = ref.getPtr(); MWWorld::Ptr ptr = ref.getPtr();
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects); osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture); createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
@ -312,7 +313,8 @@ namespace MWWorld
if (sound) if (sound)
state.mSounds.push_back(sound); state.mSounds.push_back(sound);
} }
state.mProjectileId = mPhysics->addProjectile(pos);
mMagicBolts.push_back(state); mMagicBolts.push_back(state);
} }
@ -325,7 +327,6 @@ namespace MWWorld
state.mIdArrow = projectile.getCellRef().getRefId(); state.mIdArrow = projectile.getCellRef().getRefId();
state.mCasterHandle = actor; state.mCasterHandle = actor;
state.mAttackStrength = attackStrength; state.mAttackStrength = attackStrength;
int type = projectile.get<ESM::Weapon>()->mBase->mData.mType; int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown; state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;
@ -336,6 +337,7 @@ namespace MWWorld
if (!ptr.getClass().getEnchantment(ptr).empty()) if (!ptr.getClass().getEnchantment(ptr).empty())
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
state.mProjectileId = mPhysics->addProjectile(pos);
mProjectiles.push_back(state); mProjectiles.push_back(state);
} }
@ -416,6 +418,8 @@ namespace MWWorld
it->mNode->setPosition(newPos); it->mNode->setPosition(newPos);
mPhysics->updateProjectile(it->mProjectileId, newPos);
update(*it, duration); update(*it, duration);
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. // 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 // Check for impact
// TODO: use a proper btRigidBody / btGhostObject? // 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; bool hit = false;
if (result.mHit) if (result.mHit)
@ -433,7 +437,7 @@ namespace MWWorld
hit = true; hit = true;
if (result.mHitObject.isEmpty()) if (result.mHitObject.isEmpty())
{ {
// terrain // terrain or projectile
} }
else else
{ {
@ -456,11 +460,7 @@ namespace MWWorld
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, result.mHitObject, MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, result.mHitObject,
ESM::RT_Target, it->mSpellId, it->mSourceName); ESM::RT_Target, it->mSpellId, it->mSourceName);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); cleanupMagicBolt(*it);
for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++)
sndMgr->stopSound(it->mSounds.at(soundIter));
mParent->removeChild(it->mNode);
it = mMagicBolts.erase(it); it = mMagicBolts.erase(it);
continue; continue;
@ -491,6 +491,8 @@ namespace MWWorld
it->mNode->setPosition(newPos); it->mNode->setPosition(newPos);
mPhysics->updateProjectile(it->mProjectileId, newPos);
update(*it, duration); update(*it, duration);
MWWorld::Ptr caster = it->getCaster(); MWWorld::Ptr caster = it->getCaster();
@ -502,15 +504,14 @@ namespace MWWorld
// Check for impact // Check for impact
// TODO: use a proper btRigidBody / btGhostObject? // 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); bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos);
if (result.mHit || underwater) 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. // 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(); MWWorld::Ptr bow = projectileRef.getPtr();
if (!caster.isEmpty() && it->mIdArrow != it->mBowId) if (!caster.isEmpty() && it->mIdArrow != it->mBowId)
{ {
@ -529,8 +530,9 @@ namespace MWWorld
if (underwater) if (underwater)
mRendering->emitWaterRipple(newPos); mRendering->emitWaterRipple(newPos);
mParent->removeChild(it->mNode); cleanupProjectile(*it);
it = mProjectiles.erase(it); it = mProjectiles.erase(it);
continue; 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) void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state)
{ {
mParent->removeChild(state.mNode); mParent->removeChild(state.mNode);
mPhysics->removeProjectile(state.mProjectileId);
} }
void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state) void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)
{ {
mParent->removeChild(state.mNode); mParent->removeChild(state.mNode);
mPhysics->removeProjectile(state.mProjectileId);
for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++) for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)
{ {
MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(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::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr(); MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr); model = ptr.getClass().getModel(ptr);
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType; int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown; state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
state.mProjectileId = mPhysics->addProjectile(osg::Vec3f(esm.mPosition));
} }
catch(...) catch(...)
{ {
@ -672,6 +766,7 @@ namespace MWWorld
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0)); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
MWWorld::Ptr ptr = ref.getPtr(); MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr); model = ptr.getClass().getModel(ptr);
state.mProjectileId = mPhysics->addProjectile(osg::Vec3f(esm.mPosition));
} }
catch(...) catch(...)
{ {

@ -56,6 +56,8 @@ namespace MWWorld
void update(float dt); 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. /// Removes all current projectiles. Should be called when switching to a new worldspace.
void clear(); void clear();
@ -76,6 +78,9 @@ namespace MWWorld
std::shared_ptr<MWRender::EffectAnimationTime> mEffectAnimationTime; std::shared_ptr<MWRender::EffectAnimationTime> mEffectAnimationTime;
int mActorId; int mActorId;
int mProjectileId;
osg::Vec3f mHitPosition;
// TODO: this will break when the game is saved and reloaded, since there is currently // 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. // no way to write identifiers for non-actors to a savegame.
@ -125,6 +130,8 @@ namespace MWWorld
void moveProjectiles(float dt); void moveProjectiles(float dt);
void moveMagicBolts(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, 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 = ""); bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = "");
void update (State& state, float duration); 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) void World::disable (const Ptr& reference)
{ {
if (!reference.getRefData().isEnabled()) if (!reference.getRefData().isEnabled())

@ -584,6 +584,8 @@ namespace MWWorld
RestPermitted canRest() const override; RestPermitted canRest() const override;
///< check if the player is allowed to rest ///< 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 rest(double hours) override;
void rechargeItems(double duration, bool activeOnly) override; void rechargeItems(double duration, bool activeOnly) override;

Loading…
Cancel
Save