2014-04-17 23:03:36 +00:00
|
|
|
#include "obstacle.hpp"
|
|
|
|
|
2017-06-14 08:44:18 +00:00
|
|
|
#include <osg/Group>
|
|
|
|
|
2015-07-25 02:14:22 +00:00
|
|
|
#include <components/esm/loadcell.hpp>
|
|
|
|
|
2014-04-17 23:03:36 +00:00
|
|
|
#include "../mwbase/world.hpp"
|
2017-06-14 08:44:18 +00:00
|
|
|
#include "../mwbase/environment.hpp"
|
2014-04-17 23:03:36 +00:00
|
|
|
#include "../mwworld/class.hpp"
|
|
|
|
#include "../mwworld/cellstore.hpp"
|
|
|
|
|
2015-08-29 22:06:09 +00:00
|
|
|
#include "movement.hpp"
|
|
|
|
|
2014-04-17 23:03:36 +00:00
|
|
|
namespace MWMechanics
|
|
|
|
{
|
|
|
|
// NOTE: determined empirically but probably need further tweaking
|
2015-11-09 19:31:40 +00:00
|
|
|
static const float DIST_SAME_SPOT = 0.5f;
|
|
|
|
static const float DURATION_SAME_SPOT = 1.5f;
|
2014-04-17 23:03:36 +00:00
|
|
|
static const float DURATION_TO_EVADE = 0.4f;
|
|
|
|
|
2015-09-19 04:14:00 +00:00
|
|
|
const float ObstacleCheck::evadeDirections[NUM_EVADE_DIRECTIONS][2] =
|
|
|
|
{
|
|
|
|
{ 1.0f, 0.0f }, // move to side
|
|
|
|
{ 1.0f, -1.0f }, // move to side and backwards
|
|
|
|
{ -1.0f, 0.0f }, // move to other side
|
|
|
|
{ -1.0f, -1.0f } // move to side and backwards
|
|
|
|
};
|
|
|
|
|
2017-06-14 08:44:18 +00:00
|
|
|
bool proximityToDoor(const MWWorld::Ptr& actor, float minDist)
|
2014-05-13 07:58:32 +00:00
|
|
|
{
|
2017-06-14 08:44:18 +00:00
|
|
|
if(getNearbyDoor(actor, minDist)!=MWWorld::Ptr())
|
2014-05-13 07:58:32 +00:00
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-14 08:44:18 +00:00
|
|
|
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist)
|
2014-04-17 23:03:36 +00:00
|
|
|
{
|
2017-06-14 08:44:18 +00:00
|
|
|
osg::Vec3f origin = MWBase::Environment::get().getWorld()->getActorHeadTransform(actor).getTrans();
|
|
|
|
|
|
|
|
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
|
|
|
|
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0,0,-1));
|
2014-04-17 23:03:36 +00:00
|
|
|
|
2017-06-14 08:44:18 +00:00
|
|
|
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
|
|
|
osg::Vec3f dest = origin + direction * minDist;
|
2014-04-17 23:03:36 +00:00
|
|
|
|
2015-06-03 19:37:21 +00:00
|
|
|
osg::Vec3f pos(actor.getRefData().getPosition().asVec3());
|
2017-06-14 08:44:18 +00:00
|
|
|
MWPhysics::PhysicsSystem::RayResult result = MWBase::Environment::get().getWorld()->castRayTest(pos.x(), pos.y(), pos.z(), dest.x(), dest.y(), dest.z());
|
|
|
|
|
|
|
|
if (!result.mHit || result.mHitObject.isEmpty())
|
|
|
|
return MWWorld::Ptr(); // none found
|
2014-04-17 23:03:36 +00:00
|
|
|
|
2017-06-14 08:44:18 +00:00
|
|
|
if (result.mHitObject.getClass().getTypeName() == typeid(ESM::Door).name() && !result.mHitObject.getCellRef().getTeleport())
|
|
|
|
return result.mHitObject;
|
2014-04-17 23:03:36 +00:00
|
|
|
|
2017-06-14 08:44:18 +00:00
|
|
|
return MWWorld::Ptr();
|
2014-04-17 23:03:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ObstacleCheck::ObstacleCheck():
|
|
|
|
mPrevX(0) // to see if the moved since last time
|
|
|
|
, mPrevY(0)
|
|
|
|
, mWalkState(State_Norm)
|
|
|
|
, mStuckDuration(0)
|
|
|
|
, mEvadeDuration(0)
|
2015-05-01 00:24:27 +00:00
|
|
|
, mDistSameSpot(-1) // avoid calculating it each time
|
2015-09-19 04:14:00 +00:00
|
|
|
, mEvadeDirectionIndex(0)
|
2014-04-17 23:03:36 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void ObstacleCheck::clear()
|
|
|
|
{
|
|
|
|
mWalkState = State_Norm;
|
|
|
|
mStuckDuration = 0;
|
|
|
|
mEvadeDuration = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ObstacleCheck::isNormalState() const
|
|
|
|
{
|
|
|
|
return mWalkState == State_Norm;
|
|
|
|
}
|
|
|
|
|
2016-08-19 19:15:26 +00:00
|
|
|
bool ObstacleCheck::isEvading() const
|
|
|
|
{
|
|
|
|
return mWalkState == State_Evade;
|
|
|
|
}
|
|
|
|
|
2014-04-17 23:03:36 +00:00
|
|
|
/*
|
|
|
|
* input - actor, duration (time since last check)
|
|
|
|
* output - true if evasive action needs to be taken
|
|
|
|
*
|
|
|
|
* Walking state transitions (player greeting check not shown):
|
|
|
|
*
|
|
|
|
* MoveNow <------------------------------------+
|
|
|
|
* | d|
|
|
|
|
* | |
|
|
|
|
* +-> State_Norm <---> State_CheckStuck --> State_Evade
|
|
|
|
* ^ ^ | f ^ | t ^ | |
|
|
|
|
* | | | | | | | |
|
|
|
|
* | +---+ +---+ +---+ | u
|
|
|
|
* | any < t < u |
|
|
|
|
* +--------------------------------------------+
|
|
|
|
*
|
|
|
|
* f = one reaction time
|
|
|
|
* d = proximity to a closed door
|
|
|
|
* t = how long before considered stuck
|
|
|
|
* u = how long to move sideways
|
|
|
|
*
|
|
|
|
*/
|
2016-04-16 17:14:00 +00:00
|
|
|
bool ObstacleCheck::check(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance)
|
2014-04-17 23:03:36 +00:00
|
|
|
{
|
|
|
|
const MWWorld::Class& cls = actor.getClass();
|
|
|
|
ESM::Position pos = actor.getRefData().getPosition();
|
|
|
|
|
|
|
|
if(mDistSameSpot == -1)
|
2016-04-16 17:14:00 +00:00
|
|
|
mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor) * scaleMinimumDistance;
|
2015-11-09 19:21:25 +00:00
|
|
|
|
2016-02-26 12:15:41 +00:00
|
|
|
float distSameSpot = mDistSameSpot * duration;
|
2014-04-17 23:03:36 +00:00
|
|
|
|
2015-11-09 19:21:25 +00:00
|
|
|
bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot;
|
2015-11-09 19:17:21 +00:00
|
|
|
|
2014-04-17 23:03:36 +00:00
|
|
|
// update position
|
|
|
|
mPrevX = pos.pos[0];
|
|
|
|
mPrevY = pos.pos[1];
|
|
|
|
|
|
|
|
switch(mWalkState)
|
|
|
|
{
|
|
|
|
case State_Norm:
|
|
|
|
{
|
|
|
|
if(!samePosition)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
mWalkState = State_CheckStuck;
|
|
|
|
}
|
|
|
|
/* FALL THROUGH */
|
|
|
|
case State_CheckStuck:
|
|
|
|
{
|
|
|
|
if(!samePosition)
|
|
|
|
{
|
|
|
|
mWalkState = State_Norm;
|
|
|
|
mStuckDuration = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mStuckDuration += duration;
|
|
|
|
// consider stuck only if position unchanges for a period
|
|
|
|
if(mStuckDuration < DURATION_SAME_SPOT)
|
|
|
|
break; // still checking, note duration added to timer
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mStuckDuration = 0;
|
|
|
|
mWalkState = State_Evade;
|
2015-11-09 19:26:18 +00:00
|
|
|
chooseEvasionDirection();
|
2014-04-17 23:03:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* FALL THROUGH */
|
|
|
|
case State_Evade:
|
|
|
|
{
|
|
|
|
mEvadeDuration += duration;
|
|
|
|
if(mEvadeDuration < DURATION_TO_EVADE)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// tried to evade, assume all is ok and start again
|
|
|
|
mWalkState = State_Norm;
|
|
|
|
mEvadeDuration = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* NO DEFAULT CASE */
|
|
|
|
}
|
|
|
|
return false; // no obstacles to evade (yet)
|
|
|
|
}
|
2015-08-29 22:06:09 +00:00
|
|
|
|
|
|
|
void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement)
|
|
|
|
{
|
2015-09-19 04:14:00 +00:00
|
|
|
actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex][0];
|
|
|
|
actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex][1];
|
2015-08-29 22:06:09 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 19:26:18 +00:00
|
|
|
void ObstacleCheck::chooseEvasionDirection()
|
2015-08-29 22:06:09 +00:00
|
|
|
{
|
|
|
|
// change direction if attempt didn't work
|
2015-11-09 19:26:18 +00:00
|
|
|
++mEvadeDirectionIndex;
|
|
|
|
if (mEvadeDirectionIndex == NUM_EVADE_DIRECTIONS)
|
2015-08-29 22:06:09 +00:00
|
|
|
{
|
2015-11-09 19:26:18 +00:00
|
|
|
mEvadeDirectionIndex = 0;
|
2015-08-29 22:06:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-17 23:03:36 +00:00
|
|
|
}
|