1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-03 17:49:41 +00:00

refactor pathfinding code in AiWander: use AiPackage::pathTo, reuse AiPackage::ObstacleCheck

This commit is contained in:
mrcheko 2016-07-05 21:38:41 +03:00
parent b304e98568
commit 0793e4a80e
5 changed files with 31 additions and 33 deletions

View file

@ -68,7 +68,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const ESM::Pathgr
{ {
bool wasShortcutting = mIsShortcutting; bool wasShortcutting = mIsShortcutting;
bool destInLOS = false; bool destInLOS = false;
mIsShortcutting = shortcutPath(start, dest, actor, &destInLOS); // try to shortcut first if (getTypeId() != TypeIdWander) // prohibit shortcuts for AiWander
mIsShortcutting = shortcutPath(start, dest, actor, &destInLOS); // try to shortcut first
if (!mIsShortcutting) if (!mIsShortcutting)
{ {
@ -139,8 +140,10 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
MWWorld::Ptr door = getNearbyDoor(actor); // NOTE: checks interior cells only MWWorld::Ptr door = getNearbyDoor(actor); // NOTE: checks interior cells only
if (door != MWWorld::Ptr()) if (door != MWWorld::Ptr())
{ {
if (!door.getCellRef().getTeleport() && door.getCellRef().getTrap().empty() // note: AiWander currently does not open doors
&& door.getCellRef().getLockLevel() <= 0 && door.getClass().getDoorState(door) == 0) { if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getCellRef().getTrap().empty()
&& door.getCellRef().getLockLevel() <= 0 && door.getClass().getDoorState(door) == 0)
{
MWBase::Environment::get().getWorld()->activateDoor(door, 1); MWBase::Environment::get().getWorld()->activateDoor(door, 1);
} }
} }

View file

@ -70,8 +70,6 @@ namespace MWMechanics
unsigned short mIdleAnimation; unsigned short mIdleAnimation;
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
PathFinder mPathFinder;
AiWanderStorage(): AiWanderStorage():
mTargetAngleRadians(0), mTargetAngleRadians(0),
mTurnActorGivingGreetingToFacePlayer(false), mTurnActorGivingGreetingToFacePlayer(false),
@ -87,7 +85,7 @@ namespace MWMechanics
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat): AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
, mStoredInitialActorPosition(false) , mStoredInitialActorPosition(false), mIsWanderDestReady(false)
{ {
mIdle.resize(8, 0); mIdle.resize(8, 0);
init(); init();
@ -252,7 +250,7 @@ namespace MWMechanics
if ((wanderState == Wander_MoveNow) && mDistance) if ((wanderState == Wander_MoveNow) && mDistance)
{ {
// Construct a new path if there isn't one // Construct a new path if there isn't one
if(!storage.mPathFinder.isPathConstructed()) if(!mPathFinder.isPathConstructed())
{ {
if (mAllowedNodes.size()) if (mAllowedNodes.size())
{ {
@ -290,7 +288,7 @@ namespace MWMechanics
void AiWander::returnToStartLocation(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos) void AiWander::returnToStartLocation(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)
{ {
if (!storage.mPathFinder.isPathConstructed()) if (!mPathFinder.isPathConstructed())
{ {
ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mReturnPosition)); ESM::Pathgrid::Point dest(PathFinder::MakePathgridPoint(mReturnPosition));
@ -298,10 +296,11 @@ namespace MWMechanics
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
// don't take shortcuts for wandering // don't take shortcuts for wandering
storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell()); mPathFinder.buildSyncedPath(start, dest, actor.getCell());
if (storage.mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {
mIsWanderDestReady = true;
storage.mState = Wander_Walking; storage.mState = Wander_Walking;
} }
} }
@ -371,7 +370,7 @@ namespace MWMechanics
float duration, AiWanderStorage& storage, ESM::Position& pos) float duration, AiWanderStorage& storage, ESM::Position& pos)
{ {
// Are we there yet? // Are we there yet?
if (storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE)) if (mIsWanderDestReady && pathTo(actor, mPathFinder.getPath().back(), duration, DESTINATION_TOLERANCE))
{ {
stopWalking(actor, storage); stopWalking(actor, storage);
storage.mState = Wander_ChooseAction; storage.mState = Wander_ChooseAction;
@ -414,34 +413,21 @@ 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)
{ {
// turn towards the next point in mPath if (mObstacleCheck.isEvading())
zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]));
MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);
if (mObstacleCheck.check(actor, duration))
{ {
// 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)) // NOTE: checks interior cells only
{ {
// remove allowed points then select another random destination // remove allowed points then select another random destination
mTrimCurrentNode = true; mTrimCurrentNode = true;
trimAllowedNodes(mAllowedNodes, storage.mPathFinder); trimAllowedNodes(mAllowedNodes, mPathFinder);
mObstacleCheck.clear(); mObstacleCheck.clear();
storage.mPathFinder.clearPath(); mPathFinder.clearPath();
storage.mState = Wander_MoveNow; storage.mState = Wander_MoveNow;
} }
else // probably walking into another NPC
{
// TODO: diagonal should have same animation as walk forward
// but doesn't seem to do that?
mObstacleCheck.takeEvasiveAction(movement);
}
mStuckCount++; // TODO: maybe no longer needed mStuckCount++; // TODO: maybe no longer needed
} }
else
{
movement.mPosition[1] = 1;
}
// if stuck for sufficiently long, act like current location was the destination // 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
@ -564,11 +550,12 @@ namespace MWMechanics
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos));
// don't take shortcuts for wandering // don't take shortcuts for wandering
storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell()); mPathFinder.buildSyncedPath(start, dest, actor.getCell());
storage.mPathFinder.buildPath(start, dest, actor.getCell());
if (storage.mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {
mIsWanderDestReady = true;
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node): // Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
mAllowedNodes.erase(mAllowedNodes.begin() + randNode); mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
@ -624,7 +611,8 @@ namespace MWMechanics
void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage) void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage)
{ {
storage.mPathFinder.clearPath(); mPathFinder.clearPath();
mIsWanderDestReady = false;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
} }
@ -850,6 +838,7 @@ namespace MWMechanics
, mRepeat(wander->mData.mShouldRepeat != 0) , mRepeat(wander->mData.mShouldRepeat != 0)
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
, mStartTime(MWWorld::TimeStamp(wander->mStartTime)) , mStartTime(MWWorld::TimeStamp(wander->mStartTime))
, mIsWanderDestReady(false)
{ {
if (mStoredInitialActorPosition) if (mStoredInitialActorPosition)
mInitialActorPosition = wander->mInitialActorPosition; mInitialActorPosition = wander->mInitialActorPosition;

View file

@ -130,9 +130,9 @@ namespace MWMechanics
const PathFinder& pathfinder); const PathFinder& pathfinder);
// ObstacleCheck mObstacleCheck;
float mDoorCheckDuration; float mDoorCheckDuration;
int mStuckCount; int mStuckCount;
bool mIsWanderDestReady;
// constants for converting idleSelect values into groupNames // constants for converting idleSelect values into groupNames
enum GroupIndex enum GroupIndex

View file

@ -93,6 +93,11 @@ namespace MWMechanics
return mWalkState == State_Norm; return mWalkState == State_Norm;
} }
bool ObstacleCheck::isEvading() const
{
return mWalkState == State_Evade;
}
/* /*
* input - actor, duration (time since last check) * input - actor, duration (time since last check)
* output - true if evasive action needs to be taken * output - true if evasive action needs to be taken

View file

@ -33,6 +33,7 @@ namespace MWMechanics
void clear(); void clear();
bool isNormalState() const; bool isNormalState() const;
bool isEvading() const;
// Returns true if there is an obstacle and an evasive action // Returns true if there is an obstacle and an evasive action
// should be taken // should be taken