Merge pull request #1324 from akortunov/doorfix

Improved doors detection
0.6.1
scrawl 8 years ago committed by GitHub
commit 333648c975

@ -176,7 +176,9 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
if (!mObstacleCheck.check(actor, duration)) return; if (!mObstacleCheck.check(actor, duration)) return;
// first check if obstacle is a door // first check if obstacle is a door
MWWorld::Ptr door = getNearbyDoor(actor); // NOTE: checks interior cells only static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
MWWorld::Ptr door = getNearbyDoor(actor, distance);
if (door != MWWorld::Ptr()) if (door != MWWorld::Ptr())
{ {
// note: AiWander currently does not open doors // note: AiWander currently does not open doors

@ -449,11 +449,14 @@ namespace MWMechanics
{ {
// Check if an idle actor is too close to a door - if so start walking // Check if an idle actor is too close to a door - if so start walking
storage.mDoorCheckDuration += duration; storage.mDoorCheckDuration += duration;
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
if (storage.mDoorCheckDuration >= DOOR_CHECK_INTERVAL) if (storage.mDoorCheckDuration >= DOOR_CHECK_INTERVAL)
{ {
storage.mDoorCheckDuration = 0; // restart timer storage.mDoorCheckDuration = 0; // restart timer
if (mDistance && // actor is not intended to be stationary if (mDistance && // actor is not intended to be stationary
proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6f*1.6f)) // NOTE: checks interior cells only proximityToDoor(actor, distance*1.6f))
{ {
storage.setState(Wander_MoveNow); storage.setState(Wander_MoveNow);
storage.mTrimCurrentNode = false; // just in case storage.mTrimCurrentNode = false; // just in case
@ -527,10 +530,12 @@ namespace MWMechanics
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos) void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
{ {
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
if (mObstacleCheck.isEvading()) if (mObstacleCheck.isEvading())
{ {
// first check if we're walking into a door // first check if we're walking into a door
if (proximityToDoor(actor)) // NOTE: checks interior cells only if (proximityToDoor(actor, distance))
{ {
// remove allowed points then select another random destination // remove allowed points then select another random destination
storage.mTrimCurrentNode = true; storage.mTrimCurrentNode = true;

@ -1,6 +1,7 @@
#include "obstacle.hpp" #include "obstacle.hpp"
#include <components/esm/loadcell.hpp> #include <components/esm/loadcell.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -23,50 +24,48 @@ namespace MWMechanics
{ -1.0f, -1.0f } // move to side and backwards { -1.0f, -1.0f } // move to side and backwards
}; };
// Proximity check function for interior doors. Given that most interior cells bool proximityToDoor(const MWWorld::Ptr& actor, float minDist)
// do not have many doors performance shouldn't be too much of an issue.
//
// Limitation: there can be false detections, and does not test whether the
// actor is facing the door.
bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr)
{ {
if(getNearbyDoor(actor, minSqr)!=MWWorld::Ptr()) if(getNearbyDoor(actor, minDist)!=MWWorld::Ptr())
return true; return true;
else else
return false; return false;
} }
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr) MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist)
{ {
MWWorld::CellStore *cell = actor.getCell(); MWWorld::CellStore *cell = actor.getCell();
if(cell->getCell()->isExterior())
return MWWorld::Ptr(); // check interior cells only
// Check all the doors in this cell // Check all the doors in this cell
const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors(); const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors();
const MWWorld::CellRefList<ESM::Door>::List& refList = doors.mList; const MWWorld::CellRefList<ESM::Door>::List& refList = doors.mList;
MWWorld::CellRefList<ESM::Door>::List::const_iterator it = refList.begin(); MWWorld::CellRefList<ESM::Door>::List::const_iterator it = refList.begin();
osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); osg::Vec3f pos(actor.getRefData().getPosition().asVec3());
pos.z() = 0;
/// TODO: How to check whether the actor is facing a door? Below code is for osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0));
/// the player, perhaps it can be adapted.
//MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject();
//if(!ptr.isEmpty())
//std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl;
/// TODO: The in-game observation of rot[2] value seems to be the
/// opposite of the code in World::activateDoor() ::confused::
for (; it != refList.end(); ++it) for (; it != refList.end(); ++it)
{ {
const MWWorld::LiveCellRef<ESM::Door>& ref = *it; const MWWorld::LiveCellRef<ESM::Door>& ref = *it;
if((pos - ref.mData.getPosition().asVec3()).length2() < minSqr
&& ref.mData.getPosition().rot[2] == ref.mRef.getPosition().rot[2]) osg::Vec3f doorPos(ref.mData.getPosition().asVec3());
{ doorPos.z() = 0;
// FIXME cast
return MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell()); // found, stop searching float angle = std::acos(actorDir * (doorPos - pos) / (actorDir.length() * (doorPos - pos).length()));
}
// Allow 60 degrees angle between actor and door
if (angle < -osg::PI / 3 || angle > osg::PI / 3)
continue;
// Door is not close enough
if ((pos - doorPos).length2() > minDist*minDist)
continue;
// FIXME cast
return MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell()); // found, stop searching
} }
return MWWorld::Ptr(); // none found return MWWorld::Ptr(); // none found
} }

@ -10,19 +10,14 @@ namespace MWMechanics
{ {
struct Movement; struct Movement;
/// NOTE: determined empirically based on in-game behaviour
static const float MIN_DIST_TO_DOOR_SQUARED = 128*128;
static const int NUM_EVADE_DIRECTIONS = 4; static const int NUM_EVADE_DIRECTIONS = 4;
/// tests actor's proximity to a closed door by default /// tests actor's proximity to a closed door by default
bool proximityToDoor(const MWWorld::Ptr& actor, bool proximityToDoor(const MWWorld::Ptr& actor, float minDist);
float minSqr = MIN_DIST_TO_DOOR_SQUARED);
/// Returns door pointer within range. No guarantee is given as to which one /// Returns door pointer within range. No guarantee is given as to which one
/** \return Pointer to the door, or NULL if none exists **/ /** \return Pointer to the door, or NULL if none exists **/
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist);
float minSqr = MIN_DIST_TO_DOOR_SQUARED);
class ObstacleCheck class ObstacleCheck
{ {

@ -1460,6 +1460,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(), std::vector<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;
} }

Loading…
Cancel
Save