From 123ca175c8172e0cfaec09cc24658f480eab5d84 Mon Sep 17 00:00:00 2001
From: Andrei Kortunov <andrei.kortunov@yandex.ru>
Date: Fri, 9 Aug 2019 12:10:28 +0400
Subject: [PATCH] Fallback for short weapon groups

---
 apps/openmw/mwmechanics/character.cpp  | 71 ++++++++++++++++++--------
 apps/openmw/mwmechanics/character.hpp  |  2 +
 apps/openmw/mwmechanics/weapontype.hpp | 12 ++---
 3 files changed, 57 insertions(+), 28 deletions(-)

diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp
index 970654fe73..4d8868250c 100644
--- a/apps/openmw/mwmechanics/character.cpp
+++ b/apps/openmw/mwmechanics/character.cpp
@@ -335,17 +335,12 @@ void CharacterController::refreshJumpAnims(const std::string& weapShortGroup, Ju
             jumpAnimName += weapShortGroup;
             if(!mAnimation->hasAnimation(jumpAnimName))
             {
-                jumpmask = MWRender::Animation::BlendMask_LowerBody;
-                jumpAnimName = "jump";
+                jumpAnimName = fallbackShortWeaponGroup("jump", &jumpmask);
 
-                // Since we apply movement only for lower body, do not reset idle animations.
+                // If we apply jump only for lower body, do not reset idle animations.
                 // For upper body there will be idle animation.
-                if (idle == CharState_None)
+                if (jumpmask == MWRender::Animation::BlendMask_LowerBody && idle == CharState_None)
                     idle = CharState_Idle;
-
-                // For crossbow animations use 1h ones as fallback
-                if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
-                    jumpAnimName += "1h";
             }
         }
     }
@@ -419,6 +414,43 @@ void CharacterController::onClose()
     }
 }
 
