diff --git a/soh/soh/Enhancements/Fixes/FixTwoHandedIdleAnim.cpp b/soh/soh/Enhancements/Fixes/FixTwoHandedIdleAnim.cpp new file mode 100644 index 000000000..08bec0564 --- /dev/null +++ b/soh/soh/Enhancements/Fixes/FixTwoHandedIdleAnim.cpp @@ -0,0 +1,59 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "macros.h" +#include "functions.h" +} + +#define FIDGET_SWORD_SWING 9 +#define FIDGET_ADJUST_SHIELD 12 + +static constexpr int32_t CVAR_FIX_TWO_HANDED_IDLE_DEFAULT = 0; +#define CVAR_FIX_TWO_HANDED_IDLE_NAME CVAR_ENHANCEMENT("TwoHandedIdle") +#define CVAR_FIX_TWO_HANDED_IDLE_VALUE CVarGetInteger(CVAR_FIX_TWO_HANDED_IDLE_NAME, CVAR_FIX_TWO_HANDED_IDLE_DEFAULT) + +// clang-format off +static RegisterShipInitFunc initFunc([]() { + COND_VB_SHOULD(VB_SET_IDLE_ANIM, CVAR_FIX_TWO_HANDED_IDLE_VALUE, { + Player* player = va_arg(args, Player*); + s32 commonType = va_arg(args, s32); + + // Fixes a bug here where the condition for reaching two-handed idle animation was impossible. Original condition: + /* + ( + ( + (commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) && + (commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD) + ) || + ( + (player->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && + ( + (commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) || + // This should not have been grouped here, because two handed melee weapons do not have shield. + (Player_GetMeleeWeaponHeld2(player) != 0) + ) + ) + ) + */ + + *should = ( + // Animation is not FIDGET_SWORD_SWING and FIDGET_ADJUST_SHIELD (So it's either FIDGET_ADJUST_TUNIC or FIDGET_TAP_FEET) + ( + (commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) && + (commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD) + ) || + // Animation is FIDGET_ADJUST_SHIELD and player is holding a shield in right hand + ( + (player->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && + (commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) + ) || + // Animation is FIDGET_SWORD_SWING and player is holding a melee weapon + ( + (commonType + FIDGET_SWORD_SWING == FIDGET_SWORD_SWING) && + (Player_GetMeleeWeaponHeld(player) != 0) + ) + ); + }); +}, { CVAR_FIX_TWO_HANDED_IDLE_NAME }); +// clang-format on diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index bee6ba72d..eef6bbb39 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2327,6 +2327,27 @@ typedef enum { // - `*Player` VB_SET_STATIC_FLOOR_TYPE, + // #### `result` + // ```c + // ( + // ( + // (commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) && + // (commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD) + // ) || + // ( + // (player->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && + // ( + // (commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) || + // (Player_GetMeleeWeaponHeld2(player) != 0) + // ) + // ) + // ) + // ``` + // #### `args` + // - `Player*` + // - `s32` commonType + VB_SET_IDLE_ANIM, + } GIVanillaBehavior; #endif diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 7da8447c0..e6d73a529 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -8293,19 +8293,20 @@ void Player_ChooseNextIdleAnim(PlayState* play, Player* this) { // // Note that `FIDGET_SWORD_SWING` is the first common fidget type, which is why // all operations are done relative to this type. - if (((commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) && - (commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)) || - ((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && - ((commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) || - (Player_GetMeleeWeaponHeld2(this) != 0)))) { + if (GameInteractor_Should(VB_SET_IDLE_ANIM, + (((commonType + FIDGET_SWORD_SWING != FIDGET_SWORD_SWING) && + (commonType + FIDGET_SWORD_SWING != FIDGET_ADJUST_SHIELD)) || + ((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && + ((commonType + FIDGET_SWORD_SWING == FIDGET_ADJUST_SHIELD) || + (Player_GetMeleeWeaponHeld2(this) != 0)))), + this, commonType)) { //! @bug It is possible for `FIDGET_ADJUST_SHIELD` to be used even if //! a shield is not currently equipped. This is because of how being shieldless //! is implemented. There is no sword-only model type, only //! `PLAYER_MODELGROUP_SWORD_AND_SHIELD` exists. Therefore, the right hand type will be //! `PLAYER_MODELTYPE_RH_SHIELD` if sword is in hand, even if no shield is equipped. if ((commonType + FIDGET_SWORD_SWING == FIDGET_SWORD_SWING) && - Player_HoldsTwoHandedWeapon(this) && - CVarGetInteger(CVAR_ENHANCEMENT("TwoHandedIdle"), 0) == 1) { + Player_HoldsTwoHandedWeapon(this)) { //! @bug This code is unreachable. //! The check above groups the `Player_GetMeleeWeaponHeld2` check and //! `PLAYER_MODELTYPE_RH_SHIELD` conditions together, meaning sword and shield must be