mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 08:45:33 +00:00
Generate physics collisions for projectiles (bug #3372)
Remove redundant now mHit field
This commit is contained in:
parent
c2933721c6
commit
dc7b48e92e
17 changed files with 372 additions and 26 deletions
|
@ -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;
|
||||
|
||||
|
|
64
apps/openmw/mwphysics/projectile.cpp
Normal file
64
apps/openmw/mwphysics/projectile.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
68
apps/openmw/mwphysics/projectile.hpp
Normal file
68
apps/openmw/mwphysics/projectile.hpp
Normal file
|
@ -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…
Reference in a new issue