From f41de6b02da923f24656b6510d4041c39f144688 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 1 Nov 2023 19:48:16 +0100 Subject: [PATCH] Use accumulated movement whenever possible. Apply diagonal movement by rotating accumulated movement and sliding based on that, rather than ignoring accumulated movement. --- apps/openmw/mwmechanics/character.cpp | 90 ++++++++++++++++--------- components/settings/categories/game.hpp | 1 + files/settings-default.cfg | 5 ++ 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 068d91ab42..1fca6c6b71 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2387,48 +2387,72 @@ namespace MWMechanics } osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration); - if (duration > 0.0f) - moved /= duration; - else - moved = osg::Vec3f(0.f, 0.f, 0.f); - - moved.x() *= scale; - moved.y() *= scale; - // Ensure we're moving in generally the right direction... - if (speed > 0.f && moved != osg::Vec3f()) + if (mPtr.getClass().isActor() && isMovementAnimationControlled() && !isScriptedAnimPlaying()) { - float l = moved.length(); - if (std::abs(movement.x() - moved.x()) > std::abs(moved.x()) / 2 - || std::abs(movement.y() - moved.y()) > std::abs(moved.y()) / 2 - || std::abs(movement.z() - moved.z()) > std::abs(moved.z()) / 2) + if (duration > 0.0f) + moved /= duration; + else + moved = osg::Vec3f(0.f, 0.f, 0.f); + + moved.x() *= scale; + moved.y() *= scale; + + if (speed > 0.f && moved != osg::Vec3f()) { - moved = movement; - // For some creatures getSpeed doesn't work, so we adjust speed to the animation. - // TODO: Fix Creature::getSpeed. - float newLength = moved.length(); - if (newLength > 0 && !cls.isNpc()) - moved *= (l / newLength); + // Ensure we're moving in generally the right direction + // This is necessary when the "turn to movement direction" feature is off, as animations + // will not be rotated to match diagonal movement. In this case we have to slide the + // character diagonally. + + // First decide the general direction expected from the current animation + float animMovementAngle = 0; + if (!Settings::game().mTurnToMovementDirection || isFirstPersonPlayer) + { + if (cls.getMovementSettings(mPtr).mIsStrafing) + animMovementAngle = movement.x() > 0 ? -osg::PI_2f : osg::PI_2f; + else + animMovementAngle = movement.y() >= 0 ? 0 : -osg::PIf; + } + else + { + animMovementAngle = mAnimation->getLegsYawRadians(); + if (movement.y() < 0) + animMovementAngle -= osg::PIf; + } + + const float epsilon = 0.001f; + float targetMovementAngle = std::atan2(-movement.x(), movement.y()); + float diff = targetMovementAngle - animMovementAngle; + if (std::fabsf(diff) > epsilon) + { + moved = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * moved; + } + + if (isPlayer && Settings::game().mPlayerMovementIgnoresAnimation) + { + moved = movement; + } } - } - if (mFloatToSurface && cls.isActor()) - { - if (cls.getCreatureStats(mPtr).isDead() - || (!godmode - && cls.getCreatureStats(mPtr) - .getMagicEffects() - .getOrDefault(ESM::MagicEffect::Paralyze) - .getModifier() - > 0)) + if (mFloatToSurface) { - moved.z() = 1.0; + if (cls.getCreatureStats(mPtr).isDead() + || (!godmode + && cls.getCreatureStats(mPtr) + .getMagicEffects() + .getOrDefault(ESM::MagicEffect::Paralyze) + .getModifier() + > 0)) + { + moved.z() = 1.0; + } } - } - // Update movement - if (isMovementAnimationControlled() && mPtr.getClass().isActor() && !isScriptedAnimPlaying()) + // Update movement + && !isScriptedAnimPlaying() world->queueMovement(mPtr, moved); + } mSkipAnim = false; diff --git a/components/settings/categories/game.hpp b/components/settings/categories/game.hpp index ded367a54c..375d8ef819 100644 --- a/components/settings/categories/game.hpp +++ b/components/settings/categories/game.hpp @@ -74,6 +74,7 @@ namespace Settings "unarmed creature attacks damage armor" }; SettingValue mActorCollisionShapeType{ mIndex, "Game", "actor collision shape type" }; + SettingValue mPlayerMovementIgnoresAnimation{ mIndex, "Game", "player movement ignores animation" }; }; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index bbc6b4d1c8..da1c97519a 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -365,6 +365,11 @@ unarmed creature attacks damage armor = false # 2 = Cylinder actor collision shape type = 0 +# When false the player character will base movement on animations. This will sway the camera +# while moving in third person like in vanilla, and reproduce movement bugs caused by glitchy +# vanilla animations. +player movement ignores animation = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).