2014-04-18 09:03:36 +10:00
|
|
|
#include "obstacle.hpp"
|
|
|
|
|
2017-07-31 19:28:02 +04:00
|
|
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
2015-07-25 04:14:22 +02:00
|
|
|
|
2014-04-18 09:03:36 +10:00
|
|
|
#include "../mwworld/class.hpp"
|
|
|
|
#include "../mwworld/cellstore.hpp"
|
|
|
|
|
2015-08-30 10:06:09 +12:00
|
|
|
#include "movement.hpp"
|
|
|
|
|
2014-04-18 09:03:36 +10:00
|
|
|
namespace MWMechanics
|
|
|
|
{
|
|
|
|
// NOTE: determined empirically but probably need further tweaking
|
2015-11-09 20:31:40 +01:00
|
|
|
static const float DIST_SAME_SPOT = 0.5f;
|
|
|
|
static const float DURATION_SAME_SPOT = 1.5f;
|
2014-04-18 09:03:36 +10:00
|
|
|
static const float DURATION_TO_EVADE = 0.4f;
|
|
|
|
|
2015-09-19 16:14:00 +12: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 12:44:18 +04:00
|
|
|
bool proximityToDoor(const MWWorld::Ptr& actor, float minDist)
|
2014-05-13 03:58:32 -04:00
|
|
|
{
|
2017-12-01 10:07:02 +04:00
|
|
|
if(getNearbyDoor(actor, minDist).isEmpty())
|
2014-05-13 03:58:32 -04:00
|
|
|
return false;
|
2017-12-01 10:07:02 +04:00
|
|
|
else
|
|
|
|
return true;
|
2014-05-13 03:58:32 -04:00
|
|
|
}
|
|
|
|
|
2017-12-01 10:07:02 +04:00
|
|
|
const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist)
|
2014-04-18 09:03:36 +10:00
|
|
|
{
|
2017-07-31 19:28:02 +04:00
|
|
|
MWWorld::CellStore *cell = actor.getCell();
|
2017-06-14 12:44:18 +04:00
|
|
|
|
2017-07-31 19:28:02 +04:00
|
|
|
// Check all the doors in this cell
|
|
|
|
const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors();
|
|
|
|
osg::Vec3f pos(actor.getRefData().getPosition().asVec3());
|
|
|
|
pos.z() = 0;
|
2014-04-18 09:03:36 +10:00
|
|
|
|
2017-07-31 19:28:02 +04:00
|
|
|
osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0));
|
2014-04-18 09:03:36 +10:00
|
|
|
|
2020-07-25 15:54:49 +04:00
|
|
|
for (const auto& ref : doors.mList)
|
2017-07-31 19:28:02 +04:00
|
|
|
{
|
|
|
|
osg::Vec3f doorPos(ref.mData.getPosition().asVec3());
|
2017-12-01 10:07:02 +04:00
|
|
|
|
|
|
|
// FIXME: cast
|
|
|
|
const MWWorld::Ptr doorPtr = MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell());
|
|
|
|
|
2019-08-25 15:20:14 +02:00
|
|
|
const auto doorState = doorPtr.getClass().getDoorState(doorPtr);
|
2017-12-01 10:07:02 +04:00
|
|
|
float doorRot = ref.mData.getPosition().rot[2] - doorPtr.getCellRef().getPosition().rot[2];
|
|
|
|
|
2019-08-25 15:20:14 +02:00
|
|
|
if (doorState != MWWorld::DoorState::Idle || doorRot != 0)
|
2017-12-01 10:07:02 +04:00
|
|
|
continue; // the door is already opened/opening
|
|
|
|
|
2017-07-31 19:28:02 +04:00
|
|
|
doorPos.z() = 0;
|
|
|
|
|
|
|
|
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;
|
2017-06-14 12:44:18 +04:00
|
|
|
|
2017-07-31 19:28:02 +04:00
|
|
|
// Door is not close enough
|
|
|
|
if ((pos - doorPos).length2() > minDist*minDist)
|
|
|
|
continue;
|
2014-04-18 09:03:36 +10:00
|
|
|
|
2017-12-01 10:07:02 +04:00
|
|
|
return doorPtr; // found, stop searching
|
2017-07-31 19:28:02 +04:00
|
|
|
}
|
2014-04-18 09:03:36 +10:00
|
|
|
|
2017-07-31 19:28:02 +04:00
|
|
|
return MWWorld::Ptr(); // none found
|
2014-04-18 09:03:36 +10:00
|
|
|
}
|
|
|
|
|
2019-08-17 17:57:28 +02:00
|
|
|
ObstacleCheck::ObstacleCheck()
|
2020-01-20 22:08:25 +01:00
|
|
|
: mWalkState(WalkState::Initial)
|
|
|
|
, mStateDuration(0)
|
2015-09-19 16:14:00 +12:00
|
|
|
, mEvadeDirectionIndex(0)
|
2014-04-18 09:03:36 +10:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void ObstacleCheck::clear()
|
|
|
|
{
|
2020-01-20 22:08:25 +01:00
|
|
|
mWalkState = WalkState::Initial;
|
2014-04-18 09:03:36 +10:00
|
|
|
}
|
|
|
|
|
2016-08-19 22:15:26 +03:00
|
|
|
bool ObstacleCheck::isEvading() const
|
|
|
|
{
|
2020-01-20 22:08:25 +01:00
|
|
|
return mWalkState == WalkState::Evade;
|
2016-08-19 22:15:26 +03:00
|
|
|
}
|
|
|
|
|
2014-04-18 09:03:36 +10:00
|
|
|
/*
|
|
|
|
* input - actor, duration (time since last check)
|
|
|
|
* output - true if evasive action needs to be taken
|
|
|
|
*
|
2020-01-20 22:08:25 +01:00
|
|
|
* Walking state transitions (player greeting check not shown):
|
2014-04-18 09:03:36 +10:00
|
|
|
*
|
2020-01-20 22:08:25 +01:00
|
|
|
* Initial ----> Norm <--------> CheckStuck -------> Evade ---+
|
|
|
|
* ^ ^ | f ^ | t ^ | |
|
|
|
|
* | | | | | | | |
|
|
|
|
* | +-+ +---+ +---+ | u
|
|
|
|
* | any < t < u |
|
|
|
|
* +---------------------------------------------+
|
2014-04-18 09:03:36 +10:00
|
|
|
*
|
|
|
|
* f = one reaction time
|
|
|
|
* t = how long before considered stuck
|
|
|
|
* u = how long to move sideways
|
|
|
|
*
|
|
|
|
*/
|
2020-01-20 22:08:25 +01:00
|
|
|
void ObstacleCheck::update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration)
|
2014-04-18 09:03:36 +10:00
|
|
|
{
|
2020-01-20 22:08:25 +01:00
|
|
|
const auto position = actor.getRefData().getPosition().asVec3();
|
2014-04-18 09:03:36 +10:00
|
|
|
|
2020-01-20 22:08:25 +01:00
|
|
|
if (mWalkState == WalkState::Initial)
|
|
|
|
{
|
|
|
|
mWalkState = WalkState::Norm;
|
|
|
|
mStateDuration = 0;
|
|
|
|
mPrev = position;
|
2020-02-06 00:20:55 +01:00
|
|
|
mInitialDistance = (destination - position).length();
|
2020-01-20 22:08:25 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-08-17 17:55:49 +02:00
|
|
|
|
2020-01-20 22:08:25 +01:00
|
|
|
if (mWalkState != WalkState::Evade)
|
|
|
|
{
|
2020-08-27 11:48:59 +00:00
|
|
|
const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getCurrentSpeed(actor) * duration;
|
2020-01-20 22:08:25 +01:00
|
|
|
const float prevDistance = (destination - mPrev).length();
|
|
|
|
const float currentDistance = (destination - position).length();
|
|
|
|
const float movedDistance = prevDistance - currentDistance;
|
2020-02-06 00:20:55 +01:00
|
|
|
const float movedFromInitialDistance = mInitialDistance - currentDistance;
|
2014-04-18 09:03:36 +10:00
|
|
|
|
2020-01-20 22:08:25 +01:00
|
|
|
mPrev = position;
|
2014-04-18 09:03:36 +10:00
|
|
|
|
2020-02-06 00:20:55 +01:00
|
|
|
if (movedDistance >= distSameSpot && movedFromInitialDistance >= distSameSpot)
|
2020-01-20 22:08:25 +01:00
|
|
|
{
|
|
|
|
mWalkState = WalkState::Norm;
|
|
|
|
mStateDuration = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mWalkState == WalkState::Norm)
|
2014-04-18 09:03:36 +10:00
|
|
|
{
|
2020-01-20 22:08:25 +01:00
|
|
|
mWalkState = WalkState::CheckStuck;
|
|
|
|
mStateDuration = duration;
|
2020-02-06 00:20:55 +01:00
|
|
|
mInitialDistance = (destination - position).length();
|
2019-11-19 15:34:21 +04:00
|
|
|
return;
|
2014-04-18 09:03:36 +10:00
|
|
|
}
|
2019-11-19 15:34:21 +04:00
|
|
|
|
2020-01-20 22:08:25 +01:00
|
|
|
mStateDuration += duration;
|
|
|
|
if (mStateDuration < DURATION_SAME_SPOT)
|
2014-04-18 09:03:36 +10:00
|
|
|
{
|
2020-01-20 22:08:25 +01:00
|
|
|
return;
|
2014-04-18 09:03:36 +10:00
|
|
|
}
|
2020-01-20 22:08:25 +01:00
|
|
|
|
|
|
|
mWalkState = WalkState::Evade;
|
|
|
|
mStateDuration = 0;
|
|
|
|
chooseEvasionDirection();
|
|
|
|
return;
|
2019-11-19 15:34:21 +04:00
|
|
|
}
|
|
|
|
|
2020-01-20 22:08:25 +01:00
|
|
|
mStateDuration += duration;
|
|
|
|
if(mStateDuration >= DURATION_TO_EVADE)
|
2019-11-19 15:34:21 +04:00
|
|
|
{
|
|
|
|
// tried to evade, assume all is ok and start again
|
2020-01-20 22:08:25 +01:00
|
|
|
mWalkState = WalkState::Norm;
|
|
|
|
mStateDuration = 0;
|
|
|
|
mPrev = position;
|
2014-04-18 09:03:36 +10:00
|
|
|
}
|
|
|
|
}
|
2015-08-30 10:06:09 +12:00
|
|
|
|
2018-08-18 23:17:52 +03:00
|
|
|
void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement) const
|
2015-08-30 10:06:09 +12:00
|
|
|
{
|
2015-09-19 16:14:00 +12:00
|
|
|
actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex][0];
|
|
|
|
actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex][1];
|
2015-08-30 10:06:09 +12:00
|
|
|
}
|
|
|
|
|
2015-11-09 20:26:18 +01:00
|
|
|
void ObstacleCheck::chooseEvasionDirection()
|
2015-08-30 10:06:09 +12:00
|
|
|
{
|
|
|
|
// change direction if attempt didn't work
|
2015-11-09 20:26:18 +01:00
|
|
|
++mEvadeDirectionIndex;
|
|
|
|
if (mEvadeDirectionIndex == NUM_EVADE_DIRECTIONS)
|
2015-08-30 10:06:09 +12:00
|
|
|
{
|
2015-11-09 20:26:18 +01:00
|
|
|
mEvadeDirectionIndex = 0;
|
2015-08-30 10:06:09 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-18 09:03:36 +10:00
|
|
|
}
|