From 067779983986e91b226e9a3c8932094b2330d377 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sat, 29 Aug 2015 17:21:18 +1200 Subject: [PATCH 1/6] movement logic in AiPackage uses ObstacleCheck. --- apps/openmw/mwmechanics/aipackage.cpp | 27 ++++++++++----------------- apps/openmw/mwmechanics/aipackage.hpp | 2 -- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 2c8c57c56..c18ee5ceb 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -19,7 +19,7 @@ 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 mTimer += duration; //Update timer - mStuckTimer += duration; //Update stuck timer ESM::Position pos = actor.getRefData().getPosition(); //position of the actor @@ -91,11 +90,13 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po //************************ if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished? 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 - //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 + zTurn(actor, 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 MWWorld::Ptr door = getNearbyDoor(actor); if(door != MWWorld::Ptr()) // NOTE: checks interior cells only @@ -106,24 +107,16 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po } else // probably walking into another NPC { - actor.getClass().getMovementSettings(actor).mPosition[0] = 1; - actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + movement.mPosition[0] = 1; + movement.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 + movement.mPosition[1] = 1; //Just run forward } } - else { - actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward the rest of the time - } - - zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])); - return false; } diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index d73833b94..00cab2d08 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -83,9 +83,7 @@ namespace MWMechanics ObstacleCheck mObstacleCheck; float mTimer; - float mStuckTimer; - ESM::Position mStuckPos; ESM::Pathgrid::Point mPrevDest; }; } From f59e918a3b697e119a224b86c7ac2edd591e978f Mon Sep 17 00:00:00 2001 From: dteviot Date: Sat, 29 Aug 2015 17:34:33 +1200 Subject: [PATCH 2/6] removed useless code. zTurn ignores turns < 0.0087 radians. --- apps/openmw/mwmechanics/aipackage.cpp | 2 -- apps/openmw/mwmechanics/aiwander.cpp | 3 --- 2 files changed, 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index c18ee5ceb..36843663e 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -109,8 +109,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po { movement.mPosition[0] = 1; movement.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 diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 8f27a2048..01d8125b3 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -437,9 +437,6 @@ namespace MWMechanics // but doesn't seem to do that? actor.getClass().getMovementSettings(actor).mPosition[0] = 1; 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 } From 31d82b6b0c09a9e1cf2296fc46a2c40bdaf10627 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 30 Aug 2015 08:32:47 +1200 Subject: [PATCH 3/6] Unifiy evadeObstacles() logic between AiWander and AiPackage Can't use same code, but logic is now same. --- apps/openmw/mwmechanics/aipackage.cpp | 41 +++++++++++++++------------ apps/openmw/mwmechanics/aipackage.hpp | 4 +++ apps/openmw/mwmechanics/aiwander.cpp | 20 +++++++------ apps/openmw/mwmechanics/aiwander.hpp | 2 +- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 36843663e..097cc4042 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -92,30 +92,35 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po return true; else { - zTurn(actor, mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])); + evadeObstacles(actor, duration, pos); + } + return false; +} - MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor); - if(mObstacleCheck.check(actor, duration)) +void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float duration, ESM::Position& pos) +{ + zTurn(actor, 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 + MWWorld::Ptr door = getNearbyDoor(actor); + if (door != MWWorld::Ptr()) // NOTE: checks interior cells only { - // 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 - { - movement.mPosition[0] = 1; - movement.mPosition[1] = 1; + 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 { //Not stuck, so reset things - movement.mPosition[1] = 1; //Just run forward + else // probably walking into another NPC + { + movement.mPosition[0] = 1; + movement.mPosition[1] = 1; } } - return false; + 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) diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 00cab2d08..4f919edbc 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -85,6 +85,10 @@ namespace MWMechanics float mTimer; ESM::Pathgrid::Point mPrevDest; + + private: + void evadeObstacles(const MWWorld::Ptr& actor, float duration, ESM::Position& pos); + }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 01d8125b3..ce70fee77 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -381,11 +381,7 @@ namespace MWMechanics else { // have not yet reached the destination - //... turn towards the next point in mPath - zTurn(actor, storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])); - actor.getClass().getMovementSettings(actor).mPosition[1] = 1; - - evadeObstacles(actor, storage, duration); + evadeObstacles(actor, storage, duration, pos); } } @@ -417,8 +413,12 @@ namespace MWMechanics 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)) { // first check if we're walking into a door @@ -435,11 +435,15 @@ namespace MWMechanics { // TODO: diagonal should have same animation as walk forward // but doesn't seem to do that? - actor.getClass().getMovementSettings(actor).mPosition[0] = 1; - actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; + movement.mPosition[0] = 1; + movement.mPosition[1] = 0.1f; } mStuckCount++; // TODO: maybe no longer needed } + else + { + movement.mPosition[1] = 1; + } //#if 0 // TODO: maybe no longer needed if (mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 27e6fe9fb..b0fabfce3 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -83,7 +83,7 @@ namespace MWMechanics short unsigned getRandomIdle(); void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); 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 turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos); From f2c9b9351f702d18e8ca3e2fcb97d640780fa8bf Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 30 Aug 2015 10:06:09 +1200 Subject: [PATCH 4/6] Try going right and left to "unstick" actor. --- apps/openmw/mwmechanics/aipackage.cpp | 3 +-- apps/openmw/mwmechanics/aiwander.cpp | 3 +-- apps/openmw/mwmechanics/obstacle.cpp | 20 ++++++++++++++++++++ apps/openmw/mwmechanics/obstacle.hpp | 8 ++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 097cc4042..b6b240833 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -114,8 +114,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur } else // probably walking into another NPC { - movement.mPosition[0] = 1; - movement.mPosition[1] = 1; + mObstacleCheck.takeEvasiveAction(movement); } } else { //Not stuck, so reset things diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index ce70fee77..67a9f766c 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -435,8 +435,7 @@ namespace MWMechanics { // TODO: diagonal should have same animation as walk forward // but doesn't seem to do that? - movement.mPosition[0] = 1; - movement.mPosition[1] = 0.1f; + mObstacleCheck.takeEvasiveAction(movement); } mStuckCount++; // TODO: maybe no longer needed } diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 25bd06301..fe3d68a58 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -6,6 +6,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "movement.hpp" + namespace MWMechanics { // NOTE: determined empirically but probably need further tweaking @@ -67,6 +69,7 @@ namespace MWMechanics , mStuckDuration(0) , mEvadeDuration(0) , mDistSameSpot(-1) // avoid calculating it each time + , mEvadeDirection(1.0f) { } @@ -155,6 +158,7 @@ namespace MWMechanics /* FALL THROUGH */ case State_Evade: { + chooseEvasionDirection(samePosition); mEvadeDuration += duration; if(mEvadeDuration < DURATION_TO_EVADE) return true; @@ -169,4 +173,20 @@ namespace MWMechanics } 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; + } + } + } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index e0ae9203d..ef3e29e8b 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -8,6 +8,8 @@ namespace MWWorld namespace MWMechanics { + struct Movement; + /// NOTE: determined empirically based on in-game behaviour static const float MIN_DIST_TO_DOOR_SQUARED = 128*128; @@ -36,6 +38,9 @@ namespace MWMechanics // should be taken bool check(const MWWorld::Ptr& actor, float duration); + // change direction to try to fix "stuck" actor + void takeEvasiveAction(MWMechanics::Movement& actorMovement); + private: // 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 mEvadeDuration; float mDistSameSpot; // take account of actor's speed + float mEvadeDirection; + + void chooseEvasionDirection(bool samePosition); }; } From 39c2ba8efe98aa4d9f29f06450a62f2081d372c9 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 30 Aug 2015 16:12:51 +1200 Subject: [PATCH 5/6] Pathfinding bugfix. Observed at Ebonheart (coe 1, -13). Especially at the western tower. Guards try to walk though tower door. Cause: buildPath() adds destination (even when unreachable) when only using single node from pathgrid. --- apps/openmw/mwmechanics/pathfinding.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f53badbf4..daab32136 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -225,19 +225,16 @@ namespace MWMechanics ESM::Pathgrid::Point temp(mPathgrid->mPoints[startNode]); converter.ToWorld(temp); mPath.push_back(temp); - - mPath.push_back(endPoint); - return; } - - mPath = mCell->aStarSearch(startNode, endNode.first); - if (mPath.empty()) - return; - - // convert supplied path to world co-ordinates - for (std::list::iterator iter(mPath.begin()); iter != mPath.end(); ++iter) + else { - converter.ToWorld(*iter); + mPath = mCell->aStarSearch(startNode, endNode.first); + + // convert supplied path to world co-ordinates + for (std::list::iterator iter(mPath.begin()); iter != mPath.end(); ++iter) + { + converter.ToWorld(*iter); + } } // 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. if(endNode.second) mPath.push_back(endPoint); - - return; } float PathFinder::getZAngleToNext(float x, float y) const From 1dfe438a5d37401b75bd2de491a2524cc92ffd21 Mon Sep 17 00:00:00 2001 From: dteviot Date: Sun, 30 Aug 2015 16:43:35 +1200 Subject: [PATCH 6/6] reduce "reset if stuck" AiWander timeout. Now is about 14 seconds, instead of 300. --- apps/openmw/mwmechanics/aiwander.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 67a9f766c..b789c4428 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -27,7 +27,7 @@ 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 REACTION_INTERVAL = 0.25f; static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player @@ -443,8 +443,8 @@ namespace MWMechanics { movement.mPosition[1] = 1; } -//#if 0 - // TODO: maybe no longer needed + + // if stuck for sufficiently long, act like current location was the destination if (mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset { //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; @@ -454,7 +454,6 @@ namespace MWMechanics storage.mState = Wander_ChooseAction; mStuckCount = 0; } -//#endif } void AiWander::playIdleDialogueRandomly(const MWWorld::Ptr& actor)