Skip to content

Commit 4666a5c

Browse files
committed
Option to randomize the Frogs 2 melody
1 parent b1285ac commit 4666a5c

6 files changed

Lines changed: 79 additions & 45 deletions

File tree

OcarinaSongs.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,21 @@
6969
'Nocturne of Shadow',
7070
]
7171

72-
# Song name: (rom index, warp, vanilla activation),
73-
SONG_TABLE: dict[str, tuple[int, bool, str]] = {
74-
'Zeldas Lullaby': ( 8, False, '<^><^>'),
75-
'Eponas Song': ( 7, False, '^<>^<>'),
76-
'Sarias Song': ( 6, False, 'v><v><'),
77-
'Suns Song': ( 9, False, '>v^>v^'),
78-
'Song of Time': (10, False, '>Av>Av'),
79-
'Song of Storms': (11, False, 'Av^Av^'),
80-
'Minuet of Forest': ( 0, True, 'A^<><>'),
81-
'Bolero of Fire': ( 1, True, 'vAvA>v>v'),
82-
'Serenade of Water': ( 2, True, 'Av>><'),
83-
'Requiem of Spirit': ( 3, True, 'AvA>vA'),
84-
'Nocturne of Shadow': ( 4, True, '<>>A<>v'),
85-
'Prelude of Light': ( 5, True, '^>^><^'),
72+
# Song name: (rom index, kind, vanilla activation),
73+
SONG_TABLE: dict[str, tuple[Optional[int], str, str]] = {
74+
'Zeldas Lullaby': ( 8, 'frog', '<^><^>'),
75+
'Eponas Song': ( 7, 'frog', '^<>^<>'),
76+
'Sarias Song': ( 6, 'frog', 'v><v><'),
77+
'Suns Song': ( 9, 'frog', '>v^>v^'),
78+
'Song of Time': ( 10, 'frog', '>Av>Av'),
79+
'Song of Storms': ( 11, 'frog', 'Av^Av^'),
80+
'Minuet of Forest': ( 0, 'warp', 'A^<><>'),
81+
'Bolero of Fire': ( 1, 'warp', 'vAvA>v>v'),
82+
'Serenade of Water': ( 2, 'warp', 'Av>><'),
83+
'Requiem of Spirit': ( 3, 'warp', 'AvA>vA'),
84+
'Nocturne of Shadow': ( 4, 'warp', '<>>A<>v'),
85+
'Prelude of Light': ( 5, 'warp', '^>^><^'),
86+
'ZR Frogs Ocarina Game': (None, 'frogs2', 'A<>v<>vAvAv><A'),
8687
}
8788

8889

@@ -386,26 +387,32 @@ def get_random_song() -> Song:
386387

387388

388389
# create a list of 12 songs, none of which are sub-strings of any other song
389-
def generate_song_list(world: World, frog: bool, warp: bool) -> dict[str, Song]:
390+
def generate_song_list(world: World, frog: bool, warp: bool, frogs2: bool) -> dict[str, Song]:
390391
fixed_songs = {}
391392
if not frog:
392-
fixed_songs.update({name: Song.from_str(notes) for name, (_, is_warp, notes) in SONG_TABLE.items() if not is_warp})
393+
fixed_songs.update({name: Song.from_str(notes) for name, (_, kind, notes) in SONG_TABLE.items() if kind == 'frog'})
393394
if not warp:
394-
fixed_songs.update({name: Song.from_str(notes) for name, (_, is_warp, notes) in SONG_TABLE.items() if is_warp})
395+
fixed_songs.update({name: Song.from_str(notes) for name, (_, kind, notes) in SONG_TABLE.items() if kind == 'warp'})
396+
if not frogs2:
397+
fixed_songs.update({name: Song.from_str(notes) for name, (_, kind, notes) in SONG_TABLE.items() if kind == 'frogs2'})
395398
fixed_songs.update({name: Song.from_str(notes) for name, notes in world.distribution.configure_songs().items()})
396399
for name1, song1 in fixed_songs.items():
397400
if name1 not in SONG_TABLE:
398401
raise ValueError(f'Unknown song: {name1!r}. Please use one of these: {", ".join(SONG_TABLE)}')
399402
if not song1.activation:
400403
raise ValueError(f'{name1} is empty')
401-
if len(song1.activation) > 8:
402-
raise ValueError(f'{name1} is too long (maximum is 8 notes)')
404+
if name1 == 'ZR Frogs Ocarina Game':
405+
if len(song1.activation) != 14:
406+
raise ValueError(f'{name1} song must be exactly 14 notes')
407+
else:
408+
if len(song1.activation) > 8:
409+
raise ValueError(f'{name1} is too long (maximum is 8 notes)')
403410
for name2, song2 in fixed_songs.items():
404411
if name1 != name2 and subsong(song1, song2):
405412
raise ValueError(f'{name2} is unplayable because it contains {name1}')
406413
random_songs = []
407414

408-
for _ in range(12 - len(fixed_songs)):
415+
for _ in range(12 - sum(name != 'ZR Frogs Ocarina Game' for name in fixed_songs)):
409416
for _ in range(1000):
410417
# generate a completely random song
411418
song = get_random_song()
@@ -428,6 +435,10 @@ def generate_song_list(world: World, frog: bool, warp: bool) -> dict[str, Song]:
428435
for name in DIFFICULTY_ORDER:
429436
if name not in fixed_songs:
430437
fixed_songs[name] = random_songs.pop(0)
438+
439+
if 'ZR Frogs Ocarina Game' not in fixed_songs:
440+
fixed_songs['ZR Frogs Ocarina Game'] = Song(activation=[random.randint(0, 4) for _ in range(14)])
441+
431442
return fixed_songs
432443

433444

@@ -437,6 +448,10 @@ def patch_songs(world: World, rom: Rom) -> None:
437448
if str(song) == SONG_TABLE[name][2]:
438449
continue # song activation is vanilla (possibly because this row wasn't randomized), don't randomize playback
439450

451+
if name == 'ZR Frogs Ocarina Game':
452+
rom.write_bytes(0xB78AA0, [note['note'] for note in song.playback])
453+
continue
454+
440455
# fix the song of time
441456
if name == 'Song of Time':
442457
song.increase_duration_to(260)

SettingsList.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,20 +3171,32 @@ class SettingInfos:
31713171
shared = True,
31723172
)
31733173

