Skip to content

Conversation

@Caladius
Copy link
Contributor

@Caladius Caladius commented Oct 23, 2025

Needed a break from Enemy Souls/Drops...

This shuffles the Ocarina Notes into the pool requiring you to collect all of the buttons for a given song before you can play it.

  • Models and concept taken from SoH

Build Artifacts

Copy link
Contributor

@Eblo Eblo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweeping question: why the approach of evaluating at song's end whether the song is valid, along with ad hoc VBs to prevent the player from engaging with a song check if they don't have the needed notes? I think a cleaner and saner approach would be to prevent an individual note from being registered if the button has not been obtained. As-is, I think it's somewhat confusing that Link can play the notes, only for it to fail when playing the complete song or playing for a key NPC if the relevant buttons are not acquired.

For example, the player can still play any notes for Pierre or the Termina wall regardless of buttons obtained. I realize the Termina wall drops aren't shuffled now, but they might be. You could add even more VBs for those, but if you nipped this at the per-button input level, you wouldn't need to worry about every single case.

There needs to be more logic accounting for things besides core songs. While you did define whether a song is playable, not use of the Ocarina in logic was tied to a specific song. For instance, there's no logic preventing Evan from holding one of the buttons used for the jam song. Ditto Ballad of the Wind Fish.

Scarecrow's Song is a unique case where, iirc, the player needs to have at least two different notes to make a song. We have an enhancement to skip that, but it's opt-in. This shuffle now makes that distinction significant.

@Caladius
Copy link
Contributor Author

Caladius commented Nov 8, 2025

Sweeping question: why the approach of evaluating at song's end whether the song is valid, along with ad hoc VBs to prevent the player from engaging with a song check if they don't have the needed notes? I think a cleaner and saner approach would be to prevent an individual note from being registered if the button has not been obtained. As-is, I think it's somewhat confusing that Link can play the notes, only for it to fail when playing the complete song or playing for a key NPC if the relevant buttons are not acquired.

For example, the player can still play any notes for Pierre or the Termina wall regardless of buttons obtained. I realize the Termina wall drops aren't shuffled now, but they might be. You could add even more VBs for those, but if you nipped this at the per-button input level, you wouldn't need to worry about every single case.

There needs to be more logic accounting for things besides core songs. While you did define whether a song is playable, not use of the Ocarina in logic was tied to a specific song. For instance, there's no logic preventing Evan from holding one of the buttons used for the jam song. Ditto Ballad of the Wind Fish.

Scarecrow's Song is a unique case where, iirc, the player needs to have at least two different notes to make a song. We have an enhancement to skip that, but it's opt-in. This shuffle now makes that distinction significant.

Have a place in mind for this? The approach, whether sane or not haha, was just where it worked out.

@Eblo Eblo mentioned this pull request Nov 11, 2025
31 tasks
@Eblo
Copy link
Contributor

Eblo commented Nov 14, 2025

The Ocarina button index corresponds to this enum regardless of the input used:

typedef enum OcarinaButtonIndex {
/* 0 */ OCARINA_BTN_A,
/* 1 */ OCARINA_BTN_C_DOWN,
/* 2 */ OCARINA_BTN_C_RIGHT,
/* 3 */ OCARINA_BTN_C_LEFT,
/* 4 */ OCARINA_BTN_C_UP,
/* 5 */ OCARINA_BTN_C_RIGHT_OR_C_LEFT,
/* -1 */ OCARINA_BTN_INVALID = 0xFF
} OcarinaButtonIndex;

This point in code_8019AF00.c seems convenient to add a new VB should:

if (sOcarinaInputButtonCur) {}

It comes after the base ocarina button determination has been made across input types, but before anything is done with it. I'm thinking we could wrap it like so:

 if (GameInteractor_Should(VB_PLAY_OCARINA_NOTE, sOcarinaInputButtonCur, &sCurOcarinaButtonIndex, &sCurOcarinaPitch)) {} 

Then in the enhancement code, do something like:

COND_VB_SHOULD(VB_PLAY_OCARINA_NOTE, IS_RANDO, {
    u8* sCurOcarinaButtonIndex = va_arg(args, u8*);
    u8* sCurOcarinaPitch = va_arg(args, u8*);
    bool canPlayNote = true;

    if(*sCurOcarinaButtonIndex == OCARINA_BTN_A && !Flags_GetRandoInf(RANDO_INF_OBTAINED_OCARINA_BUTTON_A)) {
        canPlayNote = false;
    }

    // Repeat the above for all other buttons. Might be cleaner to use a map or macro for brevity

    // Player pressed a button they do not have, so clear the ocarina button state
    if(!canPlayNote) {
        *sCurOcarinaButtonIndex = OCARINA_BTN_INVALID;
        *sCurOcarinaPitch = OCARINA_PITCH_NONE;
    }
});

That should catch things at the per-note level and prevent Link from even playing a single note if he doesn't have it.

@Caladius
Copy link
Contributor Author

Adding a note here to add this to the Item Tracker once merged in. #1333

Copy link
Contributor

@Eblo Eblo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing we have to come up with an answer for is the Scarecrow's Song. There is an enhancement to automatically summon the Scarecrow if the player brings out the Ocarina in range, bypassing the need to play songs. The song requires at least two different notes. I think what we can do is go into RegisterSkipScarecrowSong and tack on canPlaySong(OCARINA_SONG_SCARECROW_SPAWN) for rando, thus only allowing this enhancement to work if the player could play the Scarecrow's Song.

@Malkierian
Copy link
Contributor

I think SoH handles scarecrow very well, where anytime it checks for the ability to play, it includes a counter for adding each of the available buttons based on set flags, and if available >= 2, then scarecrow can be played. This can be handled in a helper function.

Caladius and others added 3 commits December 3, 2025 13:52
Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com>
Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com>
Co-authored-by: Eblo <7004497+Eblo@users.noreply.github.com>
@Caladius Caladius requested a review from Eblo December 7, 2025 23:36
Copy link
Contributor

@Eblo Eblo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the reorganization of VBs, this will need rebased to define and document the new VBs in GameInteractor_VanillaBehavior.h.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants