Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 43 additions & 118 deletions soh/soh/Enhancements/ArrowCycle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ extern "C" {
#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h"

s32 func_808351D4(Player* thisx, PlayState* play); // Arrow nocked
s32 func_808353D8(Player* thisx, PlayState* play); // Aiming in first person
void Player_InitItemAction(PlayState* play, Player* thisx, PlayerItemAction itemAction);
void EnArrow_Init(Actor* thisx, PlayState* play);

extern PlayState* gPlayState;
}
Expand All @@ -22,14 +21,6 @@ static const s16 sMagicArrowCosts[] = { 4, 4, 8 };

#define MINIGAME_STATUS_ACTIVE 1

static const s16 BUTTON_FLASH_DURATION = 3;
static const s16 BUTTON_FLASH_COUNT = 3;
static const s16 BUTTON_HIGHLIGHT_ALPHA = 128;

static s16 sButtonFlashTimer = 0;
static s16 sButtonFlashCount = 0;
static s16 sJustCycledFrames = 0;

static const PlayerItemAction sArrowCycleOrder[] = {
PLAYER_IA_BOW,
PLAYER_IA_BOW_FIRE,
Expand All @@ -54,11 +45,11 @@ static bool HasArrowType(PlayerItemAction itemAction) {
case PLAYER_IA_BOW:
return true;
case PLAYER_IA_BOW_FIRE:
return (INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_ARROW_FIRE);
return INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_ARROW_FIRE;
case PLAYER_IA_BOW_ICE:
return (INV_CONTENT(ITEM_ARROW_ICE) == ITEM_ARROW_ICE);
return INV_CONTENT(ITEM_ARROW_ICE) == ITEM_ARROW_ICE;
case PLAYER_IA_BOW_LIGHT:
return (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT);
return INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT;
default:
return false;
}
Expand All @@ -77,11 +68,24 @@ static s32 GetBowItemForArrow(PlayerItemAction itemAction) {
}
}

static ArrowType GetArrowTypeForArrow(s8 itemAction) {
switch (itemAction) {
case PLAYER_IA_BOW_FIRE:
return ARROW_FIRE;
case PLAYER_IA_BOW_ICE:
return ARROW_ICE;
case PLAYER_IA_BOW_LIGHT:
return ARROW_LIGHT;
default:
return ARROW_NORMAL;
}
}

static bool CanCycleArrows() {
Player* player = GET_PLAYER(gPlayState);

// don't allow cycling during minigames
if (gSaveContext.minigameState == MINIGAME_STATUS_ACTIVE) {
if (gSaveContext.minigameState == MINIGAME_STATUS_ACTIVE || gPlayState->sceneNum == SCENE_SHOOTING_GALLERY) {
return false;
}

Expand Down Expand Up @@ -113,66 +117,6 @@ static s8 GetNextArrowType(s8 currentArrowType) {
static void UpdateButtonAlpha(s16 flashAlpha, bool isButtonBow, u16* buttonAlpha) {
if (isButtonBow) {
*buttonAlpha = flashAlpha;
if (sButtonFlashTimer == 0) {
*buttonAlpha = 255;
}
}
}

static void UpdateFlashEffect(PlayState* play) {
if (sButtonFlashTimer <= 0) {
return;
}

sButtonFlashTimer--;
s16 flashAlpha = (sButtonFlashTimer % 3) ? BUTTON_HIGHLIGHT_ALPHA : 255;

if (sButtonFlashTimer == 0 && sButtonFlashCount < BUTTON_FLASH_COUNT - 1) {
sButtonFlashTimer = BUTTON_FLASH_DURATION;
sButtonFlashCount++;
}
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[1] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[1] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[1] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.cLeftAlpha);

UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[2] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[2] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[2] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.cDownAlpha);

UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[3] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[3] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[3] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.cRightAlpha);

if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0)) {
UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[4] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[4] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[4] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadRightAlpha);

UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[5] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[5] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[5] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadLeftAlpha);

UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[6] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[6] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[6] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadDownAlpha);