+std::string CharacterController::fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask)
+{
+    bool isRealWeapon = mWeaponType != ESM::Weapon::HandToHand && mWeaponType != ESM::Weapon::Spell && mWeaponType != ESM::Weapon::None;
+    if (!isRealWeapon)
+    {
+        if (blendMask != nullptr)
+            *blendMask = MWRender::Animation::BlendMask_LowerBody;
+
+        return baseGroupName;
+    }
+
+    static const std::string oneHandFallback = getWeaponType(ESM::Weapon::LongBladeOneHand)->mShortGroup;
+    static const std::string twoHandFallback = getWeaponType(ESM::Weapon::LongBladeTwoHand)->mShortGroup;
+
+    std::string groupName = baseGroupName;
+    const ESM::WeaponType* weapInfo = getWeaponType(mWeaponType);
+
+    // For real two-handed melee weapons use 2h swords animations as fallback, otherwise use the 1h ones
+    if (isRealWeapon && weapInfo->mFlags & ESM::WeaponType::TwoHanded && weapInfo->mWeaponClass == ESM::WeaponType::Melee)
+        groupName += twoHandFallback;
+    else if (isRealWeapon)
+        groupName += oneHandFallback;
+
+    // Special case for crossbows - we shouls apply 1h animations a fallback only for lower body
+    if (mWeaponType == ESM::Weapon::MarksmanCrossbow && blendMask != nullptr)
+        *blendMask = MWRender::Animation::BlendMask_LowerBody;
+
+    if (!mAnimation->hasAnimation(groupName))
+    {
+        groupName = baseGroupName;
+        if (blendMask != nullptr)
+            *blendMask = MWRender::Animation::BlendMask_LowerBody;
+    }
+
+    return groupName;
+}
+
 void CharacterController::refreshMovementAnims(const std::string& weapShortGroup, CharacterState movement, CharacterState& idle, bool force)
 {
     if (movement == mMovementState && idle == mIdleState && !force)
@@ -449,15 +481,12 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
                 movementAnimName = movestate->groupname;
                 if (swimpos == std::string::npos)
                 {
-                    movemask = MWRender::Animation::BlendMask_LowerBody;
-                    // Since we apply movement only for lower body, do not reset idle animations.
-                    // For upper body there will be idle animation.
-                    if (idle == CharState_None)
-                        idle = CharState_Idle;
+                    movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
 
-                    // For crossbow animations use 1h ones as fallback
-                    if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
-                        movementAnimName += "1h";
+                    // 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;
                 }
                 else if (idle == CharState_None)
                 {
@@ -493,10 +522,6 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
                 }
                 else
                 {
-                    // For crossbow animations use 1h ones as fallback
-                    if (mWeaponType == ESM::Weapon::MarksmanCrossbow)
-                        movementAnimName += "1h";
-
                     movementAnimName.erase(swimpos, 4);
                     if (!weapShortGroup.empty())
                     {
@@ -504,7 +529,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
                         if(mAnimation->hasAnimation(weapMovementAnimName))
                             movementAnimName = weapMovementAnimName;
                         else
-                            movemask = MWRender::Animation::BlendMask_LowerBody;
+                            movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
                     }
 
                     if (!mAnimation->hasAnimation(movementAnimName))
@@ -607,7 +632,9 @@ void CharacterController::refreshIdleAnims(const std::string& weapShortGroup, Ch
             {
                 idleGroup += weapShortGroup;
                 if(!mAnimation->hasAnimation(idleGroup))
-                    idleGroup = "idle";
+                {
+                    idleGroup = fallbackShortWeaponGroup("idle");
+                }
 
                 // play until the Loop Stop key 2 to 5 times, then play until the Stop key
                 // this replicates original engine behavior for the "Idle1h" 1st-person animation
diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp
index 45f56de499..27b646e33a 100644
--- a/apps/openmw/mwmechanics/character.hpp
+++ b/apps/openmw/mwmechanics/character.hpp
@@ -230,6 +230,8 @@ class CharacterController : public MWRender::Animation::TextKeyListener
 
     bool updateCarriedLeftVisible(int weaptype) const;
 
+    std::string fallbackShortWeaponGroup(const std::string& baseGroupName, MWRender::Animation::BlendMask* blendMask = nullptr);
+
 public:
     CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
     virtual ~CharacterController();
diff --git a/apps/openmw/mwmechanics/weapontype.hpp b/apps/openmw/mwmechanics/weapontype.hpp
index 034e6d6b04..fcb3068bec 100644
--- a/apps/openmw/mwmechanics/weapontype.hpp
+++ b/apps/openmw/mwmechanics/weapontype.hpp
@@ -68,7 +68,7 @@ namespace MWMechanics
         {
             ESM::Weapon::ShortBladeOneHand,
             {
-                /* short group */ "1h",
+                /* short group */ "1s",
                 /* long group  */ "weapononehand",
                 /*  sound ID   */ "Item Weapon Shortblade",
                 /* attach bone */ "Weapon Bone",
@@ -96,7 +96,7 @@ namespace MWMechanics
         {
             ESM::Weapon::BluntOneHand,
             {
-                /* short group */ "1h",
+                /* short group */ "1b",
                 /* long group  */ "weapononehand",
                 /*  sound ID   */ "Item Weapon Blunt",
                 /* attach bone */ "Weapon Bone",
@@ -110,7 +110,7 @@ namespace MWMechanics
         {
             ESM::Weapon::AxeOneHand,
             {
-                /* short group */ "1h",
+                /* short group */ "1b",
                 /* long group  */ "weapononehand",
                 /*  sound ID   */ "Item Weapon Blunt",
                 /* attach bone */ "Weapon Bone",
@@ -138,7 +138,7 @@ namespace MWMechanics
         {
             ESM::Weapon::AxeTwoHand,
             {
-                /* short group */ "2c",
+                /* short group */ "2b",
                 /* long group  */ "weapontwohand",
                 /*  sound ID   */ "Item Weapon Blunt",
                 /* attach bone */ "Weapon Bone",
@@ -152,7 +152,7 @@ namespace MWMechanics
         {
             ESM::Weapon::BluntTwoClose,
             {
-                /* short group */ "2c",
+                /* short group */ "2b",
                 /* long group  */ "weapontwohand",
                 /*  sound ID   */ "Item Weapon Blunt",
                 /* attach bone */ "Weapon Bone",
@@ -194,7 +194,7 @@ namespace MWMechanics
         {
             ESM::Weapon::MarksmanBow,
             {
-                /* short group */ "1h",
+                /* short group */ "bow",
                 /* long group  */ "bowandarrow",
                 /*  sound ID   */ "Item Weapon Bow",
                 /* attach bone */ "Weapon Bone Left",