From 69a1d8ac866cc0702fa31e78289f7dcfce0c6320 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Mon, 13 Jun 2022 23:21:39 +0300 Subject: [PATCH 1/9] Restart idle instantly only after the hit state ends --- apps/openmw/mwmechanics/character.cpp | 8 ++++---- apps/openmw/mwmechanics/character.hpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 84d28953d0..0c56d8c8d9 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -317,7 +317,7 @@ void CharacterController::resetCurrentDeathState() mDeathState = CharState_None; } -void CharacterController::refreshHitRecoilAnims(CharacterState& idle) +void CharacterController::refreshHitRecoilAnims() { auto& charClass = mPtr.getClass(); if (!charClass.isActor()) @@ -339,6 +339,7 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) stats.setKnockedDown(false); stats.setHitRecovery(false); stats.setBlock(false); + resetCurrentIdleState(); } else if (isKnockedOut()) mAnimation->setLoopingEnabled(mCurrentHit, knockout); @@ -392,6 +393,7 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) stats.setKnockedDown(false); stats.setHitRecovery(false); stats.setBlock(false); + resetCurrentIdleState(); return; } @@ -412,8 +414,6 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) } mAnimation->play(mCurrentHit, priority, MWRender::Animation::BlendMask_All, true, 1, startKey, stopKey, 0.0f, ~0ul); - - idle = CharState_None; } void CharacterController::refreshJumpAnims(JumpingState jump, CharacterState& idle, bool force) @@ -755,7 +755,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if (isPersistentAnimPlaying()) return; - refreshHitRecoilAnims(idle); + refreshHitRecoilAnims(); refreshJumpAnims(jump, idle, force); refreshMovementAnims(movement, idle, force); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index a15647d5be..6844f08f30 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -204,7 +204,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener void resetCurrentDeathState(); void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); - void refreshHitRecoilAnims(CharacterState& idle); + void refreshHitRecoilAnims(); void refreshJumpAnims(JumpingState jump, CharacterState& idle, bool force=false); void refreshMovementAnims(CharacterState movement, CharacterState& idle, bool force=false); void refreshIdleAnims(CharacterState idle, bool force=false); From 1a646374b0a39fce87fa856c29c4e4d2c5ecd127 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Mon, 13 Jun 2022 23:36:06 +0300 Subject: [PATCH 2/9] Always reset the idle animation after the landing animation ends --- apps/openmw/mwmechanics/character.cpp | 28 +++++++++++---------------- apps/openmw/mwmechanics/character.hpp | 2 +- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0c56d8c8d9..7cf4dea70d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -416,13 +416,15 @@ void CharacterController::refreshHitRecoilAnims() mAnimation->play(mCurrentHit, priority, MWRender::Animation::BlendMask_All, true, 1, startKey, stopKey, 0.0f, ~0ul); } -void CharacterController::refreshJumpAnims(JumpingState jump, CharacterState& idle, bool force) +void CharacterController::refreshJumpAnims(JumpingState jump, bool force) { - if (!force && jump == mJumpState && idle == CharState_None) + if (!force && jump == mJumpState) return; if (jump == JumpState_None) { + if (!mCurrentJump.empty()) + resetCurrentIdleState(); resetCurrentJumpState(); return; } @@ -431,25 +433,20 @@ void CharacterController::refreshJumpAnims(JumpingState jump, CharacterState& id std::string jumpAnimName = "jump" + weapShortGroup; MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; if (!weapShortGroup.empty() && !mAnimation->hasAnimation(jumpAnimName)) - { jumpAnimName = fallbackShortWeaponGroup("jump", &jumpmask); - // If we apply jump only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (jumpmask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None) - idle = CharState_Idle; - } - - if (!force && jump == mJumpState) + if (!mAnimation->hasAnimation(jumpAnimName)) + { + if (!mCurrentJump.empty()) + resetCurrentIdleState(); + resetCurrentJumpState(); return; + } bool startAtLoop = (jump == mJumpState); mJumpState = jump; clearStateAnimation(mCurrentJump); - if (!mAnimation->hasAnimation(jumpAnimName)) - return; - mCurrentJump = jumpAnimName; if(mJumpState == JumpState_InAir) mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, 1.0f, startAtLoop ? "loop start" : "start", "stop", 0.f, ~0ul); @@ -756,7 +753,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat return; refreshHitRecoilAnims(); - refreshJumpAnims(jump, idle, force); + refreshJumpAnims(jump, force); refreshMovementAnims(movement, idle, force); // idle handled last as it can depend on the other states @@ -2071,9 +2068,6 @@ void CharacterController::update(float duration) jumpstate = JumpState_Landing; vec.z() = 0.0f; - // We should reset idle animation during landing - clearStateAnimation(mCurrentIdle); - float height = cls.getCreatureStats(mPtr).land(isPlayer); float healthLost = getFallDamage(mPtr, height); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 6844f08f30..4b2bb3c72f 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -205,7 +205,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); void refreshHitRecoilAnims(); - void refreshJumpAnims(JumpingState jump, CharacterState& idle, bool force=false); + void refreshJumpAnims(JumpingState jump, bool force=false); void refreshMovementAnims(CharacterState movement, CharacterState& idle, bool force=false); void refreshIdleAnims(CharacterState idle, bool force=false); From 5dfce6205af93037e292973bce640d57d88091ca Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Mon, 13 Jun 2022 23:40:17 +0300 Subject: [PATCH 3/9] Reset the idle animation after a movement animation ends Fix non-biped actor idle reset --- apps/openmw/mwmechanics/character.cpp | 39 +++++++++------------------ apps/openmw/mwmechanics/character.hpp | 2 +- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7cf4dea70d..37302d2caf 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -558,15 +558,17 @@ std::string CharacterController::fallbackShortWeaponGroup(const std::string& bas return groupName; } -void CharacterController::refreshMovementAnims(CharacterState movement, CharacterState& idle, bool force) +void CharacterController::refreshMovementAnims(CharacterState movement, bool force) { - if (movement == mMovementState && idle == mIdleState && !force) + if (movement == mMovementState && !force) return; std::string movementAnimName = movementStateToAnimGroup(movement); if (movementAnimName.empty()) { + if (!mCurrentMovement.empty()) + resetCurrentIdleState(); resetCurrentMovementState(); return; } @@ -594,20 +596,11 @@ void CharacterController::refreshMovementAnims(CharacterState movement, Characte weapMovementAnimName = movementAnimName + weapShortGroup; if (!mAnimation->hasAnimation(weapMovementAnimName)) - { weapMovementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask); - // If we apply movement only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (movemask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None) - idle = CharState_Idle; - } movementAnimName = weapMovementAnimName; } - if (!force && movement == mMovementState) - return; - if (!mAnimation->hasAnimation(movementAnimName)) { std::string::size_type runpos = movementAnimName.find("run"); @@ -616,6 +609,8 @@ void CharacterController::refreshMovementAnims(CharacterState movement, Characte if (!mAnimation->hasAnimation(movementAnimName)) { + if (!mCurrentMovement.empty()) + resetCurrentIdleState(); resetCurrentMovementState(); return; } @@ -633,15 +628,6 @@ void CharacterController::refreshMovementAnims(CharacterState movement, Characte clearStateAnimation(mCurrentMovement); mCurrentMovement = movementAnimName; - // Reset idle if we actually play movement animations excepts of these cases: - // 1. When we play turning animations - // 2. When we use a fallback animation for lower body since movement animation for given weapon is missing (e.g. for crossbows and spellcasting) - if (!isTurning() && movemask == MWRender::Animation::BlendMask_All) - { - resetCurrentIdleState(); - idle = CharState_None; - } - // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity // even if we are running. This must be replicated, otherwise the observed speed would differ drastically. mAdjustMovementAnimSpeed = true; @@ -686,10 +672,11 @@ void CharacterController::refreshIdleAnims(CharacterState idle, bool force) // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), // the idle animation should be displayed if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped) - || (mMovementState != CharState_None && !isTurning()) - || mHitState != CharState_None) - && !mPtr.getClass().isBipedal(mPtr)) - idle = CharState_None; + || mMovementState != CharState_None || mHitState != CharState_None) && !mPtr.getClass().isBipedal(mPtr)) + { + resetCurrentIdleState(); + return; + } if (!force && idle == mIdleState && (mAnimation->isPlaying(mCurrentIdle) || !mAnimQueue.empty())) return; @@ -754,7 +741,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat refreshHitRecoilAnims(); refreshJumpAnims(jump, force); - refreshMovementAnims(movement, idle, force); + refreshMovementAnims(movement, force); // idle handled last as it can depend on the other states refreshIdleAnims(idle, force); @@ -2220,7 +2207,7 @@ void CharacterController::update(float duration) } } - if(movestate != CharState_None && !isTurning()) + if (movestate != CharState_None) clearAnimQueue(); if(mAnimQueue.empty() || inwater || (sneak && mIdleState != CharState_SpecialIdle)) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4b2bb3c72f..c1c7fd9cf5 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -206,7 +206,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); void refreshHitRecoilAnims(); void refreshJumpAnims(JumpingState jump, bool force=false); - void refreshMovementAnims(CharacterState movement, CharacterState& idle, bool force=false); + void refreshMovementAnims(CharacterState movement, bool force=false); void refreshIdleAnims(CharacterState idle, bool force=false); void clearAnimQueue(bool clearPersistAnims = false); From 4ce08664e956c115a0546404a1fb430d6ecf643c Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Mon, 13 Jun 2022 23:58:36 +0300 Subject: [PATCH 4/9] Fix swim spellcasting stance turning fallback --- apps/openmw/mwmechanics/character.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 37302d2caf..38c5fb472a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -573,6 +573,7 @@ void CharacterController::refreshMovementAnims(CharacterState movement, bool for return; } + mMovementState = movement; std::string::size_type swimpos = movementAnimName.find("swim"); if (!mAnimation->hasAnimation(movementAnimName)) { @@ -590,7 +591,7 @@ void CharacterController::refreshMovementAnims(CharacterState movement, bool for { std::string weapMovementAnimName; // Spellcasting stance turning is a special case - if (mWeaponType == ESM::Weapon::Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) + if (mWeaponType == ESM::Weapon::Spell && isTurning()) weapMovementAnimName = weapShortGroup + movementAnimName; else weapMovementAnimName = movementAnimName + weapShortGroup; @@ -616,8 +617,6 @@ void CharacterController::refreshMovementAnims(CharacterState movement, bool for } } - mMovementState = movement; - // If we're playing the same animation, start it from the point it ended float startpoint = 0.f; if (!mCurrentMovement.empty() && movementAnimName == mCurrentMovement) From 61d382dc9fe836cc42e92018b86083023c744b8e Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 14 Jun 2022 00:04:19 +0300 Subject: [PATCH 5/9] Set the initial IdleState to CharState_None --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 38c5fb472a..651ef6c5e5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1941,7 +1941,7 @@ void CharacterController::update(float duration) vec = osg::Vec3f(); CharacterState movestate = CharState_None; - CharacterState idlestate = CharState_SpecialIdle; + CharacterState idlestate = CharState_None; JumpingState jumpstate = JumpState_None; bool forcestateupdate = false; From 456277ba6a82abbedfad3f314c306dd79e93f444 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 14 Jun 2022 00:46:45 +0300 Subject: [PATCH 6/9] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e52937c08b..defd0e7542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Bug #5453: Magic effect VFX are offset for creatures Bug #5483: AutoCalc flag is not used to calculate spells cost Bug #5508: Engine binary links to Qt without using it + Bug #5592: Weapon idle animations do not work properly Bug #5596: Effects in constant spells should not be merged Bug #5621: Drained stats cannot be restored Bug #5766: Active grid object paging - disappearing textures From c2ac52c82a513ecee95fe8a41e46a4acc4bb5b45 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 14 Jun 2022 02:43:48 +0300 Subject: [PATCH 7/9] Remove dead code --- apps/openmw/mwmechanics/character.cpp | 11 +---------- apps/openmw/mwmechanics/character.hpp | 2 -- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 651ef6c5e5..ad6f324d77 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -131,8 +131,6 @@ std::string movementStateToAnimGroup(MWMechanics::CharacterState state) case CharState_SneakLeft: return "sneakleft"; case CharState_SneakRight: return "sneakright"; - case CharState_Jump: return "jump"; - case CharState_TurnLeft: return "turnleft"; case CharState_TurnRight: return "turnright"; case CharState_SwimTurnLeft: return "swimturnleft"; @@ -1944,8 +1942,6 @@ void CharacterController::update(float duration) CharacterState idlestate = CharState_None; JumpingState jumpstate = JumpState_None; - bool forcestateupdate = false; - mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f; isrunning = isrunning && mHasMovedInXY; @@ -2021,8 +2017,6 @@ void CharacterController::update(float duration) if(!onground && !flying && !inwater && solid) { // In the air (either getting up —ascending part of jump— or falling). - - forcestateupdate = (mJumpState != JumpState_InAir); jumpstate = JumpState_InAir; static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->mValue.getFloat(); @@ -2050,7 +2044,6 @@ void CharacterController::update(float duration) } else if(mJumpState == JumpState_InAir && !inwater && !flying && solid) { - forcestateupdate = true; jumpstate = JumpState_Landing; vec.z() = 0.0f; @@ -2223,9 +2216,7 @@ void CharacterController::update(float duration) if (!mSkipAnim) { - forcestateupdate = updateState(idlestate) || forcestateupdate; - - refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate); + refreshCurrentAnims(idlestate, movestate, jumpstate, updateState(idlestate)); updateIdleStormState(inwater); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index c1c7fd9cf5..003f64953d 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -91,8 +91,6 @@ enum CharacterState { CharState_SwimTurnLeft, CharState_SwimTurnRight, - CharState_Jump, - CharState_Death1, CharState_Death2, CharState_Death3, From 30ef7ad81bdd1f358aafa5657e295d6cdf45caf4 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 14 Jun 2022 02:45:50 +0300 Subject: [PATCH 8/9] Properly cancel the landing animation during movement --- apps/openmw/mwmechanics/character.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ad6f324d77..eef594855d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2200,7 +2200,10 @@ void CharacterController::update(float duration) } if (movestate != CharState_None) + { clearAnimQueue(); + jumpstate = JumpState_None; + } if(mAnimQueue.empty() || inwater || (sneak && mIdleState != CharState_SpecialIdle)) { From 2b019864b741191b66cf7fa22e6f3dee3f60326b Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 14 Jun 2022 13:47:16 +0300 Subject: [PATCH 9/9] Remove more legacy states --- apps/openmw/mwmechanics/character.cpp | 8 -------- apps/openmw/mwmechanics/character.hpp | 8 -------- 2 files changed, 16 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index eef594855d..c7e5cf70a3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -190,14 +190,6 @@ std::string idleStateToAnimGroup(MWMechanics::CharacterState state) case CharState_IdleSneak: return "idlesneak"; case CharState_Idle: - case CharState_Idle2: - case CharState_Idle3: - case CharState_Idle4: - case CharState_Idle5: - case CharState_Idle6: - case CharState_Idle7: - case CharState_Idle8: - case CharState_Idle9: case CharState_SpecialIdle: return "idle"; default: diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 003f64953d..2de6713396 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -50,14 +50,6 @@ enum CharacterState { CharState_SpecialIdle, CharState_Idle, - CharState_Idle2, - CharState_Idle3, - CharState_Idle4, - CharState_Idle5, - CharState_Idle6, - CharState_Idle7, - CharState_Idle8, - CharState_Idle9, CharState_IdleSwim, CharState_IdleSneak,