3174-
ocarina_songs = Combobox(
3174+
ocarina_songs = MultipleSelect(
31753175
gui_text = 'Randomize Ocarina Melodies',
3176-
default = 'off',
3176+
default = [],
31773177
choices = {
3178-
'off': 'Off',
3179-
'frog': 'Frog Songs Only',
3180-
'warp': 'Warp Songs Only',
3181-
'all': 'All Songs',
3178+
'frog': 'Frog Songs',
3179+
'warp': 'Warp Songs',
3180+
'frogs2': 'Frogs Ocarina Game',
31823181
},
31833182
gui_tooltip = '''\
31843183
Will need to memorize a new set of songs.
31853184
Can be silly, but difficult. All songs are
31863185
generally sensible, but warp songs are
31873186
typically more difficult than frog songs.
3187+
3188+
"Frog Songs": Randomizes Zelda's Lullaby,
3189+
Epona's Song, Saria's Song, Sun's Song,
3190+
Song of Time, and Song of Storms.
3191+
3192+
"Warp Songs": Randomizes Minuet of Forest,
3193+
Bolero of Fire, Serenade of Water, Requiem
3194+
of Spirit, Nocturne of Shadow, and Prelude
3195+
of Light.
3196+
3197+
"Frogs Ocarina Game": Randomizes the 14
3198+
notes of the final song of the Fabulous
3199+
Five Froggish Tenors.
31883200
''',
31893201
shared = True,
31903202
)
@@ -3429,7 +3441,7 @@ class SettingInfos:
34293441
Placing yourself on the log at Zora River
34303442
where you play the songs for the frogs will
34313443
tell you what the reward is for playing all
3432-
six non warp songs.
3444+
six non-warp songs.
34333445
34343446
If shuffled, right side items in the mask
34353447
shop will be visible but not obtainable

World.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,9 @@ def __missing__(self, dungeon_name: str) -> EmptyDungeonInfo:
153153
self.resolve_random_settings()
154154

155155
self.song_notes: dict[str, Song] = generate_song_list(self,
156-
frog=settings.ocarina_songs in ('frog', 'all'),
157-
warp=settings.ocarina_songs in ('warp', 'all'),
156+
frog='frog' in settings.ocarina_songs,
157+
warp='warp' in settings.ocarina_songs,
158+
frogs2='frogs2' in settings.ocarina_songs,
158159
)
159160

160161
if len(settings.hint_dist_user) == 0:

data/Glitched World/Overworld.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,8 @@
10831083
"ZR Frogs Ocarina Game": "
10841084
is_child and can_play(Zeldas_Lullaby) and can_play(Sarias_Song) and
10851085
can_play(Suns_Song) and can_play(Eponas_Song) and
1086-
can_play(Song_of_Time) and can_play(Song_of_Storms)",
1086+
can_play(Song_of_Time) and can_play(Song_of_Storms) and
1087+
has_all_notes_for_song('ZR Frogs Ocarina Game')",
10871088
"ZR Frogs Zeldas Lullaby": "is_child and can_play(Zeldas_Lullaby)",
10881089
"ZR Frogs Eponas Song": "is_child and can_play(Eponas_Song)",
10891090
"ZR Frogs Sarias Song": "is_child and can_play(Sarias_Song)",
@@ -1097,7 +1098,7 @@
10971098
"ZR GS Above Bridge": "can_use(Hookshot) and at_night",
10981099
"ZR Near Grottos Gossip Stone": "True",
10991100
"ZR Near Domain Gossip Stone": "True",
1100-
"ZR Frogs Ocarina Minigame Hint": "True"
1101+
"ZR Frogs Ocarina Minigame Hint": "is_child"
11011102
},
11021103
"exits": {
11031104
"ZR Front": "True",

data/World/Overworld.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,9 +2321,10 @@
23212321
"locations": {
23222322
"ZR Magic Bean Salesman": "is_child",
23232323
"ZR Frogs Ocarina Game": "
2324-
is_child and can_play(Zeldas_Lullaby) and can_play(Eponas_Song) and
2325-
can_play(Sarias_Song) and can_play(Suns_Song) and can_play(Song_of_Time) and can_play(Song_of_Storms) and
2326-
Ocarina_A_Button and Ocarina_C_left_Button and Ocarina_C_right_Button and Ocarina_C_down_Button",
2324+
is_child and
2325+
can_play(Zeldas_Lullaby) and can_play(Eponas_Song) and can_play(Sarias_Song) and
2326+
can_play(Suns_Song) and can_play(Song_of_Time) and can_play(Song_of_Storms) and
2327+
has_all_notes_for_song('ZR Frogs Ocarina Game')",
23272328
"ZR Frogs Zeldas Lullaby": "is_child and can_play(Zeldas_Lullaby)",
23282329
"ZR Frogs Eponas Song": "is_child and can_play(Eponas_Song)",
23292330
"ZR Frogs Sarias Song": "is_child and can_play(Sarias_Song)",
@@ -2348,7 +2349,7 @@
23482349
"Bug Shrub": "
23492350
(is_child or here(can_plant_bean) or Hover_Boots or logic_zora_river_lower) and
23502351
can_cut_shrubs and has_bottle",
2351-
"ZR Frogs Ocarina Minigame Hint": "True"
2352+
"ZR Frogs Ocarina Minigame Hint": "is_child"
23522353
},
23532354
"exits": {
23542355
"ZR Front": "True",

data/presets_default.json

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
"easier_fire_arrow_entry": false,
122122
"fae_torch_count": 3,
123123
"ruto_already_f1_jabu": false,
124-
"ocarina_songs": "off",
124+
"ocarina_songs": [],
125125
"correct_chest_appearances": "off",
126126
"minor_items_as_major_chest": false,
127127
"invisible_chests": false,
@@ -302,7 +302,7 @@
302302
"easier_fire_arrow_entry": false,
303303
"fae_torch_count": 3,
304304
"ruto_already_f1_jabu": false,
305-
"ocarina_songs": "off",
305+
"ocarina_songs": [],
306306
"correct_chest_appearances": "both",
307307
"minor_items_as_major_chest": false,
308308
"invisible_chests": false,
@@ -480,7 +480,7 @@
480480
"easier_fire_arrow_entry": false,
481481
"fae_torch_count": 3,
482482
"ruto_already_f1_jabu": false,
483-
"ocarina_songs": "off",
483+
"ocarina_songs": [],
484484
"correct_chest_appearances": "both",
485485
"minor_items_as_major_chest": false,
486486
"invisible_chests": false,
@@ -659,7 +659,7 @@
659659
"easier_fire_arrow_entry": false,
660660
"fae_torch_count": 3,
661661
"ruto_already_f1_jabu": false,
662-
"ocarina_songs": "off",
662+
"ocarina_songs": [],
663663
"correct_chest_appearances": "off",
664664
"minor_items_as_major_chest": false,
665665
"invisible_chests": false,
@@ -846,7 +846,7 @@
846846
"easier_fire_arrow_entry": false,
847847
"fae_torch_count": 3,
848848
"ruto_already_f1_jabu": true,
849-
"ocarina_songs": "off",
849+
"ocarina_songs": [],
850850
"correct_chest_appearances": "textures",
851851
"minor_items_as_major_chest": false,
852852
"invisible_chests": false,
@@ -1020,7 +1020,7 @@
10201020
"easier_fire_arrow_entry": false,
10211021
"fae_torch_count": 3,
10221022
"ruto_already_f1_jabu": false,
1023-
"ocarina_songs": "off",
1023+
"ocarina_songs": [],
10241024
"correct_chest_appearances": "both",
10251025
"minor_items_as_major_chest": false,
10261026
"invisible_chests": false,
@@ -1366,7 +1366,11 @@
13661366
"easier_fire_arrow_entry": false,
13671367
"fae_torch_count": 3,
13681368
"ruto_already_f1_jabu": false,
1369-
"ocarina_songs": "all",
1369+
"ocarina_songs": [
1370+
"frog",
1371+
"warp",
1372+
"frogs2"
1373+
],
13701374
"correct_chest_appearances": "off",
13711375
"minor_items_as_major_chest": false,
13721376
"invisible_chests": true,
@@ -1532,7 +1536,7 @@
15321536
"easier_fire_arrow_entry": false,
15331537
"fae_torch_count": 3,
15341538
"ruto_already_f1_jabu": false,
1535-
"ocarina_songs": "off",
1539+
"ocarina_songs": [],
15361540
"correct_chest_appearances": "off",
15371541
"minor_items_as_major_chest": false,
15381542
"invisible_chests": false,
@@ -1709,7 +1713,7 @@
17091713
"easier_fire_arrow_entry": false,
17101714
"fae_torch_count": 3,
17111715
"ruto_already_f1_jabu": false,
1712-
"ocarina_songs": "off",
1716+
"ocarina_songs": [],
17131717
"correct_chest_appearances": "both",
17141718
"minor_items_as_major_chest": false,
17151719
"invisible_chests": false,
@@ -1885,7 +1889,7 @@
18851889
"easier_fire_arrow_entry": false,
18861890
"fae_torch_count": 3,
18871891
"ruto_already_f1_jabu": false,
1888-
"ocarina_songs": "off",
1892+
"ocarina_songs": [],
18891893
"correct_chest_appearances": "off",
18901894
"minor_items_as_major_chest": false,
18911895
"invisible_chests": false,

0 commit comments

Comments
 (0)