mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-25 21:41:04 +00:00
Merge branch 'mobiusdoublereacharound' into 'master'
Rewrite melee hit target selection (bug #3438) Closes #3438 See merge request OpenMW/openmw!3274
This commit is contained in:
commit
84019e7998
@ -2,6 +2,7 @@
|
||||
------
|
||||
|
||||
Bug #2623: Snowy Granius doesn't prioritize conjuration spells
|
||||
Bug #3438: NPCs can't hit bull netch with melee weapons
|
||||
Bug #3842: Body part skeletons override the main skeleton
|
||||
Bug #4127: Weapon animation looks choppy
|
||||
Bug #4204: Dead slaughterfish doesn't float to water surface after loading saved game
|
||||
|
@ -252,13 +252,6 @@ namespace MWBase
|
||||
|
||||
virtual float getMaxActivationDistance() const = 0;
|
||||
|
||||
/// 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
|
||||
/// 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, std::vector<MWWorld::Ptr>& targets)
|
||||
= 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.
|
||||
/// @param force do this even if the ptr is flying
|
||||
@ -546,9 +539,6 @@ namespace MWBase
|
||||
const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat)
|
||||
= 0;
|
||||
|
||||
/// Return the distance between actor's weapon and target's collision box.
|
||||
virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;
|
||||
|
||||
virtual void addContainerScripts(const MWWorld::Ptr& reference, MWWorld::CellStore* cell) = 0;
|
||||
virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0;
|
||||
|
||||
|
@ -242,24 +242,10 @@ namespace MWClass
|
||||
if (!weapon.isEmpty())
|
||||
dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;
|
||||
|
||||
// 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;
|
||||
getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors);
|
||||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> result
|
||||
= MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);
|
||||
const std::pair<MWWorld::Ptr, osg::Vec3f> result = MWMechanics::getHitContact(ptr, dist);
|
||||
if (result.first.isEmpty()) // Didn't hit anything
|
||||
return true;
|
||||
|
||||
const MWWorld::Class& othercls = result.first.getClass();
|
||||
if (!othercls.isActor()) // Can't hit non-actors
|
||||
return true;
|
||||
|
||||
MWMechanics::CreatureStats& otherstats = othercls.getCreatureStats(result.first);
|
||||
if (otherstats.isDead()) // Can't hit dead actors
|
||||
return true;
|
||||
|
||||
// Note that earlier we returned true in spite of an apparent failure to hit anything alive.
|
||||
// This is because hitting nothing is not a "miss" and should be handled as such character controller-side.
|
||||
victim = result.first;
|
||||
|
@ -569,25 +569,10 @@ namespace MWClass
|
||||
* (!weapon.isEmpty() ? weapon.get<ESM::Weapon>()->mBase->mData.mReach
|
||||
: store.find("fHandToHandReach")->mValue.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 != MWMechanics::getPlayer())
|
||||
getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors);
|
||||
|
||||
// TODO: Use second to work out the hit angle
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = world->getHitContact(ptr, dist, targetActors);
|
||||
const std::pair<MWWorld::Ptr, osg::Vec3f> result = MWMechanics::getHitContact(ptr, dist);
|
||||
if (result.first.isEmpty()) // Didn't hit anything
|
||||
return true;
|
||||
|
||||
const MWWorld::Class& othercls = result.first.getClass();
|
||||
if (!othercls.isActor()) // Can't hit non-actors
|
||||
return true;
|
||||
|
||||
MWMechanics::CreatureStats& otherstats = othercls.getCreatureStats(result.first);
|
||||
if (otherstats.isDead()) // Can't hit dead actors
|
||||
return true;
|
||||
|
||||
// Note that earlier we returned true in spite of an apparent failure to hit anything alive.
|
||||
// This is because hitting nothing is not a "miss" and should be handled as such character controller-side.
|
||||
victim = result.first;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "actorutil.hpp"
|
||||
#include "aicombataction.hpp"
|
||||
#include "character.hpp"
|
||||
#include "combat.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "pathgrid.hpp"
|
||||
@ -242,7 +243,7 @@ namespace MWMechanics
|
||||
const osg::Vec3f vActorPos(pos.asVec3());
|
||||
const osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3());
|
||||
|
||||
float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);
|
||||
float distToTarget = getDistanceToBounds(actor, target);
|
||||
|
||||
storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS);
|
||||
|
||||
|
@ -551,4 +551,102 @@ namespace MWMechanics
|
||||
return distanceIgnoreZ(lhs, rhs);
|
||||
return distance(lhs, rhs);
|
||||
}
|
||||
|
||||
float getDistanceToBounds(const MWWorld::Ptr& actor, const MWWorld::Ptr& target)
|
||||
{
|
||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||
osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
|
||||
float dist = (targetPos - actorPos).length();
|
||||
dist -= world->getHalfExtents(actor).y();
|
||||
dist -= world->getHalfExtents(target).y();
|
||||
return dist;
|
||||
}
|
||||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach)
|
||||
{
|
||||
// Lasciate ogne speranza, voi ch'entrate
|
||||
MWWorld::Ptr result;
|
||||
osg::Vec3f hitPos;
|
||||
float minDist = std::numeric_limits<float>::max();
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
const ESM::Position& posdata = actor.getRefData().getPosition();
|
||||
const osg::Vec3f actorPos(posdata.asVec3());
|
||||
|
||||
// Morrowind uses body orientation or camera orientation if available
|
||||
// The difference between that and this is subtle
|
||||
osg::Quat actorRot
|
||||
= osg::Quat(posdata.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0, 0, -1));
|
||||
|
||||
const float fCombatAngleXY = store.find("fCombatAngleXY")->mValue.getFloat();
|
||||
const float fCombatAngleZ = store.find("fCombatAngleZ")->mValue.getFloat();
|
||||
const float combatAngleXYcos = std::cos(osg::DegreesToRadians(fCombatAngleXY));
|
||||
const float combatAngleZcos = std::cos(osg::DegreesToRadians(fCombatAngleZ));
|
||||
|
||||
// The player can target any active actor, non-playable actors only target their targets
|
||||
std::vector<MWWorld::Ptr> targets;
|
||||
if (actor != getPlayer())
|
||||
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targets);
|
||||
else
|
||||
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(
|
||||
actorPos, Settings::game().mActorsProcessingRange, targets);
|
||||
|
||||
for (MWWorld::Ptr& target : targets)
|
||||
{
|
||||
if (actor == target || target.getClass().getCreatureStats(target).isDead())
|
||||
continue;
|
||||
float dist = getDistanceToBounds(actor, target);
|
||||
osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
|
||||
osg::Vec3f dirToTarget = targetPos - actorPos;
|
||||
if (dist >= reach || dist >= minDist || std::abs(dirToTarget.z()) >= reach)
|
||||
continue;
|
||||
|
||||
dirToTarget.normalize();
|
||||
|
||||
// The idea is to use fCombatAngleXY and fCombatAngleZ as tolerance angles
|
||||
// in XY and YZ planes of the coordinate system where the actor's orientation
|
||||
// corresponds to (0, 1, 0) vector. This is not exactly what Morrowind does
|
||||
// but Morrowind does something (even more) stupid here
|
||||
osg::Vec3f hitDir = actorRot.inverse() * dirToTarget;
|
||||
if (combatAngleXYcos * std::abs(hitDir.x()) > hitDir.y())
|
||||
continue;
|
||||
|
||||
// Nice cliff racer hack Todd
|
||||
if (combatAngleZcos * std::abs(hitDir.z()) > hitDir.y() && !MWMechanics::canActorMoveByZAxis(target))
|
||||
continue;
|
||||
|
||||
// Gotta use physics somehow!
|
||||
if (!world->getLOS(actor, target))
|
||||
continue;
|
||||
|
||||
minDist = dist;
|
||||
result = target;
|
||||
}
|
||||
|
||||
// This hit position is currently used for spawning the blood effect.
|
||||
// Morrowind does this elsewhere, but roughly at the same time
|
||||
// and it would be hard to track the original hit results outside of this function
|
||||
// without code duplication
|
||||
// The idea is to use a random point on a plane in front of the target
|
||||
// that is defined by its width and height
|
||||
if (!result.isEmpty())
|
||||
{
|
||||
osg::Vec3f resultPos(result.getRefData().getPosition().asVec3());
|
||||
osg::Vec3f dirToActor = actorPos - resultPos;
|
||||
dirToActor.normalize();
|
||||
|
||||
hitPos = resultPos + dirToActor * world->getHalfExtents(result).y();
|
||||
// -25% to 25% of width
|
||||
float xOffset = Misc::Rng::deviate(0.f, 0.25f, world->getPrng());
|
||||
// 20% to 100% of height
|
||||
float zOffset = Misc::Rng::deviate(0.6f, 0.4f, world->getPrng());
|
||||
hitPos.x() += world->getHalfExtents(result).x() * 2.f * xOffset;
|
||||
hitPos.z() += world->getHalfExtents(result).z() * 2.f * zOffset;
|
||||
}
|
||||
|
||||
return std::make_pair(result, hitPos);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef OPENMW_MECHANICS_COMBAT_H
|
||||
#define OPENMW_MECHANICS_COMBAT_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Vec3f;
|
||||
@ -59,6 +61,11 @@ namespace MWMechanics
|
||||
|
||||
float getAggroDistance(const MWWorld::Ptr& actor, const osg::Vec3f& lhs, const osg::Vec3f& rhs);
|
||||
|
||||
// Cursed distance calculation used for combat proximity and hit checks in Morrowind
|
||||
float getDistanceToBounds(const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
|
||||
|
||||
// Similarly cursed hit target selection
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -192,93 +192,6 @@ namespace MWPhysics
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor,
|
||||
const osg::Vec3f& origin, const osg::Quat& orient, float queryDistance, std::vector<MWWorld::Ptr>& targets)
|
||||
{
|
||||
// First of all, try to hit where you aim to
|
||||
int hitmask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;
|
||||
RayCastingResult result = castRay(origin, origin + (orient * osg::Vec3f(0.0f, queryDistance, 0.0f)), actor,
|
||||
targets, hitmask, CollisionType_Actor);
|
||||
|
||||
if (result.mHit)
|
||||
{
|
||||
reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
||||
return std::make_pair(result.mHitObject, result.mHitPos);
|
||||
}
|
||||
|
||||
// Use cone shape as fallback
|
||||
const MWWorld::Store<ESM::GameSetting>& store
|
||||
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||
|
||||
btConeShape shape(osg::DegreesToRadians(store.find("fCombatAngleXY")->mValue.getFloat() / 2.0f), queryDistance);
|
||||
shape.setLocalScaling(btVector3(
|
||||
1, 1, osg::DegreesToRadians(store.find("fCombatAngleZ")->mValue.getFloat() / 2.0f) / shape.getRadius()));
|
||||
|
||||
// The shape origin is its center, so we have to move it forward by half the length. The
|
||||
// real origin will be provided to getFilteredContact to find the closest.
|
||||
osg::Vec3f center = origin + (orient * osg::Vec3f(0.0f, queryDistance * 0.5f, 0.0f));
|
||||
|
||||
btCollisionObject object;
|
||||
object.setCollisionShape(&shape);
|
||||
object.setWorldTransform(btTransform(Misc::Convert::toBullet(orient), Misc::Convert::toBullet(center)));
|
||||
|
||||
const btCollisionObject* me = nullptr;
|
||||
std::vector<const btCollisionObject*> targetCollisionObjects;
|
||||
|
||||
const Actor* physactor = getActor(actor);
|
||||
if (physactor)
|
||||
me = physactor->getCollisionObject();
|
||||
|
||||
if (!targets.empty())
|
||||
{
|
||||
for (MWWorld::Ptr& target : targets)
|
||||
{
|
||||
const Actor* targetActor = getActor(target);
|
||||
if (targetActor)
|
||||
targetCollisionObjects.push_back(targetActor->getCollisionObject());
|
||||
}
|
||||
}
|
||||
|
||||
DeepestNotMeContactTestResultCallback resultCallback(
|
||||
me, targetCollisionObjects, Misc::Convert::toBullet(origin));
|
||||
resultCallback.m_collisionFilterGroup = CollisionType_Actor;
|
||||
resultCallback.m_collisionFilterMask
|
||||
= CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;
|
||||
mTaskScheduler->contactTest(&object, resultCallback);
|
||||
|
||||
if (resultCallback.mObject)
|
||||
{
|
||||
PtrHolder* holder = static_cast<PtrHolder*>(resultCallback.mObject->getUserPointer());
|
||||
if (holder)
|
||||
{
|
||||
reportCollision(resultCallback.mContactPoint, resultCallback.mContactNormal);
|
||||
return std::make_pair(holder->getPtr(), Misc::Convert::toOsg(resultCallback.mContactPoint));
|
||||
}
|
||||
}
|
||||
return std::make_pair(MWWorld::Ptr(), osg::Vec3f());
|
||||
}
|
||||
|
||||
float PhysicsSystem::getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const
|
||||
{
|
||||
btCollisionObject* targetCollisionObj = nullptr;
|
||||
const Actor* actor = getActor(target);
|
||||
if (actor)
|
||||
targetCollisionObj = actor->getCollisionObject();
|
||||
if (!targetCollisionObj)
|
||||
return 0.f;
|
||||
|
||||
btTransform rayFrom;
|
||||
rayFrom.setIdentity();
|
||||
rayFrom.setOrigin(Misc::Convert::toBullet(point));
|
||||
|
||||
auto hitpoint = mTaskScheduler->getHitPoint(rayFrom, targetCollisionObj);
|
||||
if (hitpoint)
|
||||
return (point - Misc::Convert::toOsg(*hitpoint)).length();
|
||||
|
||||
// didn't hit the target. this could happen if point is already inside the collision box
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f& from, const osg::Vec3f& to,
|
||||
const MWWorld::ConstPtr& ignore, const std::vector<MWWorld::Ptr>& targets, int mask, int group) const
|
||||
{
|
||||
|
@ -207,15 +207,6 @@ namespace MWPhysics
|
||||
const MWWorld::ConstPtr& ptr, int collisionGroup, int collisionMask) const;
|
||||
osg::Vec3f traceDown(const MWWorld::Ptr& ptr, const osg::Vec3f& position, float maxHeight);
|
||||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f& origin,
|
||||
const osg::Quat& orientation, float queryDistance, std::vector<MWWorld::Ptr>& targets);
|
||||
|
||||
/// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the
|
||||
/// target vector hits the collision shape and then calculates distance from the intersection point.
|
||||
/// This can be used to find out how much nearer we need to move to the target for a "getHitContact" to be
|
||||
/// successful. \note Only Actor targets are supported at the moment.
|
||||
float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override;
|
||||
|
||||
/// @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,
|
||||
|
@ -23,12 +23,6 @@ namespace MWPhysics
|
||||
public:
|
||||
virtual ~RayCastingInterface() = default;
|
||||
|
||||
/// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the
|
||||
/// target vector hits the collision shape and then calculates distance from the intersection point.
|
||||
/// This can be used to find out how much nearer we need to move to the target for a "getHitContact" to be
|
||||
/// successful. \note Only Actor targets are supported at the moment.
|
||||
virtual float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const = 0;
|
||||
|
||||
/// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all
|
||||
/// other actors.
|
||||
virtual RayCastingResult castRay(const osg::Vec3f& from, const osg::Vec3f& to,
|
||||
|
@ -1037,44 +1037,6 @@ namespace MWWorld
|
||||
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
osg::Quat rot
|
||||
= osg::Quat(posdata.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0, 0, -1));
|
||||
|
||||
osg::Vec3f halfExtents = mPhysics->getHalfExtents(ptr);
|
||||
|
||||
// the origin of hitbox is an actor's front, not center
|
||||
distance += halfExtents.y();
|
||||
|
||||
// special cased for better aiming with the camera
|
||||
// if we do not hit anything, will use the default approach as fallback
|
||||
if (ptr == getPlayerPtr())
|
||||
{
|
||||
osg::Vec3f pos = getActorHeadTransform(ptr).getTrans();
|
||||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
|
||||
if (!result.first.isEmpty())
|
||||
return std::make_pair(result.first, result.second);
|
||||
}
|
||||
|
||||
osg::Vec3f pos = ptr.getRefData().getPosition().asVec3();
|
||||
|
||||
// general case, compatible with all types of different creatures
|
||||
// note: we intentionally do *not* use the collision box offset here, this is required to make
|
||||
// some flying creatures work that have their collision box offset in the air
|
||||
pos.z() += halfExtents.z();
|
||||
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
|
||||
if (result.first.isEmpty())
|
||||
return std::make_pair(MWWorld::Ptr(), osg::Vec3f());
|
||||
|
||||
return std::make_pair(result.first, result.second);
|
||||
}
|
||||
|
||||
void World::deleteObject(const Ptr& ptr)
|
||||
{
|
||||
if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == nullptr)
|
||||
@ -3010,12 +2972,12 @@ namespace MWWorld
|
||||
}
|
||||
else
|
||||
{
|
||||
// For actor targets, we want to use hit contact with bounding boxes.
|
||||
// For actor targets, we want to use melee hit contact.
|
||||
// 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). If we used the bounding boxes for static objects, then we would not be able to target e.g.
|
||||
// objects lying on a shelf.
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> result1 = getHitContact(actor, fCombatDistance, targetActors);
|
||||
const std::pair<Ptr, osg::Vec3f> result1 = MWMechanics::getHitContact(actor, fCombatDistance);
|
||||
|
||||
// Get the target to use for "on touch" effects, using the facing direction from Head node
|
||||
osg::Vec3f origin = getActorHeadTransform(actor).getTrans();
|
||||
@ -3728,15 +3690,6 @@ namespace MWWorld
|
||||
return (targetPos - weaponPos);
|
||||
}
|
||||
|
||||
float World::getHitDistance(const ConstPtr& actor, const ConstPtr& target)
|
||||
{
|
||||
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
|
||||
osg::Vec3f halfExtents = mPhysics->getHalfExtents(actor);
|
||||
weaponPos.z() += halfExtents.z();
|
||||
|
||||
return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y();
|
||||
}
|
||||
|
||||
void preload(MWWorld::Scene* scene, const ESMStore& store, const ESM::RefId& obj)
|
||||
{
|
||||
if (obj.empty())
|
||||
|
@ -345,12 +345,6 @@ namespace MWWorld
|
||||
|
||||
float getDistanceToFacedObject() override;
|
||||
|
||||
/// 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
|
||||
/// use the "Head" node as a basis.
|
||||
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(
|
||||
const MWWorld::ConstPtr& ptr, float distance, std::vector<MWWorld::Ptr>& targets) override;
|
||||
|
||||
/// @note No-op for items in containers. Use ContainerStore::removeItem instead.
|
||||
void deleteObject(const Ptr& ptr) override;
|
||||
|
||||
@ -627,9 +621,6 @@ namespace MWWorld
|
||||
osg::Vec3f aimToTarget(
|
||||
const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) override;
|
||||
|
||||
/// Return the distance between actor's weapon and target's collision box.
|
||||
float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) override;
|
||||
|
||||
bool isPlayerInJail() const override;
|
||||
|
||||
void setPlayerTraveling(bool traveling) override;
|
||||
|
Loading…
x
Reference in New Issue
Block a user