UpdateButtonAlpha(flashAlpha,
(gSaveContext.equips.buttonItems[7] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[7] >= ITEM_BOW_ARROW_FIRE &&
gSaveContext.equips.buttonItems[7] <= ITEM_BOW_ARROW_LIGHT),
&play->interfaceCtx.dpadUpAlpha);
}
}

Expand All @@ -193,72 +137,53 @@ static void UpdateEquippedBow(PlayState* play, s8 arrowType) {
}

gSaveContext.buttonStatus[i] = BTN_ENABLED;
sButtonFlashTimer = BUTTON_FLASH_DURATION;
sButtonFlashCount = 0;
}
}

UpdateFlashEffect(play);
}

static void CycleToNextArrow(PlayState* play, Player* player) {
s8 nextArrow = GetNextArrowType(player->heldItemAction);

if (player->heldActor != NULL && player->heldActor->id == ACTOR_EN_ARROW) {
EnArrow* arrow = (EnArrow*)player->heldActor;

if (arrow->actor.child != NULL) {
Actor_Kill(arrow->actor.child);
}

Actor_Kill(&arrow->actor);
}

Player_InitItemAction(play, player, (PlayerItemAction)nextArrow);
UpdateEquippedBow(play, nextArrow);
Audio_PlaySoundGeneral(NA_SE_PL_CHANGE_ARMS, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
sJustCycledFrames = 2;
}

void ArrowCycleMain() {
bool ArrowCycleMain() {
if (gPlayState == nullptr || !CanCycleArrows()) {
return;
}

if (sJustCycledFrames > 0) {
sJustCycledFrames--;
return false;
}

UpdateFlashEffect(gPlayState);

Player* player = GET_PLAYER(gPlayState);
Input* input = &gPlayState->state.input[0];

if (IsAimingBow(player) && CHECK_BTN_ANY(input->press.button, BTN_R)) {
if (player->heldActor != NULL && player->heldActor->id == ACTOR_EN_ARROW) {
if (IsHoldingMagicBow(player) && gSaveContext.magicState != MAGIC_STATE_IDLE && player->heldActor == NULL) {
Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
return;
return true;
}

// reset magic state to IDLE before cycling to prevent error sound
gSaveContext.magicState = MAGIC_STATE_IDLE;

CycleToNextArrow(gPlayState, player);
s8 nextArrow = GetNextArrowType(player->heldItemAction);
player->heldItemAction = nextArrow;
player->itemAction = nextArrow;
Actor* arrow = player->heldActor;

if (arrow->child != NULL) {
Actor_Kill(arrow->child);
arrow->child = NULL;
}
arrow->params = GetArrowTypeForArrow(nextArrow);
EnArrow_Init(arrow, gPlayState);
UpdateEquippedBow(gPlayState, nextArrow);
return true;
}
return false;
}

void RegisterArrowCycle() {
COND_ID_HOOK(OnActorUpdate, ACTOR_PLAYER, CVAR_ARROW_CYCLE_VALUE, [](void* actor) { ArrowCycleMain(); });

// suppress shield input when R is held while aiming to allow arrow cycling
COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_ARROW_CYCLE_VALUE, {
Player* player = (Player*)va_arg(args, void*);
Input* input = (Input*)va_arg(args, void*);
if ((IsAimingBow(player) || sJustCycledFrames > 0) && CHECK_BTN_ANY(input->cur.button, BTN_R)) {
input->cur.button &= ~BTN_R;
input->press.button &= ~BTN_R;
if (IsAimingBow(player) && CHECK_BTN_ANY(input->press.button, BTN_R)) {
if (ArrowCycleMain()) {
input->cur.button &= ~BTN_R;
input->press.button &= ~BTN_R;
}
}
});

Expand All @@ -270,9 +195,9 @@ void RegisterArrowCycle() {

if (gSaveContext.magic < sMagicArrowCosts[magicArrowType]) {
*arrowType = ARROW_NORMAL;
} else {
*should = false;
}

*should = false;
});

COND_VB_SHOULD(VB_EN_ARROW_MAGIC_CONSUMPTION, CVAR_ARROW_CYCLE_VALUE, {
Expand Down
Loading