mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-22 23:09:43 +00:00
Merge remote-tracking branch 'dteviot/FixStuckDraft'
This commit is contained in:
commit
a8bee25757
7 changed files with 81 additions and 62 deletions
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
MWMechanics::AiPackage::~AiPackage() {}
|
MWMechanics::AiPackage::~AiPackage() {}
|
||||||
|
|
||||||
MWMechanics::AiPackage::AiPackage() : mTimer(0.26f), mStuckTimer(0) { //mTimer starts at .26 to force initial pathbuild
|
MWMechanics::AiPackage::AiPackage() : mTimer(0.26f) { //mTimer starts at .26 to force initial pathbuild
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po
|
||||||
{
|
{
|
||||||
//Update various Timers
|
//Update various Timers
|
||||||
mTimer += duration; //Update timer
|
mTimer += duration; //Update timer
|
||||||
mStuckTimer += duration; //Update stuck timer
|
|
||||||
|
|
||||||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
||||||
|
|
||||||
|
@ -91,40 +90,36 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po
|
||||||
//************************
|
//************************
|
||||||
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished?
|
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished?
|
||||||
return true;
|
return true;
|
||||||
else if(mStuckTimer>0.5) //Every half second see if we need to take action to avoid something
|
else
|
||||||
{
|
{
|
||||||
/// TODO (tluppi#1#): Use ObstacleCheck here. Not working for some reason
|
evadeObstacles(actor, duration, pos);
|
||||||
//if(mObstacleCheck.check(actor, duration)) {
|
|
||||||
if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < actor.getClass().getSpeed(actor)*0.05 && distance(dest, start) > 20) { //Actually stuck, and far enough away from destination to care
|
|
||||||
// first check if we're walking into a door
|
|
||||||
MWWorld::Ptr door = getNearbyDoor(actor);
|
|
||||||
if(door != MWWorld::Ptr()) // NOTE: checks interior cells only
|
|
||||||
{
|
|
||||||
if(!door.getCellRef().getTeleport() && door.getCellRef().getTrap().empty() && door.getClass().getDoorState(door) == 0) { //Open the door if untrapped
|
|
||||||
MWBase::Environment::get().getWorld()->activateDoor(door, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // probably walking into another NPC
|
|
||||||
{
|
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
|
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
|
||||||
// change the angle a bit, too
|
|
||||||
zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { //Not stuck, so reset things
|
|
||||||
mStuckTimer = 0;
|
|
||||||
mStuckPos = pos;
|
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward the rest of the time
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float duration, ESM::Position& pos)
|
||||||
|
{
|
||||||
zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]));
|
zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]));
|
||||||
|
|
||||||
return false;
|
MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);
|
||||||
|
if (mObstacleCheck.check(actor, duration))
|
||||||
|
{
|
||||||
|
// first check if we're walking into a door
|
||||||
|
MWWorld::Ptr door = getNearbyDoor(actor);
|
||||||
|
if (door != MWWorld::Ptr()) // NOTE: checks interior cells only
|
||||||
|
{
|
||||||
|
if (!door.getCellRef().getTeleport() && door.getCellRef().getTrap().empty() && door.getClass().getDoorState(door) == 0) { //Open the door if untrapped
|
||||||
|
MWBase::Environment::get().getWorld()->activateDoor(door, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // probably walking into another NPC
|
||||||
|
{
|
||||||
|
mObstacleCheck.takeEvasiveAction(movement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { //Not stuck, so reset things
|
||||||
|
movement.mPosition[1] = 1; //Just run forward
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MWMechanics::AiPackage::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell)
|
bool MWMechanics::AiPackage::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell)
|
||||||
|
|
|
@ -83,10 +83,12 @@ namespace MWMechanics
|
||||||
ObstacleCheck mObstacleCheck;
|
ObstacleCheck mObstacleCheck;
|
||||||
|
|
||||||
float mTimer;
|
float mTimer;
|
||||||
float mStuckTimer;
|
|
||||||
|
|
||||||
ESM::Position mStuckPos;
|
|
||||||
ESM::Pathgrid::Point mPrevDest;
|
ESM::Pathgrid::Point mPrevDest;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void evadeObstacles(const MWWorld::Ptr& actor, float duration, ESM::Position& pos);
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
static const int COUNT_BEFORE_RESET = 200; // TODO: maybe no longer needed
|
static const int COUNT_BEFORE_RESET = 10;
|
||||||
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
||||||
static const float REACTION_INTERVAL = 0.25f;
|
static const float REACTION_INTERVAL = 0.25f;
|
||||||
static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player
|
static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player
|
||||||
|
@ -381,11 +381,7 @@ namespace MWMechanics
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// have not yet reached the destination
|
// have not yet reached the destination
|
||||||
//... turn towards the next point in mPath
|
evadeObstacles(actor, storage, duration, pos);
|
||||||
zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]));
|
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
|
||||||
|
|
||||||
evadeObstacles(actor, storage, duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,8 +413,12 @@ namespace MWMechanics
|
||||||
storage.mState = Wander_IdleNow;
|
storage.mState = Wander_IdleNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration)
|
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
|
||||||
{
|
{
|
||||||
|
// turn towards the next point in mPath
|
||||||
|
zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]));
|
||||||
|
|
||||||
|
MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);
|
||||||
if (mObstacleCheck.check(actor, duration))
|
if (mObstacleCheck.check(actor, duration))
|
||||||
{
|
{
|
||||||
// first check if we're walking into a door
|
// first check if we're walking into a door
|
||||||
|
@ -435,16 +435,16 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
// TODO: diagonal should have same animation as walk forward
|
// TODO: diagonal should have same animation as walk forward
|
||||||
// but doesn't seem to do that?
|
// but doesn't seem to do that?
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
|
mObstacleCheck.takeEvasiveAction(movement);
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
|
|
||||||
// change the angle a bit, too
|
|
||||||
const ESM::Position& pos = actor.getRefData().getPosition();
|
|
||||||
zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]));
|
|
||||||
}
|
}
|
||||||
mStuckCount++; // TODO: maybe no longer needed
|
mStuckCount++; // TODO: maybe no longer needed
|
||||||
}
|
}
|
||||||
//#if 0
|
else
|
||||||
// TODO: maybe no longer needed
|
{
|
||||||
|
movement.mPosition[1] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if stuck for sufficiently long, act like current location was the destination
|
||||||
if (mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
|
if (mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
|
||||||
{
|
{
|
||||||
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
|
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
|
||||||
|
@ -454,7 +454,6 @@ namespace MWMechanics
|
||||||
storage.mState = Wander_ChooseAction;
|
storage.mState = Wander_ChooseAction;
|
||||||
mStuckCount = 0;
|
mStuckCount = 0;
|
||||||
}
|
}
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::playIdleDialogueRandomly(const MWWorld::Ptr& actor)
|
void AiWander::playIdleDialogueRandomly(const MWWorld::Ptr& actor)
|
||||||
|
|
|
@ -83,7 +83,7 @@ namespace MWMechanics
|
||||||
short unsigned getRandomIdle();
|
short unsigned getRandomIdle();
|
||||||
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
||||||
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||||
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration);
|
void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos);
|
||||||
void playIdleDialogueRandomly(const MWWorld::Ptr& actor);
|
void playIdleDialogueRandomly(const MWWorld::Ptr& actor);
|
||||||
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
||||||
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos);
|
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos);
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
||||||
|
#include "movement.hpp"
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
// NOTE: determined empirically but probably need further tweaking
|
// NOTE: determined empirically but probably need further tweaking
|
||||||
|
@ -67,6 +69,7 @@ namespace MWMechanics
|
||||||
, mStuckDuration(0)
|
, mStuckDuration(0)
|
||||||
, mEvadeDuration(0)
|
, mEvadeDuration(0)
|
||||||
, mDistSameSpot(-1) // avoid calculating it each time
|
, mDistSameSpot(-1) // avoid calculating it each time
|
||||||
|
, mEvadeDirection(1.0f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +158,7 @@ namespace MWMechanics
|
||||||
/* FALL THROUGH */
|
/* FALL THROUGH */
|
||||||
case State_Evade:
|
case State_Evade:
|
||||||
{
|
{
|
||||||
|
chooseEvasionDirection(samePosition);
|
||||||
mEvadeDuration += duration;
|
mEvadeDuration += duration;
|
||||||
if(mEvadeDuration < DURATION_TO_EVADE)
|
if(mEvadeDuration < DURATION_TO_EVADE)
|
||||||
return true;
|
return true;
|
||||||
|
@ -169,4 +173,20 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
return false; // no obstacles to evade (yet)
|
return false; // no obstacles to evade (yet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement)
|
||||||
|
{
|
||||||
|
actorMovement.mPosition[0] = mEvadeDirection;
|
||||||
|
actorMovement.mPosition[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObstacleCheck::chooseEvasionDirection(bool samePosition)
|
||||||
|
{
|
||||||
|
// change direction if attempt didn't work
|
||||||
|
if (samePosition && (0 < mEvadeDuration))
|
||||||
|
{
|
||||||
|
mEvadeDirection = mEvadeDirection == 1.0f ? -1.0f : 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ namespace MWWorld
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
struct Movement;
|
||||||
|
|
||||||
/// NOTE: determined empirically based on in-game behaviour
|
/// NOTE: determined empirically based on in-game behaviour
|
||||||
static const float MIN_DIST_TO_DOOR_SQUARED = 128*128;
|
static const float MIN_DIST_TO_DOOR_SQUARED = 128*128;
|
||||||
|
|
||||||
|
@ -36,6 +38,9 @@ namespace MWMechanics
|
||||||
// should be taken
|
// should be taken
|
||||||
bool check(const MWWorld::Ptr& actor, float duration);
|
bool check(const MWWorld::Ptr& actor, float duration);
|
||||||
|
|
||||||
|
// change direction to try to fix "stuck" actor
|
||||||
|
void takeEvasiveAction(MWMechanics::Movement& actorMovement);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// for checking if we're stuck (ignoring Z axis)
|
// for checking if we're stuck (ignoring Z axis)
|
||||||
|
@ -53,6 +58,9 @@ namespace MWMechanics
|
||||||
float mStuckDuration; // accumulate time here while in same spot
|
float mStuckDuration; // accumulate time here while in same spot
|
||||||
float mEvadeDuration;
|
float mEvadeDuration;
|
||||||
float mDistSameSpot; // take account of actor's speed
|
float mDistSameSpot; // take account of actor's speed
|
||||||
|
float mEvadeDirection;
|
||||||
|
|
||||||
|
void chooseEvasionDirection(bool samePosition);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,19 +225,16 @@ namespace MWMechanics
|
||||||
ESM::Pathgrid::Point temp(mPathgrid->mPoints[startNode]);
|
ESM::Pathgrid::Point temp(mPathgrid->mPoints[startNode]);
|
||||||
converter.ToWorld(temp);
|
converter.ToWorld(temp);
|
||||||
mPath.push_back(temp);
|
mPath.push_back(temp);
|
||||||
|
|
||||||
mPath.push_back(endPoint);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
mPath = mCell->aStarSearch(startNode, endNode.first);
|
|
||||||
if (mPath.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// convert supplied path to world co-ordinates
|
|
||||||
for (std::list<ESM::Pathgrid::Point>::iterator iter(mPath.begin()); iter != mPath.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
converter.ToWorld(*iter);
|
mPath = mCell->aStarSearch(startNode, endNode.first);
|
||||||
|
|
||||||
|
// convert supplied path to world co-ordinates
|
||||||
|
for (std::list<ESM::Pathgrid::Point>::iterator iter(mPath.begin()); iter != mPath.end(); ++iter)
|
||||||
|
{
|
||||||
|
converter.ToWorld(*iter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If endNode found is NOT the closest PathGrid point to the endPoint,
|
// If endNode found is NOT the closest PathGrid point to the endPoint,
|
||||||
|
@ -254,8 +251,6 @@ namespace MWMechanics
|
||||||
// The AI routines will have to deal with such situations.
|
// The AI routines will have to deal with such situations.
|
||||||
if(endNode.second)
|
if(endNode.second)
|
||||||
mPath.push_back(endPoint);
|
mPath.push_back(endPoint);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float PathFinder::getZAngleToNext(float x, float y) const
|
float PathFinder::getZAngleToNext(float x, float y) const
|
||||||
|
|
Loading…
Reference in a new issue