Play non-warp ocarina songs from the pause menu - #6951
Conversation
I can't look at the decomp source code or this PR right now, but I think actors like Mido (
Do songs with effects liks Sun's or Song of Time (if time travelling) work? Edit: Had some minutes to look at it, I think it should be possible to incorporate |
|
Thanks for the review!
Good catch, I confirm it does not work, but it does work when playing the ocarina the vanilla way.
I'll try to fix it, good idea. |
|
@djevangelia, I tried your idea and it does not work sadly... I can't get Mido accept the saria song to let me pass. We enter a bogus state where Mido is still listening to us waiting for the Saria Song but it was already played, so we are both playing ocarina and not all at once. IMO, these are the ways we can solve this:
I'd definitly go with option 3 myself, there are only 4 of these actor-cases. Worse case if we've missed some things, the vanilla behavior will still work and we can improve this later. This is the result of Claude's analysis, but I am quite sure it's accurate:
Details of why the generic implementation is hard (AI generated):1. player->ocarinaTalkActor (unk_6A8) can't identify the target from the menu -- it's empty in our contextIt's only populated while the player is actively in the ocarina "play-for-actor" flow, which the pause menu never enters. Even standing next to an actor:
So ocarinaTalkActor is NULL when we run — it doesn't spare the actor lookup. 2. The ocarina states (23-25) give only a boolean "near an ocarina actor," not which one.NEAR_OCARINA_ACTOR is fine for a generic-vs-specific split, but it can't identify the actor, and ATTEMPT/PLAY_FOR_ACTOR are never set from the menu -- so you still must enumerate to find the target. 3. The only way to deliver via those states (set ATTEMPT_PLAY_FOR_ACTOR, let the actor react) breaks the gameplayWe'd have to make sure we don't break the gameplay behavior |
|
Aha, it's only during the play. Makes it difficult. My suggestion regarding Ocarina states was not for specific actors, but for quickly determining that the song is to be played generic and not for actor. Some players might want to be able to summon Epona from the pause menu etc. |
The epona from pause works as is, of course. Just to be clear, in the latest commit of this pr, everything I've tested works as you expected it, is it that you play the song from the pause menu or play it manually. Most gameplay events work too, except 4 gameplay events won't trigger with the pause menu, but I did not break the vanilla behaviour, you still can use the Ocarina to pass mido. Everything else I've tested works as expected. The last message is about finding a way to fix this 4 corner cases and let us pass mido with the pause menu's Saria's song |
|
Feel like a gameplay macro system would address this pretty well, maybe some trickery to force using ocarina out of pause |
|
Cows also respond to Ocarina but seem to have a different system for activation relying on |
Right, they answer to epona's song if I remember well, I'll test this case too |
|
The big-fairy spawning on some songs (usually sun's or storms) won't work either with this last commit, the fairy won't spawn when the right song is played from the menu, they do when playing the ocarina. Still looking for a cow, but my guess is that they won't work either. |
Confirmed this one somewhat works with my local changes... But the cow won't give me what I want for some reason even if I play the vanilla song... I'm re-testing without these changes to compare... I also confirmed playing the song of storm in the Kokiri forest near the Lost Wood entrance works to open the secret whole... so really the only remaining corner cases to test are
|
They are from z_shot_sun.c which seem to work similarly to z_obj_timeblock.c (most SoT blocks) and z_obj_warp2block.c (some SoT blocks such as SoT block room in MQ Spirit) which also should be tested. Sorry for coming with new actors all the time, I don't barely remember any of them as I don't play. Do you have an empty bottle for cows? |
Not a problem! Thanks for helping! So I actually forgot to test the SoT blocks, I'll have to do it. I'll edit the PR description with a list of things I've tested vs. know aren't working. For the cow, I suspect I either have a corrupted data, or one of my mod broke it. I'm currently testing in develop to see if I have a repro. High chances I found an unrelated bug, and the feature works as expected. Yes, I have an empty bottle, and the sound I hear is the 5-rupy sound, not the usual Fanfare, very weird. |
|
Not sure why, but develop (8481060) is crashing a lot for me... I turned off my HD texture mods but it still crashes. 29285ed does it too. Anyway, I tried to go to the exact same cow, with exact same behavior on the develop branch: the cow speaks to me about its childhood, then 5 rupee song, then no milk in my empty glass. Unrelated issue. I'll investigate it separately. I think my savegame is corrupted so the cow thinks I don't have an empty bottle. This is the callstack I get:
|
|
OK tested with a fresh savegame, and it works with both the ocarina or using the menu to play the song. I used the cow in the house as a kid (forcing the save to have the bare minimum) and it works. So I'll mark this one as working. I also fixed my savegame with #6958 and after the cows were giving me some milk as expected. |
|
uh for some reason when learning songs it displays 2 notes then doesn't let me repeat the song to learn it, don't know if you are just starting with the songs but for zelda's lullaby i had to debug warp out to fix, it doesn't seem to work with epona's song though so i have to edit my save to add it |
90c4782 to
81457a5
Compare
|
Latest version added a check so we can't play a song where the ocarina cannot be used (horseback riding, in the air, underwater, etc.) to avoid gameplay bugs; I reused the in-game logic. I tested Mido, Darunia and the SoT blocks, they now work. Big Fairies are using similar mechanism than the SoT block but I did not have time to re-test, same for Skull Kid and Malon, they should work but I did not test.
I did consider this btw @serprex, I just felt I was almost there with the current approach of sending gameplay events directly, but using a macro system to send actual song notes would work to, pretty sure there are corner cases also (detect if the actor is listening before sending the notes, etc.) |
I could not repro this on my end... maybe you had a corrupted savegame like I did at some point? I did test learning the saria's song near the forest temple entrance as a kid. |
55e13dd to
8ff3135
Compare
Pure rename so history/blame follow into the expanded feature. Content changes come in the next commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extends the pause-menu song shortcut to the six non-warp songs. Selecting a song closes the menu and triggers its effect in gameplay: warp songs warp as before, and songs with a zone effect (Zelda's Lullaby, Song of Storms, Song of Time) hand the matching in-range staff spot straight to its own listening handler, so vanilla keeps owning every effect, cutscene and precondition. Splits the old "Pause Warp" toggle into "Play Songs from Pause Menu" and "Skip Warp Cutscenes", with a config migration carrying the old flag onto both. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RespawnData.data is s8; lastPlayedSong is u16. gcc is quiet but Windows CI treats the narrowing (C4244) as an error. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses review feedback: keep the play-songs-from-pause-menu feature self-contained in its hook instead of exporting a helper from the En_Okarina_Tag overlay. The staff-spot listening handlers are declared extern in PauseMenuSongs.cpp (same idiom as RebottleBlueFire), so z_en_okarina_tag.c returns to vanilla. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Call the matching spot's listening handler directly and reset the ocarina/message state inline, instead of flipping actionFunc and deferring cleanup to the next frame. Drops the needsOcarinaCleanup state and keeps the whole trigger in one frame. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Song of Time blocks (Obj_Timeblock) and Great Fairy spawners (Shot_Sun) react to OCARINA_MODE_04 + lastPlayedSong, not the MODE_03 "correct song" event the staff spots and NPCs use, so the menu path never reached them. Drive the in-range actors into their song-complete state and hold MODE_04 for a couple frames so they consume it on their next update. Also fix the type 1/2/4/6 okarina-tag spots (Door of Time, drain well, Zora waterfall, Royal Family Tomb): they deferred their listening handler by a frame while the same-frame reset cleared MODE_03 before it ran. Run it synchronously, like the type-7 spots right above. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the NPC hand-off (previously Mido-only) to two more song-reactive NPCs: Darunia in Goron City (Saria's Song -> Darunia's Joy) and adult Malon at Lon Lon Ranch (Epona's Song). Like Mido, we set the actor's actionFunc straight to its listening handler and leave OCARINA_MODE_03 for it to consume next frame. Unify the hand-off state: only Mido offers a dialogue the player would normally start (gated by npcHandoffStartTalk); Darunia drives its own cutscene and Malon force-talks itself, so those just need the ocarina state restored afterward. Skull Kid (En_Skj) is intentionally left out: it is ACTORCAT_ENEMY with a static-struct minigame that repositions the player and is entangled with item/randomizer logic; it needs separate handling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only inject a played song when Link is in a normal, controllable field state -- grounded and not in a cutscene, message, mid-song, in water, on horseback, or dying. Mirrors the footing the vanilla ocarina action needs to start, and avoids leaving the ocarina/message system in a bad state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjDjMt2YeFw6wzqYL1zM6Y
The non-tag actors (Song of Time blocks, Great Fairy spawners, and the ocarina NPCs) all update before the enhancement hook and just poll ocarinaMode + lastPlayedSong, so they were each driven by a slightly different ad-hoc mechanism: a MODE_04 hold timer for the blocks/fairies and a separate MODE_03 hand-off timer for the NPCs. Collapse those two into a single hold-the-mode/restore path (PauseSong_HoldMode + PauseSong_AdvancePending): push the matching in-range actor into its song-finished state, leave the matching mode set for it to read next frame, then restore state (starting Mido's dialogue when it reacts). Staff spots still run inline -- driving them there, after every actor has updated, keeps the MODE_04 they set from leaking to the MODE_04 readers. Behavior is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjDjMt2YeFw6wzqYL1zM6Y
Drop the dependency on the ocarina symbol-rename PR (HarbourMasters#6967): the En_Du and En_Okarina_Tag listening handlers this feature calls are already non-static on develop, just not yet given descriptive names, so reference them by their raw func_ symbols (documented inline). Every other handler (En_Md, En_Ma2, Obj_Timeblock, Shot_Sun) is already named on develop. The feature now touches no decompiled files at all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjDjMt2YeFw6wzqYL1zM6Y
When a non-warp song is selected somewhere the ocarina can't be played, close the pause menu and show a short textbox -- the same hand-off a warp song uses, minus the song -- rather than swallowing the button press with no feedback. ActivateCannotPlay mirrors ActivateWarp; PauseCannotPlay_Execute restores control once the message closes. The message is a CustomMessage displayed through the OnOpenText hook (EN/DE/FR, phrased impersonally to avoid the tu/vous choice; JP falls back to English), keyed on a new TEXT_CANNOT_PLAY_OCARINA_MSG id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjDjMt2YeFw6wzqYL1zM6Y
The stump Skull Kid that rewards Saria's Song runs its listen/reward state machine on the visible actor, which init moves to ACTORCAT_NPC (the blue lock-on), not ACTORCAT_ENEMY. Fold it into the existing NPC hand-off loop: when it is idling in EnSkj_WaitInRange, push it to EnSkj_WaitForSong, hold MODE_03, and start the reward talk once it reacts -- same pattern as Mido. Being in WaitInRange already implies the player is in range (it leaves that state otherwise), so no extra distance check is needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjDjMt2YeFw6wzqYL1zM6Y
Extract the menu-close prologue duplicated across ActivateWarp/ActivateSong/ ActivateCannotPlay into PauseMenu_BeginClose, dedup the ocarinaMode reset in PauseSong_Execute, and trim two verbose comments. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjDjMt2YeFw6wzqYL1zM6Y
Warp songs are just songs played from the menu, so gate them on the same PauseSong_CanPlayOcarina() check as the rest instead of letting them fire from states the game would never allow the ocarina in (mid-air, underwater, on horseback, ...). Hoisting the guard ahead of the warp/non-warp branch also lets both share it and the "can't play here" message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjDjMt2YeFw6wzqYL1zM6Y
8ff3135 to
77b31da
Compare
A song selected from the pause menu could be blocked by a previous song's ocarina/message state still settling after unpause, wrongly reporting the spot as unplayable. Close the menu, then re-check for a few frames before concluding the ocarina truly can't be played here. When it genuinely can't, show the standard textbox and hand it the game's fading end type so it dismisses itself after a few seconds instead of waiting on A. Link keeps normal control and physics while it fades. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the file-local CVAR_PAUSE_WARP_* macros to CVAR_PAUSE_SONGS_*, since the toggle now covers every song rather than just warps. Trim comments that had accumulated duplicate rationale, keeping one home for each explanation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
I did another testing pass on this one and fixed the last remaining bugs I could spot. @serprex, @djevangelia, ready for another review pass. Tested all actors that interact with the ocarina: Darunia, Skull Kid, Mido, and Malon, plus Song of Time blocks, cows, etc. — everything is now in the PR description. One caveat with Malon: I can't remember what was the expected behavior, but I do see the exact same dialogue after playing the song manually and with the Pause menu. I also added a check that the ocarina can actually be played before playing the song: if it can't, a dialogue shows up saying you can't play the ocarina here. The message is translated in English (fluent), French (native), and German (AI-generated). Tested many cases to make sure it doesn't break gameplay. This check also applies to the old warp songs — it would be easy to gate behind an option to bring back the old behavior (no check was done before warping), but I didn't. |
Closes #6940.
What
Extends the pause-menu song shortcut (formerly "Pause Warp") to the six non-warp songs. On the Quest screen, pressing A on any song you know closes the menu and triggers that song's real gameplay effect: warp songs warp as before, and the zone songs (Zelda's Lullaby, Song of Storms, Song of Time) fire their staff-spot effect when you're standing in range of one.
The old "Pause Warp" toggle is split in two:
A config migration carries an existing "Pause Warp" enable onto both new flags.
Why / how
Rather than reimplement each song's effect and its preconditions, the shortcut delegates to vanilla. For a zone song it hands the matching in-range
En_Okarina_Tagstaff spot straight to its own listening handler and raisesOCARINA_MODE_03, so the game's own code owns every effect, cutscene and eligibility check (for example the Ocarina-of-Time gate on the Door of Time). The only decisions the shortcut makes are proximity and song-matches-spot; everything scenario-specific stays in the actor.Testing
Build Artifacts