Skip to content

Commit

Permalink
Display random melodies
Browse files Browse the repository at this point in the history
  • Loading branch information
GSKirox committed Jan 24, 2024
1 parent 1827fbb commit fb19e39
Show file tree
Hide file tree
Showing 14 changed files with 34,181 additions and 33,323 deletions.
2,590 changes: 1,304 additions & 1,286 deletions ASM/build/asm_symbols.txt

Large diffs are not rendered by default.

Binary file modified ASM/build/bundle.o
Binary file not shown.
729 changes: 372 additions & 357 deletions ASM/build/c_symbols.txt

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions ASM/c/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "scene.h"
#include "music.h"
#include "uninvertYaxis.h"
#include "ocarina_buttons.h"
#include "debug.h"

void Gameplay_InitSkybox(z64_game_t* globalCtx, int16_t skyboxId);
Expand Down Expand Up @@ -54,6 +55,7 @@ void after_game_state_update() {
draw_silver_rupee_count(&z64_game, &(z64_ctxt.gfx->overlay));
draw_illegal_model_text(&(z64_ctxt.gfx->overlay));
debug_utilities(&(z64_ctxt.gfx->overlay));
draw_ocarina_melodies(&(z64_ctxt.gfx->overlay));
}
give_ganon_boss_key();
}
Expand Down
201 changes: 196 additions & 5 deletions ASM/c/ocarina_buttons.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
#include "ocarina_buttons.h"
#include "gfx.h"

uint8_t has_a_button() {
return !SHUFFLE_OCARINA_BUTTONS || z64_file.scene_flags[0x50].unk_00_ & 1 << 0;
}

uint8_t has_cup_button() {
return !SHUFFLE_OCARINA_BUTTONS || z64_file.scene_flags[0x50].unk_00_ & 1 << 1;
}

uint8_t has_cdown_button() {
return !SHUFFLE_OCARINA_BUTTONS || z64_file.scene_flags[0x50].unk_00_ & 1 << 2;
}

uint8_t has_cleft_button() {
return !SHUFFLE_OCARINA_BUTTONS || z64_file.scene_flags[0x50].unk_00_ & 1 << 3;
}

uint8_t has_cright_button() {
return !SHUFFLE_OCARINA_BUTTONS || z64_file.scene_flags[0x50].unk_00_ & 1 << 4;
}

uint8_t c_block_ocarina() {
uint8_t res = 0;
if (!(z64_file.scene_flags[0x50].unk_00_ & 1 << 0)) { // A
if (!(has_a_button())) { // A
res |= 1 << 0;
}
if (!(z64_file.scene_flags[0x50].unk_00_ & 1 << 1)) { // C up
if (!(has_cup_button())) { // C up
res |= 1 << 1;
}
if (!(z64_file.scene_flags[0x50].unk_00_ & 1 << 2)) { // C down
if (!(has_cdown_button())) { // C down
res |= 1 << 2;
}
if (!(z64_file.scene_flags[0x50].unk_00_ & 1 << 3)) { // C left
if (!(has_cleft_button())) { // C left
res |= 1 << 3;
}
if (!(z64_file.scene_flags[0x50].unk_00_ & 1 << 4)) { // C right
if (!(has_cright_button())) { // C right
res |= 1 << 4;
}
return res;
Expand All @@ -26,3 +47,173 @@ int8_t can_spawn_epona() {
}
return (c_block_ocarina() & EPONAS_SONG_NOTES) ? 0 : 1;
}

extern uint8_t SHOW_OCARINA_MELODIES;
void print_ocarina_a(z64_disp_buf_t* db, uint8_t left, uint8_t top) {
int icon_width = 8;
int icon_height = 8;
gDPSetPrimColor(db->p++, 0, 0, 0x5A, 0x5A, 0xFF, 0xFF);
if (!(has_a_button())) {
gDPSetPrimColor(db->p++, 0, 0, 0x40, 0x40, 0x40, 0x90);
}
sprite_draw(db, &ocarina_button_sprite, 0, left, top, icon_width, icon_height);
}

void print_ocarina_cup(z64_disp_buf_t* db, uint8_t left, uint8_t top) {
int icon_width = 8;
int icon_height = 8;
gDPSetPrimColor(db->p++, 0, 0, 0xFA, 0xA0, 0x00, 0xFF);
if (!(has_cup_button())) {
gDPSetPrimColor(db->p++, 0, 0, 0x40, 0x40, 0x40, 0x90);
}
sprite_draw(db, &ocarina_button_sprite, 4, left, top, icon_width, icon_height);
}

void print_ocarina_cdown(z64_disp_buf_t* db, uint8_t left, uint8_t top) {
int icon_width = 8;
int icon_height = 8;
gDPSetPrimColor(db->p++, 0, 0, 0xFA, 0xA0, 0x00, 0xFF);
if (!(has_cdown_button())) {
gDPSetPrimColor(db->p++, 0, 0, 0x40, 0x40, 0x40, 0x90);
}
sprite_draw(db, &ocarina_button_sprite, 1, left, top, icon_width, icon_height);
}

void print_ocarina_cleft(z64_disp_buf_t* db, uint8_t left, uint8_t top) {
int icon_width = 8;
int icon_height = 8;
gDPSetPrimColor(db->p++, 0, 0, 0xFA, 0xA0, 0x00, 0xFF);
if (!(has_cleft_button())) {
gDPSetPrimColor(db->p++, 0, 0, 0x40, 0x40, 0x40, 0x90);
}
sprite_draw(db, &ocarina_button_sprite, 3, left, top, icon_width, icon_height);
}

void print_ocarina_cright(z64_disp_buf_t* db, uint8_t left, uint8_t top) {
int icon_width = 8;
int icon_height = 8;
gDPSetPrimColor(db->p++, 0, 0, 0xFA, 0xA0, 0x00, 0xFF);
if (!(has_cright_button())) {
gDPSetPrimColor(db->p++, 0, 0, 0x40, 0x40, 0x40, 0x90);
}
sprite_draw(db, &ocarina_button_sprite, 2, left, top, icon_width, icon_height);
}

extern int8_t SONG_MELODIES[0x60];
typedef struct {
colorRGBA8_t color;
uint8_t pos;
} song_color_and_position;
const song_color_and_position songs_colors_and_positions[12] = {
{{0x97, 0xFF, 0x63, 0xFF}, 6}, // Minuet
{{0xFF, 0x50, 0x28, 0xFF}, 7}, // Bolero
{{0x63, 0x97, 0xFF, 0xFF}, 8}, // Serenade
{{0xFF, 0x63, 0xFF, 0xFF}, 9}, // Nocturne
{{0xFF, 0x9F, 0x00, 0xFF}, 10},// Requiem
{{0xFF, 0xF0, 0x63, 0xFF}, 11},// Prelude
{{0xFF, 0x50, 0x28, 0xFF}, 0}, // ZL
{{0xFF, 0x9F, 0x00, 0xFF}, 1}, // Epona
{{0x97, 0xFF, 0x63, 0xFF}, 2}, // Saria
{{0xFF, 0xF0, 0x63, 0xFF}, 3}, // Suns
{{0x63, 0x97, 0xFF, 0xFF}, 4}, // SoT
{{0xFF, 0x63, 0xFF, 0xFF}, 5}, // SoS
};

uint8_t ocarina_action_ok(uint16_t ocarinaAction) {

if (ocarinaAction == 1) { // OCARINA_ACTION_FREE_PLAY
return 1;
}
if (ocarinaAction >= 0x1C && // OCARINA_ACTION_CHECK_MINUET
ocarinaAction <= 0x27) { // OCARINA_ACTION_CHECK_STORMS
return 1;
}
if (ocarinaAction == 0x30) { // OCARINA_ACTION_CHECK_NOWARP
return 1;
}
return 0;
}

void draw_ocarina_melodies(z64_disp_buf_t* db) {

if (!SHOW_OCARINA_MELODIES) {
return;
}

MessageContext *msgCtx = &(z64_game.msgContext);
if (!(z64_link.state_flags_2 & 0x8000000) ||
!ocarina_action_ok(msgCtx->ocarinaAction)) {
return;
}

gSPDisplayList(db->p++, &setup_db);

// Set up dimensions
int icon_size = 8;
int font_width = 6;
int font_height = 11;
int rows = 12;
int bg_width = 9 * icon_size;
int bg_height = rows * icon_size;
int bg_left = 30;
int bg_top = 30;

gDPSetCombineMode(db->p++, G_CC_PRIMITIVE, G_CC_PRIMITIVE);
gDPSetPrimColor(db->p++, 0, 0, 0x00, 0x00, 0x00, 0xD0);
gSPTextureRectangle(db->p++,
bg_left<<2, bg_top<<2,
(bg_left + bg_width)<<2, (bg_top + bg_height)<<2,
0,
0, 0,
1<<10, 1<<10);

gDPPipeSync(db->p++);
gDPSetCombineMode(db->p++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
gDPSetPrimColor(db->p++, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF);

gDPPipeSync(db->p++);
gDPSetCombineMode(db->p++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
uint16_t songsBits = (uint16_t)((z64_file.quest_items >> 6) & 0x0FFF);

for (int i = 0; i < 12; i++) {
// Check if we have the song
if ((songsBits & 0x1) == 0) {
songsBits >>= 1;
continue;
}
songsBits >>= 1;

// Draw a colored note sprite.
colorRGBA8_t color = songs_colors_and_positions[i].color;
uint8_t position = songs_colors_and_positions[i].pos;
gDPSetPrimColor(db->p++, 0, 0, color.r, color.g, color.b, color.a);
sprite_load(db, &song_note_sprite, 0, 1);
sprite_draw(db, &song_note_sprite, 0, bg_left, bg_top + position * icon_size, icon_size, icon_size);
uint8_t song_sprite_length = icon_size;

sprite_load(db, &ocarina_button_sprite, 0, 5);
for (int j = 0; j < 8; j++) {
switch (SONG_MELODIES[position * 8 + j])
{
case 0:
print_ocarina_a(db, bg_left + song_sprite_length + j * icon_size, bg_top + position * icon_size);
break;
case 1:
print_ocarina_cup(db, bg_left + song_sprite_length + j * icon_size, bg_top + position * icon_size);
break;
case 2:
print_ocarina_cdown(db, bg_left + song_sprite_length + j * icon_size, bg_top + position * icon_size);
break;
case 3:
print_ocarina_cleft(db, bg_left + song_sprite_length + j * icon_size, bg_top + position * icon_size);
break;
case 4:
print_ocarina_cright(db, bg_left + song_sprite_length + j * icon_size, bg_top + position * icon_size);
break;
default:
break;
}
}
}
gDPPipeSync(db->p++);
}
7 changes: 7 additions & 0 deletions ASM/c/ocarina_buttons.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#ifndef OCARINA_BUTTONS_H
#define OCARINA_BUTTONS_H
#include "z64.h"
#include "text.h"
#include "color.h"

uint8_t c_block_ocarina();
int8_t can_spawn_epona();

extern uint8_t SHUFFLE_OCARINA_BUTTONS;

void draw_ocarina_melodies(z64_disp_buf_t* db);
#endif
5 changes: 5 additions & 0 deletions ASM/src/config.asm
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ SHUFFLE_OCARINA_BUTTONS:
.byte 0x00
EPONAS_SONG_NOTES:
.byte 0x00
.area 0x60, 0
SONG_MELODIES:
.endarea
SHOW_OCARINA_MELODIES:
.byte 0x00
.align 4

; These configuration values are given fixed addresses to aid auto-trackers.
Expand Down
25 changes: 23 additions & 2 deletions OcarinaSongs.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@
'Nocturne of Shadow',
]

USUAL_SORT_ORDER: list[str] = [
'Zeldas Lullaby',
'Eponas Song',
'Sarias Song',
'Suns Song',
'Song of Time',
'Song of Storms',
'Minuet of Forest',
'Bolero of Fire',
'Serenade of Water',
'Nocturne of Shadow',
'Requiem of Spirit',
'Prelude of Light',
]

# Song name: (rom index, warp, vanilla activation),
SONG_TABLE: dict[str, tuple[int, bool, str]] = {
'Zeldas Lullaby': ( 8, False, '<^><^>'),
Expand Down Expand Up @@ -424,11 +439,17 @@ def generate_song_list(world: World, frog: bool, warp: bool) -> dict[str, Song]:
raise ShuffleError('Could not generate random songs')

# sort the songs by length
random_songs.sort(key=lambda s: s.difficulty)

for name in DIFFICULTY_ORDER:
if name not in fixed_songs:
fixed_songs[name] = random_songs.pop(0)
return fixed_songs

# sort the songs back in their usual order
fixed_songs_ordered = {}
for name in USUAL_SORT_ORDER:
fixed_songs_ordered[name] = fixed_songs[name]

return fixed_songs_ordered


# replace the playback and activation requirements for the ocarina songs
Expand Down
23 changes: 23 additions & 0 deletions Patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -2522,6 +2522,29 @@ def update_scrub_text(message: bytearray, text_replacement: list[str], default_p

patch_songs(world, rom)

if world.settings.show_ocarina_melodies:
symbol = rom.sym('SHOW_OCARINA_MELODIES')
bytes_to_write = []
for name, song in world.song_notes.items():
notes = str(world.song_notes[name])
for note in notes:
if note == 'A':
bytes_to_write.append(0)
if note == '^':
bytes_to_write.append(1)
if note == 'v':
bytes_to_write.append(2)
if note == '<':
bytes_to_write.append(3)
if note == '>':
bytes_to_write.append(4)
if world.song_notes[name].length < 8:
for i in range(8 - world.song_notes[name].length):
bytes_to_write.append(5)

rom.write_bytes(rom.sym('SONG_MELODIES'), bytes_to_write)
rom.write_byte(symbol, 0x01)

if world.settings.shuffle_individual_ocarina_notes:
rom.write_byte(rom.sym('SHUFFLE_OCARINA_BUTTONS'), 1)
epona_notes = str(world.song_notes['Eponas Song'])
Expand Down
16 changes: 16 additions & 0 deletions SettingsList.py
Original file line number Diff line number Diff line change
Expand Up @@ -3224,6 +3224,22 @@ class SettingInfos:
typically more difficult than frog songs.
''',
shared = True,
disable = {
'off': {'settings': ['show_ocarina_melodies']}
},
)

show_ocarina_melodies = Checkbutton(
gui_text = 'Display Ocarina Melodies',
gui_tooltip = '''\
Display a window that shows the random melodies inputs
when you pull out the Ocarina.
''',
default = False,
shared = True,
gui_params = {
"hide_when_disabled": True,
},
)

correct_chest_appearances = Combobox(
Expand Down
Loading

0 comments on commit fb19e39

Please sign in to comment.