From 64ebfa222eac018365c82fdd22cdce7c81978c0f Mon Sep 17 00:00:00 2001 From: Eblo <7004497+Eblo@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:38:51 -0400 Subject: [PATCH] [Enhancement] Two-handed sword spin attacks (#679) * Enhancement to enable magic for 2-handed swords Create a new magic charge size for the two-handed swords Add utility method for determining if the held item's button is being pressed, not just B * Clean up two-handed sword spin attacks Add hook for thunder matrix transformation, move that logic into TwoHandedSwordSpinAttack Move held item button press function into TwoHandedSwordSpinAttack.cpp Add enhancement to the searchable menu Clean up logic for magic spin attack form check Use variadic args for hooks * Remove redundant includes from TwoHandedSwordSpinAttack.cpp * Use extern on TwoHandedSwordSpinAttack structs --- mm/2s2h/BenGui/BenMenuBar.cpp | 3 + mm/2s2h/BenGui/SearchableMenuItems.h | 3 + mm/2s2h/Enhancements/Enhancements.cpp | 1 + mm/2s2h/Enhancements/Equipment/Equipment.h | 1 + .../Equipment/TwoHandedSwordSpinAttack.cpp | 57 +++++++++++++++++++ mm/2s2h/GameInteractor/GameInteractor.h | 3 + .../actors/ovl_En_M_Thunder/z_en_m_thunder.c | 19 ++++--- .../actors/ovl_player_actor/z_player.c | 23 ++++++-- 8 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 mm/2s2h/Enhancements/Equipment/TwoHandedSwordSpinAttack.cpp diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index 93152da61..ddd988877 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -578,6 +578,9 @@ void DrawEnhancementsMenu() { { .tooltip = "Pressing B will instantly recall the fin boomerang back to Zora Link after they are thrown." }); + UIWidgets::CVarCheckbox( + "Two-Handed Sword Spin Attack", "gEnhancements.Equipment.TwoHandedSwordSpinAttack", + { .tooltip = "Enables magic spin attacks for the Fierce Deity Sword and Great Fairy's Sword." }); ImGui::EndMenu(); } diff --git a/mm/2s2h/BenGui/SearchableMenuItems.h b/mm/2s2h/BenGui/SearchableMenuItems.h index 1bff76ce3..4131b5941 100644 --- a/mm/2s2h/BenGui/SearchableMenuItems.h +++ b/mm/2s2h/BenGui/SearchableMenuItems.h @@ -1027,6 +1027,9 @@ void AddEnhancements() { "Removes the animation for equipping Magic Arrows.", WIDGET_CVAR_CHECKBOX }, { "Instant Fin Boomerangs Recall", "gEnhancements.PlayerActions.InstantRecall", "Pressing B will instantly recall the fin boomerang back to Zora Link after they are thrown.", + WIDGET_CVAR_CHECKBOX }, + { "Two-Handed Sword Spin Attack", "gEnhancements.Equipment.TwoHandedSwordSpinAttack", + "Enables magic spin attacks for the Fierce Deity Sword and Great Fairy's Sword.", WIDGET_CVAR_CHECKBOX } }, { { .widgetName = "Modes", .widgetType = WIDGET_SEPARATOR_TEXT }, { "Play as Kafei", "gModes.PlayAsKafei", "Requires scene reload to take effect.", WIDGET_CVAR_CHECKBOX }, diff --git a/mm/2s2h/Enhancements/Enhancements.cpp b/mm/2s2h/Enhancements/Enhancements.cpp index e0518fdc2..22c97aa68 100644 --- a/mm/2s2h/Enhancements/Enhancements.cpp +++ b/mm/2s2h/Enhancements/Enhancements.cpp @@ -34,6 +34,7 @@ void InitEnhancements() { // Fixes RegisterFierceDeityZTargetMovement(); + RegisterTwoHandedSwordSpinAttack(); // Graphics RegisterDisableBlackBars(); diff --git a/mm/2s2h/Enhancements/Equipment/Equipment.h b/mm/2s2h/Enhancements/Equipment/Equipment.h index ca23748c9..849c10257 100644 --- a/mm/2s2h/Enhancements/Equipment/Equipment.h +++ b/mm/2s2h/Enhancements/Equipment/Equipment.h @@ -3,5 +3,6 @@ void RegisterInstantRecall(); void RegisterSkipMagicArrowEquip(); +void RegisterTwoHandedSwordSpinAttack(); #endif // EQUIPMENT_H diff --git a/mm/2s2h/Enhancements/Equipment/TwoHandedSwordSpinAttack.cpp b/mm/2s2h/Enhancements/Equipment/TwoHandedSwordSpinAttack.cpp new file mode 100644 index 000000000..64d6c706f --- /dev/null +++ b/mm/2s2h/Enhancements/Equipment/TwoHandedSwordSpinAttack.cpp @@ -0,0 +1,57 @@ +#include +#include "2s2h/GameInteractor/GameInteractor.h" +#include "overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.h" + +extern "C" { +extern PlayState* gPlayState; +extern Input* sPlayerControlInput; +} + +void RegisterTwoHandedSwordSpinAttack() { + + REGISTER_VB_SHOULD(VB_CHECK_HELD_ITEM_BUTTON_PRESS, { + if (CVarGetInteger("gEnhancements.Equipment.TwoHandedSwordSpinAttack", 0)) { + // Instead of checking B only, check whichever button is assigned to the currently held item. This allows + // the Great Fairy Sword, a C button item, to be held for charged spin attacks. + Player* player = GET_PLAYER(gPlayState); + u16* sDpadItemButtons = va_arg(args, u16*); + u16* sPlayerItemButtons = va_arg(args, u16*); + + if (player->heldItemButton < 0) { + *should = false; + } else { + uint16_t buttonToCheck; + if (IS_HELD_DPAD(player->heldItemButton)) { + buttonToCheck = sDpadItemButtons[HELD_ITEM_TO_DPAD(player->heldItemButton)]; + } else { + buttonToCheck = sPlayerItemButtons[player->heldItemButton]; + } + *should = CHECK_BTN_ALL(sPlayerControlInput->cur.button, buttonToCheck); + } + } + }); + + REGISTER_VB_SHOULD(VB_MAGIC_SPIN_ATTACK_CHECK_FORM, { + if (CVarGetInteger("gEnhancements.Equipment.TwoHandedSwordSpinAttack", 0)) { + // Additionally allow the Fierce Deity form to use charged spin attacks + PlayerTransformation form = va_arg(args, PlayerTransformation); + if (form == PLAYER_FORM_FIERCE_DEITY) { + *should = true; + } + } + }); + + REGISTER_VB_SHOULD(VB_TRANSFORM_THUNDER_MATRIX, { + if (CVarGetInteger("gEnhancements.Equipment.TwoHandedSwordSpinAttack", 0)) { + EnMThunder* enMThunder = va_arg(args, EnMThunder*); + // Define new thunder matrix transformation for two-handed sword magic. Applies to both the GFS and the + // Fierce Deity sword. + if (enMThunder->type == ENMTHUNDER_TYPE_GREAT_FAIRYS_SWORD) { + Matrix_Translate(0.0f, 220.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(-1.7f, -1.0f, -0.6f, MTXMODE_APPLY); + Matrix_RotateXS(0x4000, MTXMODE_APPLY); + *should = false; + } + } + }); +} diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index 28b4936d5..253690740 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -69,6 +69,9 @@ typedef enum { VB_SHOULD_PUTAWAY, VB_ELEGY_CHECK_SCENE, VB_NEED_SCARECROW_SONG, + VB_CHECK_HELD_ITEM_BUTTON_PRESS, + VB_MAGIC_SPIN_ATTACK_CHECK_FORM, + VB_TRANSFORM_THUNDER_MATRIX, } GIVanillaBehavior; typedef enum { diff --git a/mm/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c b/mm/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c index 766d832c0..47929fb60 100644 --- a/mm/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c +++ b/mm/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c @@ -8,6 +8,7 @@ #include "z64rumble.h" #include "overlays/actors/ovl_Eff_Dust/z_eff_dust.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "2s2h/GameInteractor/GameInteractor.h" #define FLAGS (ACTOR_FLAG_10) @@ -543,14 +544,16 @@ void EnMThunder_Draw(Actor* thisx, PlayState* play2) { Matrix_Mult(&player->leftHandMf, MTXMODE_NEW); - if (this->type == ENMTHUNDER_TYPE_GILDED_SWORD) { - Matrix_Translate(0.0f, 220.0f, 0.0f, MTXMODE_APPLY); - Matrix_Scale(-1.2f, -0.8f, -0.6f, MTXMODE_APPLY); - Matrix_RotateXS(0x4000, MTXMODE_APPLY); - } else { - Matrix_Translate(0.0f, 220.0f, 0.0f, MTXMODE_APPLY); - Matrix_Scale(-0.7f, -0.6f, -0.4f, MTXMODE_APPLY); - Matrix_RotateXS(0x4000, MTXMODE_APPLY); + if (GameInteractor_Should(VB_TRANSFORM_THUNDER_MATRIX, true, this)) { + if (this->type == ENMTHUNDER_TYPE_GILDED_SWORD) { + Matrix_Translate(0.0f, 220.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(-1.2f, -0.8f, -0.6f, MTXMODE_APPLY); + Matrix_RotateXS(0x4000, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, 220.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(-0.7f, -0.6f, -0.4f, MTXMODE_APPLY); + Matrix_RotateXS(0x4000, MTXMODE_APPLY); + } } if (this->unk1B0 >= 0.85f) { diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index 11817644e..83eb3374f 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -5268,7 +5268,9 @@ void func_808332A0(PlayState* play, Player* this, s32 magicCost, s32 isSwordBeam } this->stateFlags1 |= PLAYER_STATE1_1000; - if ((this->actor.id == ACTOR_PLAYER) && (isSwordBeam || (this->transformation == PLAYER_FORM_HUMAN))) { + if ((this->actor.id == ACTOR_PLAYER) && + (isSwordBeam || (GameInteractor_Should(VB_MAGIC_SPIN_ATTACK_CHECK_FORM, + this->transformation == PLAYER_FORM_HUMAN, this->transformation)))) { s16 pitch = 0; Actor* thunder; @@ -8192,13 +8194,16 @@ s32 func_8083A4A4(Player* this, f32* arg1, s16* arg2, f32 arg3) { } void func_8083A548(Player* this) { - if ((this->unk_ADC > 0) && !CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B)) { + if ((this->unk_ADC > 0) && + !GameInteractor_Should(VB_CHECK_HELD_ITEM_BUTTON_PRESS, CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B), + sDpadItemButtons, sPlayerItemButtons)) { this->unk_ADC = -this->unk_ADC; } } s32 Player_ActionChange_8(Player* this, PlayState* play) { - if (CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B)) { + if (GameInteractor_Should(VB_CHECK_HELD_ITEM_BUTTON_PRESS, CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B), + sDpadItemButtons, sPlayerItemButtons)) { if (!(this->stateFlags1 & PLAYER_STATE1_400000) && (Player_GetMeleeWeaponHeld(this) != PLAYER_MELEEWEAPON_NONE)) { if ((this->unk_ADC > 0) && (((this->transformation == PLAYER_FORM_ZORA)) || @@ -10462,7 +10467,9 @@ s32 func_80840A30(PlayState* play, Player* this, f32* arg2, f32 arg3) { s32 func_80840CD4(Player* this, PlayState* play) { if (Player_StartCsAction(play, this)) { this->stateFlags2 |= PLAYER_STATE2_20000; - } else if (!CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B)) { + } else if (!GameInteractor_Should(VB_CHECK_HELD_ITEM_BUTTON_PRESS, + CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B), sDpadItemButtons, + sPlayerItemButtons)) { PlayerMeleeWeaponAnimation meleeWeaponAnim; if ((this->unk_B08 >= 0.85f) || func_808333CC(this)) { @@ -14561,7 +14568,9 @@ void Player_Action_12(Player* this, PlayState* play) { if (!func_80847880(play, this)) { if (!Player_TryActionChangeList(play, this, sPlayerActionChangeList7, false) || (Player_Action_12 == this->actionFunc)) { - if (!CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B)) { + if (!GameInteractor_Should(VB_CHECK_HELD_ITEM_BUTTON_PRESS, + CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B), sDpadItemButtons, + sPlayerItemButtons)) { func_80839E74(this, play); } } @@ -15246,7 +15255,9 @@ void Player_Action_30(Player* this, PlayState* play) { if (this->unk_B08 >= 0.1f) { this->unk_ADD = 0; this->av2.actionVar2 = 1; - } else if (!CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B)) { + } else if (!GameInteractor_Should(VB_CHECK_HELD_ITEM_BUTTON_PRESS, + CHECK_BTN_ALL(sPlayerControlInput->cur.button, BTN_B), sDpadItemButtons, + sPlayerItemButtons)) { func_80840E5C(this, play); } } else if (!func_80840CD4(this, play)) {