[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
This commit is contained in:
Eblo
2024-10-10 14:38:51 -05:00
committed by GitHub
parent 0129959d7b
commit 64ebfa222e
8 changed files with 96 additions and 14 deletions
+3
View File
@@ -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();
}
+3
View File
@@ -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 },
+1
View File
@@ -34,6 +34,7 @@ void InitEnhancements() {
// Fixes
RegisterFierceDeityZTargetMovement();
RegisterTwoHandedSwordSpinAttack();
// Graphics
RegisterDisableBlackBars();
@@ -3,5 +3,6 @@
void RegisterInstantRecall();
void RegisterSkipMagicArrowEquip();
void RegisterTwoHandedSwordSpinAttack();
#endif // EQUIPMENT_H
@@ -0,0 +1,57 @@
#include <libultraship/bridge.h>
#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;
}
}
});
}
+3
View File
@@ -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 {
@@ -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) {
@@ -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)) {