From fc0f813dcb57a18186bbc8d386f80b881d6fc770 Mon Sep 17 00:00:00 2001
From: capostrophic <alexdobrohotov@yandex.ru>
Date: Thu, 9 Jan 2020 14:11:53 +0300
Subject: [PATCH] Add and use 'always active' AI package flag

Update documentation
---
 apps/openmw/mwmechanics/actors.cpp              | 11 ++++++++---
 apps/openmw/mwmechanics/aipackage.hpp           |  3 +++
 apps/openmw/mwmechanics/aisequence.cpp          |  9 ++++-----
 apps/openmw/mwmechanics/aitravel.hpp            |  2 ++
 docs/source/reference/modding/settings/game.rst | 10 +++++-----
 5 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp
index cbe058a00d..b49dcd4694 100644
--- a/apps/openmw/mwmechanics/actors.cpp
+++ b/apps/openmw/mwmechanics/actors.cpp
@@ -1728,9 +1728,14 @@ namespace MWMechanics
                 const float dist = (playerPos - iter->first.getRefData().getPosition().asVec3()).length();
                 bool isPlayer = iter->first == player;
                 CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
-                int packageId = stats.getAiSequence().getTypeId();
-                bool travelling = (packageId == AiPackage::TypeIdTravel) || (packageId == AiPackage::TypeIdInternalTravel);
-                bool inRange = isPlayer || dist <= mActorsProcessingRange || travelling;
+                // Actors with active AI should be able to move.
+                bool alwaysActive = false;
+                if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())
+                {
+                    MWMechanics::AiSequence& seq = stats.getAiSequence();
+                    alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive();
+                }
+                bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive;
                 int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)
                 if (isPlayer)
                     activeFlag = 2;
diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp
index 0e628388de..b9b3baf643 100644
--- a/apps/openmw/mwmechanics/aipackage.hpp
+++ b/apps/openmw/mwmechanics/aipackage.hpp
@@ -104,6 +104,9 @@ namespace MWMechanics
 
             virtual osg::Vec3f getDestination() { return osg::Vec3f(0, 0, 0); }
 
+            // Return true if any loaded actor with this AI package must be active.
+            virtual bool alwaysActive() const { return false; }
+
             /// Reset pathfinding state
             void reset();
 
diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp
index 0c675bfc1f..5760069e77 100644
--- a/apps/openmw/mwmechanics/aisequence.cpp
+++ b/apps/openmw/mwmechanics/aisequence.cpp
@@ -209,12 +209,15 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
         }
 
         MWMechanics::AiPackage* package = mPackages.front();
+        if (!package->alwaysActive() && outOfRange)
+            return;
+
         int packageTypeId = package->getTypeId();
         // workaround ai packages not being handled as in the vanilla engine
         if (isActualAiPackage(packageTypeId))
             mLastAiPackage = packageTypeId;
         // if active package is combat one, choose nearest target
-        if (!outOfRange && packageTypeId == AiPackage::TypeIdCombat)
+        if (packageTypeId == AiPackage::TypeIdCombat)
         {
             std::list<AiPackage *>::iterator itActualCombat;
 
@@ -272,10 +275,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
 
         try
         {
-            if (outOfRange && packageTypeId != AiPackage::TypeIdTravel
-                && packageTypeId != AiPackage::TypeIdInternalTravel)
-                return;
-
             if (package->execute (actor, characterController, mAiState, duration))
             {
                 // Put repeating noncombat AI packages on the end of the stack so they can be used again
diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp
index ea69b5d745..a333c83fd0 100644
--- a/apps/openmw/mwmechanics/aitravel.hpp
+++ b/apps/openmw/mwmechanics/aitravel.hpp
@@ -34,6 +34,8 @@ namespace MWMechanics
 
             virtual bool useVariableSpeed() const { return true;}
 
+            virtual bool alwaysActive() const { return true; }
+
             virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); }
 
         private:
diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst
index 162cb0e2ff..9a3e9c84d8 100644
--- a/docs/source/reference/modding/settings/game.rst
+++ b/docs/source/reference/modding/settings/game.rst
@@ -103,13 +103,13 @@ actors processing range
 :Range:		3584 to 7168
 :Default:	7168
 
-This setting allows to specify a distance from player in game units, in which OpenMW updates actor's state.
+This setting specifies the actor state update distance from the player in game units.
 Actor state update includes AI, animations, and physics processing.
-Actors near that border start softly fade out instead of just appearing/disapperaing.
-It is not recommended to change this value from default if you use mods with
-long-range AiTravel packages (e.g. patrols, caravans and travellers).
+Actors close to this distance softly fade in and out instead of appearing or disappearing abruptly.
+Keep in mind that actors running Travel AI packages are always active to avoid
+issues in mods with long-range AiTravel packages (for example, patrols, caravans and travellers).
 
-This setting can be controlled in game with the "Actors processing range slider" in the Prefs panel of the Options menu.
+This setting can be controlled in game with the "Actors Processing Range" slider in the Prefs panel of the Options menu.
 
 classic reflected absorb spells behavior
 ----------------------------------------