Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arrow Switching #1

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
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
52 changes: 52 additions & 0 deletions soh/soh/Enhancements/arrow-switching/arrow_switching.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "arrow_switching.h"

#include <global.h>


s32 func_808351D4(Player* this, PlayState* play); // Arrow nocked
s32 func_808353D8(Player* this, PlayState* play); // Aiming in first person

typedef struct {
u8 asArrow;
u8 asBowArrow;
} ArrowItems;

static ArrowItems arrowTypeToItem[] = {
/* normal arrows */ { ITEM_BOW, ITEM_BOW },
/* fire arrows */ { ITEM_ARROW_FIRE, ITEM_BOW_ARROW_FIRE },
/* ice arrows */ { ITEM_ARROW_ICE, ITEM_BOW_ARROW_ICE },
/* light arrows */ { ITEM_ARROW_LIGHT, ITEM_BOW_ARROW_LIGHT },
/* unused arrow types are excluded from cycling */
};

// Returns true if the player is in a state where they can switch arrows.
// Specifically, the gArrowSwitching CVar is enabled, the player is holding the
// bow with normal, fire, ice, or light arrows, and they're either aiming or
// have an arrow notched.
bool ArrowSwitching_CanSwitch(Player* player) {
if (!CVarGetInteger("gArrowSwitching", 0)) {
return false;
}

if (player->heldItemAction < PLAYER_IA_BOW || player->heldItemAction > PLAYER_IA_BOW_LIGHT) {
return false;
}

return player->func_82C == func_808351D4 || player->func_82C == func_808353D8;
}

bool ArrowSwitching_Next(u8 currentItemAction, u8* item, u8* itemAction) {
const u8 arrowCount = ARRAY_COUNT(arrowTypeToItem);
u8 heldArrowIA = currentItemAction - PLAYER_IA_BOW;
u8 i;
for (i = 1; i < arrowCount; i++) {
u8 arrowIA = (heldArrowIA + i) % arrowCount;
ArrowItems items = arrowTypeToItem[arrowIA];
if (INV_CONTENT(items.asArrow) != ITEM_NONE) {
*item = items.asBowArrow;
*itemAction = PLAYER_IA_BOW + arrowIA;
break;
}
}
return i != arrowCount;
}
9 changes: 9 additions & 0 deletions soh/soh/Enhancements/arrow-switching/arrow_switching.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef ENHANCEMENTS_ARROW_SWITCHING_H
#define ENHANCEMENTS_ARROW_SWITCHING_H

#include <z64.h>

bool ArrowSwitching_CanSwitch(Player*);
bool ArrowSwitching_Next(u8 currentItemAction, u8* item, u8* itemAction);

