From 5369d206822def39fac0d6b7674fb8e1537cb82d Mon Sep 17 00:00:00 2001
From: dteviot <dteviot@gmail.com>
Date: Sat, 19 Sep 2015 15:34:02 +1200
Subject: [PATCH 1/2] Moved pathfinding logic from AiCombat to Pathfinding.

---
 apps/openmw/mwmechanics/aicombat.cpp    | 24 +++---------------------
 apps/openmw/mwmechanics/pathfinding.cpp | 11 +++++++++++
 2 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp
index b42c3bdcc..6270779a4 100644
--- a/apps/openmw/mwmechanics/aicombat.cpp
+++ b/apps/openmw/mwmechanics/aicombat.cpp
@@ -458,28 +458,10 @@ namespace MWMechanics
 
                 followTarget = false;
 
-                buildNewPath(actor, target); //may fail to build a path, check before use
+                buildNewPath(actor, target);
 
-                // if current actor pos is closer to target then last point of path (excluding target itself) then go straight on target
-                // This works on the borders between the path grid and areas with no waypoints.
-                if(inLOS && mPathFinder.getPath().size() > 1)
-                {
-                    // get point just before target
-                    std::list<ESM::Pathgrid::Point>::const_iterator pntIter = --mPathFinder.getPath().end();
-                    --pntIter;
-                    osg::Vec3f vBeforeTarget(PathFinder::MakeOsgVec3(*pntIter));
-
-                    if(distToTarget <= (vTargetPos - vBeforeTarget).length())
-                    {
-                        mPathFinder.clearPath();
-                    }
-                }
-
-                // if there is no new path, then go straight on target
-                if (!mPathFinder.isPathConstructed())
-                {
-                    movement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos));
-                }
+                // should always return a path (even if it's just go straight on target.)
+                assert(mPathFinder.isPathConstructed());
             }
 
             if (readyToAttack)
diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp
index f26d3e109..dbe20fdc0 100644
--- a/apps/openmw/mwmechanics/pathfinding.cpp
+++ b/apps/openmw/mwmechanics/pathfinding.cpp
@@ -215,6 +215,17 @@ namespace MWMechanics
             endPointInLocalCoords,
                 startNode);
 
+        // if it's shorter for actor to travel from start to end, than to travel from either
+        // start or end to nearest pathgrid point, just travel from start to end.
+        float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2();
+        float endTolastNodeLength2 = distanceSquared(mPathgrid->mPoints[endNode.first], endPointInLocalCoords);
+        float startTo1stNodeLength2 = distanceSquared(mPathgrid->mPoints[startNode], startPointInLocalCoords);
+        if ((startToEndLength2 < startTo1stNodeLength2) || (startToEndLength2 < endTolastNodeLength2))
+        {
+            mPath.push_back(endPoint);
+            return;
+        }
+
         // AiWander has logic that depends on whether a path was created,
         // deleting allowed nodes if not.  Hence a path needs to be created
         // even if the start and the end points are the same.

From 60d0ad928386e4d77aa08add8e763fcc4c9ab170 Mon Sep 17 00:00:00 2001
From: dteviot <dteviot@gmail.com>
Date: Sat, 19 Sep 2015 16:14:00 +1200
Subject: [PATCH 2/2] When stuck, try moving backwards as well as to side.

---
 apps/openmw/mwmechanics/obstacle.cpp | 20 ++++++++++++++++----
 apps/openmw/mwmechanics/obstacle.hpp |  7 ++++++-
 2 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp
index fe3d68a58..53f12a982 100644
--- a/apps/openmw/mwmechanics/obstacle.cpp
+++ b/apps/openmw/mwmechanics/obstacle.cpp
@@ -15,6 +15,14 @@ namespace MWMechanics
     static const float DURATION_SAME_SPOT = 1.0f;
     static const float DURATION_TO_EVADE = 0.4f;
 
+    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
+    };
+
     // Proximity check function for interior doors.  Given that most interior cells
     // do not have many doors performance shouldn't be too much of an issue.
     //
@@ -69,7 +77,7 @@ namespace MWMechanics
       , mStuckDuration(0)
       , mEvadeDuration(0)
       , mDistSameSpot(-1) // avoid calculating it each time
-      , mEvadeDirection(1.0f)
+      , mEvadeDirectionIndex(0)
     {
     }
 
@@ -176,8 +184,8 @@ namespace MWMechanics
 
     void ObstacleCheck::takeEvasiveAction(MWMechanics::Movement& actorMovement)
     {
-        actorMovement.mPosition[0] = mEvadeDirection;
-        actorMovement.mPosition[1] = 0;
+        actorMovement.mPosition[0] = evadeDirections[mEvadeDirectionIndex][0];
+        actorMovement.mPosition[1] = evadeDirections[mEvadeDirectionIndex][1];
     }
 
     void ObstacleCheck::chooseEvasionDirection(bool samePosition)
@@ -185,7 +193,11 @@ namespace MWMechanics
         // change direction if attempt didn't work
         if (samePosition && (0 < mEvadeDuration))
         {
-            mEvadeDirection = mEvadeDirection == 1.0f ? -1.0f : 1.0f;
+            ++mEvadeDirectionIndex;
+            if (mEvadeDirectionIndex == NUM_EVADE_DIRECTIONS)
+            {
+                mEvadeDirectionIndex = 0;
+            }
         }
     }
 
diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp
index ef3e29e8b..ecff00a5c 100644
--- a/apps/openmw/mwmechanics/obstacle.hpp
+++ b/apps/openmw/mwmechanics/obstacle.hpp
@@ -13,6 +13,8 @@ namespace MWMechanics
     /// NOTE: determined empirically based on in-game behaviour
     static const float MIN_DIST_TO_DOOR_SQUARED = 128*128;
 
+    static const int NUM_EVADE_DIRECTIONS = 4;
+
     /// tests actor's proximity to a closed door by default
     bool proximityToDoor(const MWWorld::Ptr& actor,
                          float minSqr = MIN_DIST_TO_DOOR_SQUARED,
@@ -47,6 +49,9 @@ namespace MWMechanics
             float mPrevX;
             float mPrevY;
 
+            // directions to try moving in when get stuck
+            static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];
+
             enum WalkState
             {
                 State_Norm,
@@ -58,7 +63,7 @@ namespace MWMechanics
             float mStuckDuration; // accumulate time here while in same spot
             float mEvadeDuration;
             float mDistSameSpot; // take account of actor's speed
-            float mEvadeDirection;
+            int mEvadeDirectionIndex;
 
             void chooseEvasionDirection(bool samePosition);
     };