mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 07:23:54 +00:00
Merge pull request #1201 from Allofich/hit
Prevent AI actors from hitting unintended targets
This commit is contained in:
commit
cdf65ef681
10 changed files with 121 additions and 31 deletions
|
@ -255,7 +255,7 @@ namespace MWBase
|
||||||
/// Returns a pointer to the object the provided object would hit (if within the
|
/// Returns a pointer to the object the provided object would hit (if within the
|
||||||
/// specified distance), and the point where the hit occurs. This will attempt to
|
/// specified distance), and the point where the hit occurs. This will attempt to
|
||||||
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
|
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
|
||||||
virtual std::pair<MWWorld::Ptr,osg::Vec3f> getHitContact(const MWWorld::ConstPtr &ptr, float distance) = 0;
|
virtual std::pair<MWWorld::Ptr,osg::Vec3f> getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector<MWWorld::Ptr> &targets) = 0;
|
||||||
|
|
||||||
virtual void adjustPosition (const MWWorld::Ptr& ptr, bool force) = 0;
|
virtual void adjustPosition (const MWWorld::Ptr& ptr, bool force) = 0;
|
||||||
///< Adjust position after load to be on ground. Must be called after model load.
|
///< Adjust position after load to be on ground. Must be called after model load.
|
||||||
|
|
|
@ -249,7 +249,12 @@ namespace MWClass
|
||||||
if (!weapon.isEmpty())
|
if (!weapon.isEmpty())
|
||||||
dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;
|
dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;
|
||||||
|
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist);
|
// 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 (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer())
|
||||||
|
ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors);
|
||||||
|
|
||||||
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);
|
||||||
if (result.first.isEmpty())
|
if (result.first.isEmpty())
|
||||||
return; // Didn't hit anything
|
return; // Didn't hit anything
|
||||||
|
|
||||||
|
|
|
@ -566,8 +566,13 @@ namespace MWClass
|
||||||
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
||||||
store.find("fHandToHandReach")->getFloat());
|
store.find("fHandToHandReach")->getFloat());
|
||||||
|
|
||||||
|
// 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 (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer())
|
||||||
|
ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors);
|
||||||
|
|
||||||
// TODO: Use second to work out the hit angle
|
// TODO: Use second to work out the hit angle
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = world->getHitContact(ptr, dist);
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = world->getHitContact(ptr, dist, targetActors);
|
||||||
MWWorld::Ptr victim = result.first;
|
MWWorld::Ptr victim = result.first;
|
||||||
osg::Vec3f hitPosition (result.second);
|
osg::Vec3f hitPosition (result.second);
|
||||||
if(victim.isEmpty()) // Didn't hit anything
|
if(victim.isEmpty()) // Didn't hit anything
|
||||||
|
|
|
@ -75,6 +75,17 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
|
||||||
return !targetActor.isEmpty();
|
return !targetActor.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const
|
||||||
|
{
|
||||||
|
for (std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat)
|
||||||
|
targetActors.push_back((*it)->getTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
return !targetActors.empty();
|
||||||
|
}
|
||||||
|
|
||||||
std::list<AiPackage*>::const_iterator AiSequence::begin() const
|
std::list<AiPackage*>::const_iterator AiSequence::begin() const
|
||||||
{
|
{
|
||||||
return mPackages.begin();
|
return mPackages.begin();
|
||||||
|
|
|
@ -79,6 +79,9 @@ namespace MWMechanics
|
||||||
/// Return true and assign target if combat package is currently active, return false otherwise
|
/// Return true and assign target if combat package is currently active, return false otherwise
|
||||||
bool getCombatTarget (MWWorld::Ptr &targetActor) const;
|
bool getCombatTarget (MWWorld::Ptr &targetActor) const;
|
||||||
|
|
||||||
|
/// Return true and assign targets for all combat packages, or return false if there are no combat packages
|
||||||
|
bool getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const;
|
||||||
|
|
||||||
/// Is there any combat package?
|
/// Is there any combat package?
|
||||||
bool isInCombat () const;
|
bool isInCombat () const;
|
||||||
|
|
||||||
|
|
|
@ -798,6 +798,7 @@ namespace MWPhysics
|
||||||
class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback
|
class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback
|
||||||
{
|
{
|
||||||
const btCollisionObject* mMe;
|
const btCollisionObject* mMe;
|
||||||
|
const std::vector<const btCollisionObject*> mTargets;
|
||||||
|
|
||||||
// Store the real origin, since the shape's origin is its center
|
// Store the real origin, since the shape's origin is its center
|
||||||
btVector3 mOrigin;
|
btVector3 mOrigin;
|
||||||
|
@ -807,8 +808,8 @@ namespace MWPhysics
|
||||||
btVector3 mContactPoint;
|
btVector3 mContactPoint;
|
||||||
btScalar mLeastDistSqr;
|
btScalar mLeastDistSqr;
|
||||||
|
|
||||||
DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const btVector3 &origin)
|
DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*> targets, const btVector3 &origin)
|
||||||
: mMe(me), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0),
|
: mMe(me), mTargets(targets), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0),
|
||||||
mLeastDistSqr(std::numeric_limits<float>::max())
|
mLeastDistSqr(std::numeric_limits<float>::max())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
@ -819,6 +820,16 @@ namespace MWPhysics
|
||||||
const btCollisionObject* collisionObject = col1Wrap->m_collisionObject;
|
const btCollisionObject* collisionObject = col1Wrap->m_collisionObject;
|
||||||
if (collisionObject != mMe)
|
if (collisionObject != mMe)
|
||||||
{
|
{
|
||||||
|
if (!mTargets.empty())
|
||||||
|
{
|
||||||
|
if ((std::find(mTargets.begin(), mTargets.end(), collisionObject) == mTargets.end()))
|
||||||
|
{
|
||||||
|
PtrHolder* holder = static_cast<PtrHolder*>(collisionObject->getUserPointer());
|
||||||
|
if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
|
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
|
||||||
if(!mObject || distsqr < mLeastDistSqr)
|
if(!mObject || distsqr < mLeastDistSqr)
|
||||||
{
|
{
|
||||||
|
@ -835,7 +846,7 @@ namespace MWPhysics
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor,
|
std::pair<MWWorld::Ptr, osg::Vec3f> PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor,
|
||||||
const osg::Vec3f &origin,
|
const osg::Vec3f &origin,
|
||||||
const osg::Quat &orient,
|
const osg::Quat &orient,
|
||||||
float queryDistance)
|
float queryDistance, std::vector<MWWorld::Ptr> targets)
|
||||||
{
|
{
|
||||||
const MWWorld::Store<ESM::GameSetting> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
const MWWorld::Store<ESM::GameSetting> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
|
@ -852,11 +863,23 @@ namespace MWPhysics
|
||||||
object.setWorldTransform(btTransform(toBullet(orient), toBullet(center)));
|
object.setWorldTransform(btTransform(toBullet(orient), toBullet(center)));
|
||||||
|
|
||||||
const btCollisionObject* me = NULL;
|
const btCollisionObject* me = NULL;
|
||||||
|
std::vector<const btCollisionObject*> targetCollisionObjects;
|
||||||
|
|
||||||
const Actor* physactor = getActor(actor);
|
const Actor* physactor = getActor(actor);
|
||||||
if (physactor)
|
if (physactor)
|
||||||
me = physactor->getCollisionObject();
|
me = physactor->getCollisionObject();
|
||||||
|
|
||||||
DeepestNotMeContactTestResultCallback resultCallback(me, toBullet(origin));
|
if (!targets.empty())
|
||||||
|
{
|
||||||
|
for (std::vector<MWWorld::Ptr>::const_iterator it = targets.begin(); it != targets.end(); ++it)
|
||||||
|
{
|
||||||
|
const Actor* physactor2 = getActor(*it);
|
||||||
|
if (physactor2)
|
||||||
|
targetCollisionObjects.push_back(physactor2->getCollisionObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeepestNotMeContactTestResultCallback resultCallback(me, targetCollisionObjects, toBullet(origin));
|
||||||
resultCallback.m_collisionFilterGroup = CollisionType_Actor;
|
resultCallback.m_collisionFilterGroup = CollisionType_Actor;
|
||||||
resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;
|
resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;
|
||||||
mCollisionWorld->contactTest(&object, resultCallback);
|
mCollisionWorld->contactTest(&object, resultCallback);
|
||||||
|
@ -903,28 +926,40 @@ namespace MWPhysics
|
||||||
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClosestNotMeRayResultCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to)
|
ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to)
|
||||||
: btCollisionWorld::ClosestRayResultCallback(from, to)
|
: btCollisionWorld::ClosestRayResultCallback(from, to)
|
||||||
, mMe(me)
|
, mMe(me), mTargets(targets)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
|
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
|
||||||
{
|
{
|
||||||
if (rayResult.m_collisionObject == mMe)
|
if (rayResult.m_collisionObject == mMe)
|
||||||
return 1.f;
|
return 1.f;
|
||||||
|
if (!mTargets.empty())
|
||||||
|
{
|
||||||
|
if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))
|
||||||
|
{
|
||||||
|
PtrHolder* holder = static_cast<PtrHolder*>(rayResult.m_collisionObject->getUserPointer());
|
||||||
|
if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor())
|
||||||
|
return 1.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
const btCollisionObject* mMe;
|
const btCollisionObject* mMe;
|
||||||
|
const std::vector<const btCollisionObject*> mTargets;
|
||||||
};
|
};
|
||||||
|
|
||||||
PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore, int mask, int group) const
|
PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore, std::vector<MWWorld::Ptr> targets, int mask, int group) const
|
||||||
{
|
{
|
||||||
btVector3 btFrom = toBullet(from);
|
btVector3 btFrom = toBullet(from);
|
||||||
btVector3 btTo = toBullet(to);
|
btVector3 btTo = toBullet(to);
|
||||||
|
|
||||||
const btCollisionObject* me = NULL;
|
const btCollisionObject* me = NULL;
|
||||||
|
std::vector<const btCollisionObject*> targetCollisionObjects;
|
||||||
|
|
||||||
if (!ignore.isEmpty())
|
if (!ignore.isEmpty())
|
||||||
{
|
{
|
||||||
const Actor* actor = getActor(ignore);
|
const Actor* actor = getActor(ignore);
|
||||||
|
@ -938,7 +973,17 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosestNotMeRayResultCallback resultCallback(me, btFrom, btTo);
|
if (!targets.empty())
|
||||||
|
{
|
||||||
|
for (std::vector<MWWorld::Ptr>::const_iterator it = targets.begin(); it != targets.end(); ++it)
|
||||||
|
{
|
||||||
|
const Actor* actor = getActor(*it);
|
||||||
|
if (actor)
|
||||||
|
targetCollisionObjects.push_back(actor->getCollisionObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo);
|
||||||
resultCallback.m_collisionFilterGroup = group;
|
resultCallback.m_collisionFilterGroup = group;
|
||||||
resultCallback.m_collisionFilterMask = mask;
|
resultCallback.m_collisionFilterMask = mask;
|
||||||
|
|
||||||
|
@ -991,7 +1036,7 @@ namespace MWPhysics
|
||||||
osg::Vec3f pos1 (physactor1->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.9)); // eye level
|
osg::Vec3f pos1 (physactor1->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.9)); // eye level
|
||||||
osg::Vec3f pos2 (physactor2->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.9));
|
osg::Vec3f pos2 (physactor2->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.9));
|
||||||
|
|
||||||
RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door);
|
RayResult result = castRay(pos1, pos2, MWWorld::ConstPtr(), std::vector<MWWorld::Ptr>(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door);
|
||||||
|
|
||||||
return !result.mHit;
|
return !result.mHit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ namespace MWPhysics
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor,
|
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor,
|
||||||
const osg::Vec3f &origin,
|
const osg::Vec3f &origin,
|
||||||
const osg::Quat &orientation,
|
const osg::Quat &orientation,
|
||||||
float queryDistance);
|
float queryDistance, std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>());
|
||||||
|
|
||||||
|
|
||||||
/// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the
|
/// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the
|
||||||
|
@ -112,9 +112,10 @@ namespace MWPhysics
|
||||||
MWWorld::Ptr mHitObject;
|
MWWorld::Ptr mHitObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @param me Optional, a Ptr to ignore in the list of results
|
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors.
|
||||||
RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore = MWWorld::ConstPtr(), int mask =
|
RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore = MWWorld::ConstPtr(),
|
||||||
CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const;
|
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
||||||
|
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const;
|
||||||
|
|
||||||
RayResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius);
|
RayResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius);
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
#include <components/esm/esmwriter.hpp>
|
#include <components/esm/esmwriter.hpp>
|
||||||
#include <components/esm/projectilestate.hpp>
|
#include <components/esm/projectilestate.hpp>
|
||||||
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/controller.hpp>
|
#include <components/sceneutil/controller.hpp>
|
||||||
#include <components/sceneutil/visitor.hpp>
|
#include <components/sceneutil/visitor.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
|
@ -25,6 +27,7 @@
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/spellcasting.hpp"
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
#include "../mwmechanics/aipackage.hpp"
|
||||||
|
|
||||||
#include "../mwrender/effectmanager.hpp"
|
#include "../mwrender/effectmanager.hpp"
|
||||||
#include "../mwrender/animation.hpp"
|
#include "../mwrender/animation.hpp"
|
||||||
|
@ -49,8 +52,8 @@ namespace
|
||||||
const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||||
iter->mEffectID);
|
iter->mEffectID);
|
||||||
|
|
||||||
// All the projectiles should use the same speed. From observations in the
|
// Speed of multi-effect projectiles should be the average of the constituent effects,
|
||||||
// original engine, this seems to be the average of the constituent effects.
|
// based on observation of the original engine.
|
||||||
speed += magicEffect->mData.mSpeed;
|
speed += magicEffect->mData.mSpeed;
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
|
@ -337,9 +340,14 @@ namespace MWWorld
|
||||||
|
|
||||||
MWWorld::Ptr caster = it->getCaster();
|
MWWorld::Ptr caster = it->getCaster();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// Check for impact
|
// Check for impact
|
||||||
// TODO: use a proper btRigidBody / btGhostObject?
|
// TODO: use a proper btRigidBody / btGhostObject?
|
||||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, 0xff, MWPhysics::CollisionType_Projectile);
|
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
|
||||||
|
|
||||||
bool hit = false;
|
bool hit = false;
|
||||||
if (result.mHit)
|
if (result.mHit)
|
||||||
|
@ -404,11 +412,17 @@ namespace MWWorld
|
||||||
|
|
||||||
MWWorld::Ptr caster = it->getCaster();
|
MWWorld::Ptr caster = it->getCaster();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// Check for impact
|
// Check for impact
|
||||||
// TODO: use a proper btRigidBody / btGhostObject?
|
// TODO: use a proper btRigidBody / btGhostObject?
|
||||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, 0xff, MWPhysics::CollisionType_Projectile);
|
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
|
||||||
|
|
||||||
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);
|
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow);
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
#include <components/esm/esmwriter.hpp>
|
#include <components/esm/esmwriter.hpp>
|
||||||
#include <components/esm/cellid.hpp>
|
#include <components/esm/cellid.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include <components/files/collections.hpp>
|
#include <components/files/collections.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
|
@ -1064,7 +1065,7 @@ namespace MWWorld
|
||||||
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
|
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<MWWorld::Ptr,osg::Vec3f> World::getHitContact(const MWWorld::ConstPtr &ptr, float distance)
|
std::pair<MWWorld::Ptr,osg::Vec3f> World::getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector<MWWorld::Ptr> &targets)
|
||||||
{
|
{
|
||||||
const ESM::Position &posdata = ptr.getRefData().getPosition();
|
const ESM::Position &posdata = ptr.getRefData().getPosition();
|
||||||
|
|
||||||
|
@ -1084,7 +1085,7 @@ namespace MWWorld
|
||||||
distance += halfExtents.y();
|
distance += halfExtents.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance);
|
std::pair<MWWorld::Ptr,osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
|
||||||
if(result.first.isEmpty())
|
if(result.first.isEmpty())
|
||||||
return std::make_pair(MWWorld::Ptr(), osg::Vec3f());
|
return std::make_pair(MWWorld::Ptr(), osg::Vec3f());
|
||||||
|
|
||||||
|
@ -1450,7 +1451,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
osg::Vec3f a(x1,y1,z1);
|
osg::Vec3f a(x1,y1,z1);
|
||||||
osg::Vec3f b(x2,y2,z2);
|
osg::Vec3f b(x2,y2,z2);
|
||||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door);
|
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door);
|
||||||
return result.mHit;
|
return result.mHit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2443,7 +2444,7 @@ namespace MWWorld
|
||||||
if (includeWater) {
|
if (includeWater) {
|
||||||
collisionTypes |= MWPhysics::CollisionType_Water;
|
collisionTypes |= MWPhysics::CollisionType_Water;
|
||||||
}
|
}
|
||||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(from, to, MWWorld::Ptr(), collisionTypes);
|
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(from, to, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), collisionTypes);
|
||||||
|
|
||||||
if (!result.mHit)
|
if (!result.mHit)
|
||||||
return maxDist;
|
return maxDist;
|
||||||
|
@ -2664,12 +2665,17 @@ namespace MWWorld
|
||||||
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
||||||
osg::Vec3f dest = origin + direction * distance;
|
osg::Vec3f dest = origin + direction * distance;
|
||||||
|
|
||||||
|
// 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 (!actor.isEmpty() && actor != MWMechanics::getPlayer())
|
||||||
|
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
|
||||||
|
|
||||||
// For actor targets, we want to use bounding boxes (physics raycast).
|
// For actor targets, we want to use bounding boxes (physics raycast).
|
||||||
// This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise.
|
// This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise.
|
||||||
// For object targets, we want the detailed shapes (rendering raycast).
|
// For object targets, we want the detailed shapes (rendering raycast).
|
||||||
// If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.
|
// If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.
|
||||||
|
|
||||||
MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, MWPhysics::CollisionType_Actor);
|
MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, targetActors, MWPhysics::CollisionType_Actor);
|
||||||
|
|
||||||
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
|
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
|
||||||
|
|
||||||
|
@ -2681,7 +2687,7 @@ namespace MWWorld
|
||||||
if (result2.mHit)
|
if (result2.mHit)
|
||||||
dist2 = (origin - result2.mHitPointWorld).length();
|
dist2 = (origin - result2.mHitPointWorld).length();
|
||||||
|
|
||||||
if (dist1 <= dist2 && result1.mHit)
|
if (result1.mHit)
|
||||||
{
|
{
|
||||||
target = result1.mHitObject;
|
target = result1.mHitObject;
|
||||||
hitPosition = result1.mHitPos;
|
hitPosition = result1.mHitPos;
|
||||||
|
@ -2692,7 +2698,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
target = result2.mHitObject;
|
target = result2.mHitObject;
|
||||||
hitPosition = result2.mHitPointWorld;
|
hitPosition = result2.mHitPointWorld;
|
||||||
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && (target.getClass().isActor() || !target.getClass().canBeActivated(target)))
|
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target))
|
||||||
target = NULL;
|
target = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2702,7 +2708,7 @@ namespace MWWorld
|
||||||
if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats (target).getAiSequence().isInCombat())
|
if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats (target).getAiSequence().isInCombat())
|
||||||
{
|
{
|
||||||
distance = std::min (distance, getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat());
|
distance = std::min (distance, getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat());
|
||||||
if (distance < dist1 && distance < dist2)
|
if (distance < dist1)
|
||||||
target = NULL;
|
target = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -354,7 +354,7 @@ namespace MWWorld
|
||||||
/// Returns a pointer to the object the provided object would hit (if within the
|
/// Returns a pointer to the object the provided object would hit (if within the
|
||||||
/// specified distance), and the point where the hit occurs. This will attempt to
|
/// specified distance), and the point where the hit occurs. This will attempt to
|
||||||
/// use the "Head" node as a basis.
|
/// use the "Head" node as a basis.
|
||||||
virtual std::pair<MWWorld::Ptr,osg::Vec3f> getHitContact(const MWWorld::ConstPtr &ptr, float distance);
|
virtual std::pair<MWWorld::Ptr,osg::Vec3f> getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector<MWWorld::Ptr> &targets);
|
||||||
|
|
||||||
/// @note No-op for items in containers. Use ContainerStore::removeItem instead.
|
/// @note No-op for items in containers. Use ContainerStore::removeItem instead.
|
||||||
virtual void deleteObject (const Ptr& ptr);
|
virtual void deleteObject (const Ptr& ptr);
|
||||||
|
|
Loading…
Reference in a new issue