#endif
22 changes: 22 additions & 0 deletions soh/soh/Enhancements/controls/GameControlEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ namespace GameControlEditor {
static CustomButtonMap ocarinaSharp = {"Pitch up", "gOcarinaSharpBtnMap", BTN_R};
static CustomButtonMap ocarinaFlat = {"Pitch down", "gOcarinaFlatBtnMap", BTN_Z};

// Misc.
static CustomButtonMap arrowSwitch = {"Switch arrows", "gArrowSwitchBtnMap", BTN_R};

void GameControlEditorWindow::InitElement() {
addButtonName(BTN_A, "A");
addButtonName(BTN_B, "B");
Expand Down Expand Up @@ -332,6 +335,25 @@ namespace GameControlEditor {
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");

float longestLabelWidth = ImGui::CalcTextSize(arrowSwitch.label).x + 10;

// Switch arrows
bool arrowSwitchingEnabled = CVarGetInteger("gArrowSwitching", 0);
if (!arrowSwitchingEnabled) {
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
}
N64ButtonMask arrowSwitchAllowedButtons = BTN_A | BTN_L | BTN_R | BTN_CUP;
DrawMapping(arrowSwitch, longestLabelWidth, ~arrowSwitchAllowedButtons);
if (!arrowSwitchingEnabled) {
ImGui::PopStyleVar(1);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("%s", "This option is disabled because Arrow Switching from Enhancements > Gameplay > Time Savers is disabled");
}
ImGui::PopItemFlag();
}

window->EndGroupPanelPublic(0);
}

Expand Down
1 change: 1 addition & 0 deletions soh/soh/Enhancements/presets.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ const std::vector<const char*> enhancementsCvars = {
"gBombchuBowlingNoBigCucco",
"gBombchuBowlingAmmunition",
"gCreditsFix",
"gArrowSwitching",
"gSilverRupeeJingleExtend",
"gStaticExplosionRadius",
"gNoInputForCredits",
Expand Down
7 changes: 5 additions & 2 deletions soh/soh/SohMenuBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,9 @@ void DrawEnhancementsMenu() {
UIWidgets::Tooltip("Makes nuts explode bombs, similar to how they interact with bombchus. This does not affect bombflowers.");
UIWidgets::PaddedEnhancementCheckbox("Equip Multiple Arrows at Once", "gSeparateArrows", true, false);
UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots. (Note this will disable the behaviour of the 'Equip Dupe' glitch)");
UIWidgets::PaddedEnhancementCheckbox("Switch Arrow Types", "gArrowSwitching", true, false);
UIWidgets::Tooltip("Press R with the bow out to switch between normal, fire, ice, and light arrows\n"
"Use the \"Customize Game Controls\" window to switch with a different button");
UIWidgets::PaddedEnhancementCheckbox("Bow as Child/Slingshot as Adult", "gBowSlingShotAmmoFix", true, false);
UIWidgets::Tooltip("Allows child to use bow with arrows.\nAllows adult to use slingshot with seeds.\n\nRequires glitches or 'Timeless Equipment' cheat to equip.");
UIWidgets::PaddedEnhancementCheckbox("Better Farore's Wind", "gBetterFW", true, false);
Expand Down Expand Up @@ -1090,8 +1093,8 @@ void DrawEnhancementsMenu() {
"Fixes an incorrect calculation that acted like water underneath ground was above it.");
UIWidgets::PaddedEnhancementCheckbox("Fix Bush Item Drops", "gBushDropFix", true, false);
UIWidgets::Tooltip("Fixes the bushes to drop items correctly rather than spawning undefined items.");
UIWidgets::PaddedEnhancementCheckbox("Fix falling from vine edges", "gFixVineFall", true, false);
UIWidgets::Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges.");
UIWidgets::PaddedEnhancementCheckbox("Fix falling from vine edges", "gFixVineFall", true, false);
UIWidgets::Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges.");
UIWidgets::PaddedEnhancementCheckbox("Fix Link's eyes open while sleeping", "gFixEyesOpenWhileSleeping", true, false);
UIWidgets::Tooltip("Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping.");
UIWidgets::PaddedEnhancementCheckbox("Fix Darunia dancing too fast", "gEnhancements.FixDaruniaDanceSpeed",
Expand Down
7 changes: 6 additions & 1 deletion soh/src/code/z_map_exp.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "textures/parameter_static/parameter_static.h"
#include "textures/map_i_static/map_i_static.h"
#include "textures/map_grand_static/map_grand_static.h"
#include "soh/Enhancements/arrow-switching/arrow_switching.h"
#include <assert.h>

MapData* gMapData;
Expand Down Expand Up @@ -963,7 +964,11 @@ void Minimap_Draw(PlayState* play) {
Minimap_DrawCompassIcons(play); // Draw icons for the player spawn and current position
}

if (CHECK_BTN_ALL(play->state.input[0].press.button, BTN_L) && !Play_InCsMode(play) && enableMapToggle) {
u16 minimapButton = BTN_L;
if (ArrowSwitching_CanSwitch(GET_PLAYER(play))) {
minimapButton &= ~CVarGetInteger("gArrowSwitchBtnMap", BTN_R);
}
if (minimapButton && CHECK_BTN_ALL(play->state.input[0].press.button, minimapButton) && !Play_InCsMode(play) && enableMapToggle) {
// clang-format off
if (!R_MINIMAP_DISABLED) { Audio_PlaySoundGeneral(NA_SE_SY_CAMERA_ZOOM_UP, &D_801333D4, 4,
&D_801333E0, &D_801333E0, &D_801333E8); }
Expand Down
99 changes: 92 additions & 7 deletions soh/src/overlays/actors/ovl_player_actor/z_player.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "objects/object_link_child/object_link_child.h"
#include "textures/icon_item_24_static/icon_item_24_static.h"
#include <soh/Enhancements/custom-message/CustomMessageTypes.h>
#include "soh/Enhancements/arrow-switching/arrow_switching.h"
#include "soh/Enhancements/item-tables/ItemTableTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
Expand Down Expand Up @@ -2218,6 +2219,59 @@ s32 func_80834380(PlayState* play, Player* this, s32* itemPtr, s32* typePtr) {
}
}

static bool gSwitchingArrow = false;


bool Player_SwitchArrowsIfEnabled(Player* this) {
if (!CVarGetInteger("gArrowSwitching", 0)) {
return false;
}
if (this->heldItemAction < PLAYER_IA_BOW || this->heldItemAction > PLAYER_IA_BOW_LIGHT) {
return false;
}
if (!CHECK_BTN_ANY(sControlInput->press.button, CVarGetInteger("gArrowSwitchBtnMap", BTN_R))) {
return false;
}

u8 newItem, newItemAction;
if (!ArrowSwitching_Next(this->heldItemAction, &newItem, &newItemAction)) {
return false;
}

gSaveContext.equips.buttonItems[this->heldItemButton] = newItem;
this->heldItemId = newItem;
this->itemAction = newItemAction;
this->heldItemAction = newItemAction;
return true;
}

// Replace previously nocked arrow with the new arrow type
void Player_ReplaceSwitchedArrow(PlayState* play, Player* this) {
s32 item, arrowType;
if (this->unk_860 < 0 || func_80834380(play, this, &item, &arrowType) <= 0) {
return;
}

s32 newMagicArrowType = arrowType - ARROW_FIRE;
s32 arrowCost;
if (newMagicArrowType < 0 || newMagicArrowType > 2) {
arrowCost = 0;
} else {
arrowCost = sMagicArrowCosts[newMagicArrowType];
}

if (arrowCost == 0 || !Magic_RequestChange(play, arrowCost, MAGIC_CONSUME_WAIT_PREVIEW)) {
arrowType = ARROW_NORMAL;
}

if (this->heldActor != NULL) {
Actor_Kill(this->heldActor);
}
this->heldActor = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_ARROW,
this->actor.world.pos.x, this->actor.world.pos.y,
this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, arrowType);
}

// The player has pressed the bow or hookshot button
s32 func_8083442C(Player* this, PlayState* play) {
s32 item;
Expand All @@ -2241,7 +2295,8 @@ s32 func_8083442C(Player* this, PlayState* play) {

if (this->unk_860 >= 0) {
if ((magicArrowType >= 0) && (magicArrowType <= 2) &&
!Magic_RequestChange(play, sMagicArrowCosts[magicArrowType], MAGIC_CONSUME_NOW)) {
!Magic_RequestChange(play, sMagicArrowCosts[magicArrowType],
CVarGetInteger("gArrowSwitching", 0) ? MAGIC_CONSUME_WAIT_PREVIEW : MAGIC_CONSUME_NOW)) {
arrowType = ARROW_NORMAL;
}

Expand Down Expand Up @@ -2308,7 +2363,7 @@ s32 func_80834758(PlayState* play, Player* this) {
if (!(this->stateFlags1 & (PLAYER_STATE1_SHIELDING | PLAYER_STATE1_ON_HORSE | PLAYER_STATE1_IN_CUTSCENE)) &&
(play->shootingGalleryStatus == 0) && (this->heldItemAction == this->itemAction) &&
(this->currentShield != PLAYER_SHIELD_NONE) && !Player_IsChildWithHylianShield(this) && func_80833BCC(this) &&
CHECK_BTN_ALL(sControlInput->cur.button, BTN_R)) {
!ArrowSwitching_CanSwitch(this) && CHECK_BTN_ALL(sControlInput->cur.button, BTN_R)) {

anim = func_808346C4(play, this);
frame = Animation_GetLastFrame(anim);
Expand Down Expand Up @@ -2551,6 +2606,12 @@ s32 func_808350A4(PlayState* play, Player* this) {
}
} else {
Inventory_ChangeAmmo(item, -1);
if (CVarGetInteger("gArrowSwitching", 0) &&
arrowType >= ARROW_FIRE && arrowType <= ARROW_LIGHT
&& this->heldActor->child != NULL) {
gSaveContext.magicState = MAGIC_STATE_IDLE;
Magic_RequestChange(play, sMagicArrowCosts[arrowType - ARROW_FIRE], MAGIC_CONSUME_NOW);
}
}

if (play->shootingGalleryStatus == 1) {
Expand Down Expand Up @@ -2604,6 +2665,11 @@ s32 func_808351D4(Player* this, PlayState* play) {

func_80834EB8(this, play);

if (gSwitchingArrow) {
Player_ReplaceSwitchedArrow(play, this);
gSwitchingArrow = false;
}

if ((this->unk_836 > 0) && ((this->unk_860 < 0) || (!D_80853618 && !func_80834E7C(play)))) {
func_80833638(this, func_808353D8);
if (this->unk_860 >= 0) {
Expand All @@ -2621,6 +2687,14 @@ s32 func_808351D4(Player* this, PlayState* play) {
this->stateFlags1 |= PLAYER_STATE1_READY_TO_FIRE;
}

gSwitchingArrow = Player_SwitchArrowsIfEnabled(this);
if (gSwitchingArrow && this->heldActor != NULL) {
if (this->heldActor->child != NULL) {
Actor_Kill(this->heldActor->child);
}
gSwitchingArrow = true;
}

return 1;
}

Expand All @@ -2631,6 +2705,8 @@ s32 func_808353D8(Player* this, PlayState* play) {
return 1;
}

Player_SwitchArrowsIfEnabled(this);

if (!func_80834758(play, this) &&
(D_80853614 || ((this->unk_860 < 0) && D_80853618) || func_80834E44(play))) {
this->unk_860 = ABS(this->unk_860);
Expand Down Expand Up @@ -5213,7 +5289,11 @@ s32 func_8083B644(Player* this, PlayState* play) {
this->stateFlags2 |= PLAYER_STATE2_NAVI_ALERT;
}

if (!CHECK_BTN_ALL(sControlInput->press.button, CVarGetInteger("gNaviOnL", 0) ? BTN_L : BTN_CUP) &&
u16 naviButton = CVarGetInteger("gNaviOnL", 0) ? BTN_L : BTN_CUP;
if (ArrowSwitching_CanSwitch(this)) {
naviButton &= ~CVarGetInteger("gArrowSwitchBtnMap", BTN_R);
}
if (!(naviButton && CHECK_BTN_ALL(sControlInput->press.button, naviButton)) &&
!sp28) {
return 0;
}
Expand Down Expand Up @@ -11593,15 +11673,20 @@ void func_8084B1D8(Player* this, PlayState* play) {
func_80836670(this, play);
}

u16 buttonsToCheck = BTN_A | BTN_B | BTN_R | BTN_CUP | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN;
u16 itemButtons = BTN_A | BTN_B | BTN_R | BTN_CUP | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN;
if (CVarGetInteger("gDpadEquips", 0) != 0) {
buttonsToCheck |= BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT;
itemButtons |= BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT;
}
u16 returnButtons = BTN_A | BTN_B | BTN_R;
if (ArrowSwitching_CanSwitch(this)) {
itemButtons &= ~CVarGetInteger("gArrowSwitchBtnMap", BTN_R);
returnButtons &= ~CVarGetInteger("gArrowSwitchBtnMap", BTN_R);
}
if ((this->csMode != 0) || (this->unk_6AD == 0) || (this->unk_6AD >= 4) || func_80833B54(this) ||
(this->unk_664 != NULL) || !func_8083AD4C(play, this) ||
(((this->unk_6AD == 2) && (CHECK_BTN_ANY(sControlInput->press.button, BTN_A | BTN_B | BTN_R) ||
(((this->unk_6AD == 2) && (CHECK_BTN_ANY(sControlInput->press.button, returnButtons) ||
func_80833B2C(this) || (!func_8002DD78(this) && !func_808334B4(this)))) ||
((this->unk_6AD == 1) && CHECK_BTN_ANY(sControlInput->press.button, buttonsToCheck)))) {
((this->unk_6AD == 1) && CHECK_BTN_ANY(sControlInput->press.button, itemButtons)))) {
func_8083C148(this, play);
func_80078884(NA_SE_SY_CAMERA_ZOOM_UP);
} else if ((DECR(this->unk_850) == 0) || (this->unk_6AD != 2)) {
Expand Down
Loading