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)) {