From 2d87d287ba033b9ac6976d01f26d5585f31f4753 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Mon, 6 Jan 2020 15:09:32 +0300 Subject: [PATCH 1/2] Handle out-of-range actors' travel packages (#5212) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 10 +++++++++- apps/openmw/mwmechanics/aisequence.cpp | 8 ++++++-- apps/openmw/mwmechanics/aisequence.hpp | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad6a78761..19337301b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -180,6 +180,7 @@ Bug #5209: Spellcasting ignores race height Bug #5210: AiActivate allows actors to open dialogue and inventory windows Bug #5211: Screen fades in if the first loaded save is in interior cell + Bug #5212: AiTravel does not work for actors outside of AI processing range Bug #5213: SameFaction script function is broken Bug #5218: Crash when disabling ToggleBorders Bug #5220: GetLOS crashes when actor isn't loaded diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 35a48710d..cbe058a00 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1695,6 +1695,11 @@ namespace MWMechanics } } } + else if (aiActive && iter->first != player && isConscious(iter->first)) + { + CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); + stats.getAiSequence().execute(iter->first, *ctrl, duration, /*outOfRange*/true); + } if(iter->first.getClass().isNpc()) { @@ -1722,7 +1727,10 @@ namespace MWMechanics { const float dist = (playerPos - iter->first.getRefData().getPosition().asVec3()).length(); bool isPlayer = iter->first == player; - bool inRange = isPlayer || dist <= mActorsProcessingRange; + 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; 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/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index e02240352..0c675bfc1 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -198,7 +198,7 @@ bool isActualAiPackage(int packageTypeId) packageTypeId <= AiPackage::TypeIdActivate); } -void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration) +void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange) { if(actor != getPlayer()) { @@ -214,7 +214,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac if (isActualAiPackage(packageTypeId)) mLastAiPackage = packageTypeId; // if active package is combat one, choose nearest target - if (packageTypeId == AiPackage::TypeIdCombat) + if (!outOfRange && packageTypeId == AiPackage::TypeIdCombat) { std::list::iterator itActualCombat; @@ -272,6 +272,10 @@ 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/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 5df440407..7f07d5aae 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -110,7 +110,7 @@ namespace MWMechanics void stopPursuit(); /// Execute current package, switching if needed. - void execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration); + void execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange=false); /// Simulate the passing of time using the currently active AI package void fastForward(const MWWorld::Ptr &actor); From fc0f813dcb57a18186bbc8d386f80b881d6fc770 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 9 Jan 2020 14:11:53 +0300 Subject: [PATCH 2/2] 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 cbe058a00..b49dcd469 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 0e628388d..b9b3baf64 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 0c675bfc1..5760069e7 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::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 ea69b5d74..a333c83fd 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 162cb0e2f..9a3e9c84d 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 ----------------------------------------