Skip to content
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
940 changes: 474 additions & 466 deletions ASM/build/asm_symbols.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ASM/src/build.asm
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ RANDO_CONTEXT:
.include "bgs_fix.asm"
.include "chus_in_logic.asm"
.include "rainbow_bridge.asm"
.include "lacs_condition.asm"
.include "gossip_hints.asm"
.include "potion_shop.asm"
.include "jabu_elevator.asm"
Expand Down
7 changes: 7 additions & 0 deletions ASM/src/config.asm
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ RAINBOW_BRIDGE_CONDITION:
; 4 = Vanilla
; 5 = Tokens

LACS_CONDITION:
.word 0x00
; 0 = Vanilla
; 1 = Medallions
; 2 = Dungeons
; 3 = Stones

GOSSIP_HINT_CONDITION:
.word 0x00
; 0 = Mask of Truth
Expand Down
16 changes: 16 additions & 0 deletions ASM/src/hacks.asm
Original file line number Diff line number Diff line change
Expand Up @@ -1340,3 +1340,19 @@ skip_GS_BGS_text:
; Replaces: bnez at, 0x80AECC6C
.orga 0xE6BEFC
nop

; ==================================================================================================
; Change the Light Arrow Cutscene trigger condition.
; ==================================================================================================
.orga 0xACCE18
jal lacs_condition_check
lw v0, 0x00A4(s0)
beqz v1, 0x00ACCE9C
nop
nop
nop
nop
nop
nop
nop
nop
48 changes: 48 additions & 0 deletions ASM/src/lacs_condition.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
lacs_condition_check:
lw t1, LACS_CONDITION
li t2, 1
beq t1, t2, @@medallions
nop
li t2, 2
beq t1, t2, @@dungeons
nop
li t2, 3
beq t1, t2, @@stones
nop

@@vanilla:
li t3, 0x0018
and t4, v0, t3
bne t3, t4, @@return
nop
jr ra
li v1, 1

@@medallions:
li t3, 0x003F
and t4, v0, t3
bne t3, t4, @@return
nop
jr ra
li v1, 1

@@dungeons:
lui t3, 0x001C
addiu t3, 0x003F
and t4, v0, t3
bne t3, t4, @@return
nop
jr ra
li v1, 1

@@stones:
lui t3, 0x001C
and t4, v0, t3
bne t3, t4, @@return
nop
jr ra
li v1, 1

@@return:
jr ra
li v1, 0
4 changes: 3 additions & 1 deletion Item.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ def majoritem(self):
return False
if self.smallkey and self.world.shuffle_smallkeys == 'dungeon':
return False
if self.bosskey and self.world.shuffle_bosskeys == 'dungeon':
if self.bosskey and not self.name.endswith('(Ganons Castle)') and self.world.shuffle_bosskeys == 'dungeon':
return False
if self.bosskey and self.name.endswith('(Ganons Castle)') and self.world.shuffle_ganon_bosskey == 'dungeon':
return False

return True
Expand Down
9 changes: 8 additions & 1 deletion ItemPool.py
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,11 @@ def get_pool_core(world):
world.state.collect(item)
pool.extend(get_junk_item())
if world.shuffle_bosskeys == 'remove':
for item in [item for dungeon in world.dungeons for item in dungeon.boss_key]:
for item in [item for dungeon in world.dungeons if dungeon.name != 'Ganons Castle' for item in dungeon.boss_key]:
world.state.collect(item)
pool.extend(get_junk_item())
if world.shuffle_ganon_bosskey == 'remove':
for item in [item for dungeon in world.dungeons if dungeon.name == 'Ganons Castle' for item in dungeon.boss_key]:
world.state.collect(item)
pool.extend(get_junk_item())

Expand Down Expand Up @@ -1207,6 +1211,9 @@ def get_pool_core(world):
if not world.dungeon_mq['Water Temple']:
world.state.collect(ItemFactory('Small Key (Water Temple)'))

if world.shuffle_ganon_bosskey in ['lacs_vanilla', 'lacs_medallions', 'lacs_stones', 'lacs_dungeons']:
placed_items['Zelda'] = 'Boss Key (Ganons Castle)'

if world.item_pool_value == 'plentiful':
pool.extend(easy_items)
else:
Expand Down
17 changes: 13 additions & 4 deletions Patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,17 @@ def set_entrance_updates(entrances):
elif world.bridge == 'tokens':
rom.write_int32(symbol, 5)

# Set up LACS conditions.
symbol = rom.sym('LACS_CONDITION')
if world.lacs_condition == 'medallions':
rom.write_int32(symbol, 1)
elif world.lacs_condition == 'dungeons':
rom.write_int32(symbol, 2)
elif world.lacs_condition == 'stones':
rom.write_int32(symbol, 3)
else:
rom.write_int32(symbol, 0)

if world.open_forest == 'open':
save_context.write_bits(0xED5, 0x10) # "Showed Mido Sword & Shield"

Expand All @@ -1001,8 +1012,6 @@ def set_entrance_updates(entrances):
rom.write_byte(symbol, 0x01)
else:
rom.write_byte(symbol, 0x00)
if world.unlocked_ganondorf:
save_context.write_bits(0x00D4 + 0x0A * 0x1C + 0x04 + 0x1, 0x10) # Ganon's Tower switch flag (unlock boss key door)
if world.skipped_trials['Forest']:
save_context.write_bits(0x0EEA, 0x08) # "Completed Forest Trial"
if world.skipped_trials['Fire']:
Expand Down Expand Up @@ -1483,7 +1492,7 @@ def update_scrub_text(message, text_replacement, default_price, price, item_name
# Change first magic bean to cost 60 (is used as the price for the one time item when beans are shuffled)
rom.write_byte(0xE209FD, 0x3C)

if world.shuffle_smallkeys == 'remove' or world.shuffle_bosskeys == 'remove':
if world.shuffle_smallkeys == 'remove' or world.shuffle_bosskeys == 'remove' or world.shuffle_ganon_bosskey == 'remove':
locked_doors = get_locked_doors(rom, world)
for _,[door_byte, door_bits] in locked_doors.items():
save_context.write_bits(door_byte, door_bits)
Expand Down Expand Up @@ -1883,7 +1892,7 @@ def locked_door(rom, actor_id, actor, scene):
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]

# If boss door, set the door's unlock flag
if world.shuffle_bosskeys == 'remove':
if (world.shuffle_bosskeys == 'remove' and scene != 0x0A) or (world.shuffle_ganon_bosskey == 'remove' and scene == 0x0A):
if actor_id == 0x002E and actor_type == 0x05:
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]

Expand Down
4 changes: 3 additions & 1 deletion Region.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ def can_fill(self, item, manual=False):
is_dungeon_restricted = self.world.shuffle_mapcompass == 'dungeon'
elif item.smallkey and item.type != 'FortressSmallKey':
is_dungeon_restricted = self.world.shuffle_smallkeys == 'dungeon'
elif item.bosskey:
elif item.bosskey and not item.name.endswith('(Ganons Castle)'):
is_dungeon_restricted = self.world.shuffle_bosskeys == 'dungeon'
elif item.bosskey and item.name.endswith('(Ganons Castle)'):
is_dungeon_restricted = self.world.shuffle_ganon_bosskey == 'dungeon'

if is_dungeon_restricted and not manual:
return self.dungeon and self.dungeon.is_dungeon_item(item) and item.world.id == self.world.id
Expand Down
62 changes: 44 additions & 18 deletions SettingsList.py
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,44 @@ def logic_tricks_list_tooltip(widget, pos):
'randomize_key': 'randomize_settings',
},
),
Combobox(
name = 'shuffle_ganon_bosskey',
default = 'dungeon',
choices = {
'remove': "Ganon's Boss Key: Remove",
'dungeon': "Ganon's Boss Key: Dungeon Only",
'keysanity': "Ganon's Boss Key: Anywhere",
'lacs_vanilla': "Ganon's Boss Key: On LACS: Vanilla",
'lacs_medallions': "Ganon's Boss Key: On LACS: Medallions",
'lacs_stones': "Ganon's Boss Key: On LACS: Stones",
'lacs_dungeons': "Ganon's Boss Key: On LACS: Dungeons",
},
gui_group = 'shuffle',
gui_tooltip = '''\
'Remove': Ganon's Castle Boss Key is removed
and the boss door in Ganon's Tower starts unlocked.

'Dungeon': Ganon's Castle Boss Key can only appear
inside Ganon's Castle.

'Anywhere': Ganon's Castle Boss Key can appear
anywhere in the world.

'On LACS': These settings put the boss key on the
Light Arrow Cutscene location, from Zelda in Temple
of Time as adult, with differing requirements.

'On LACS: Vanilla': Shadow and Spirit Medallions.
'On LACS: Medallions': All 6 Medallions.
'On LACS: Stones': All 3 Spiritual Stones.
'On LACS: Dungeons': All Spiritual Stones & Medallions.

''',
shared = True,
gui_params = {
'randomize_key': 'randomize_settings',
},
),
Checkbutton(
name = 'enhance_map_compass',
gui_text = 'Maps and Compasses Give Information',
Expand All @@ -1544,22 +1582,6 @@ def logic_tricks_list_tooltip(widget, pos):
'randomize_key': 'randomize_settings',
},
),
Checkbutton(
name = 'unlocked_ganondorf',
gui_text = 'Remove Ganon\'s Boss Door Lock',
gui_group = 'shuffle',
gui_tooltip = '''\
The Boss Key door in Ganon's Tower
will start unlocked. This is intended
to be used with reduced trial
requirements to make it more likely
that skipped trials can be avoided.
''',
shared = True,
gui_params = {
'randomize_key': 'randomize_settings',
},
),
Checkbutton(
name = 'mq_dungeons_random',
gui_text = 'Random Number of MQ Dungeons',
Expand Down Expand Up @@ -1594,6 +1616,9 @@ def logic_tricks_list_tooltip(widget, pos):
dependency = lambda settings: 0 if settings.mq_dungeons_random or settings.logic_rules == 'glitched' else None,

shared = True,
gui_params = {
'randomize_key': 'randomize_settings',
},
),
Setting_Info(
name = 'disabled_locations',
Expand Down Expand Up @@ -1914,8 +1939,9 @@ def logic_tricks_list_tooltip(widget, pos):
'distribution': [
('half', 1),
('normal', 1),
('double', 1),
('quadruple', 1),
('double', 1),
('quadruple', 1),
('ohko', 1),
],
},
),
Expand Down
18 changes: 16 additions & 2 deletions World.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ def __init__(self, settings):
self.shuffle_special_indoor_entrances = self.entrance_shuffle in ['all-indoors', 'all']
self.shuffle_overworld_entrances = self.entrance_shuffle == 'all'

# Determine LACS Condition
if self.shuffle_ganon_bosskey == 'lacs_medallions':
self.lacs_condition = 'medallions'
elif self.shuffle_ganon_bosskey == 'lacs_dungeons':
self.lacs_condition = 'dungeons'
elif self.shuffle_ganon_bosskey == 'lacs_stones':
self.lacs_condition = 'stones'
else:
self.lacs_condition = 'vanilla'

# trials that can be skipped will be decided later
self.skipped_trials = {
'Forest': False,
Expand Down Expand Up @@ -303,7 +313,9 @@ def get_restricted_dungeon_items(self):
if self.shuffle_smallkeys == 'dungeon':
itempool.extend([item for dungeon in self.dungeons for item in dungeon.small_keys])
if self.shuffle_bosskeys == 'dungeon':
itempool.extend([item for dungeon in self.dungeons for item in dungeon.boss_key])
itempool.extend([item for dungeon in self.dungeons if dungeon.name != 'Ganons Castle' for item in dungeon.boss_key])
if self.shuffle_ganon_bosskey == 'dungeon':
itempool.extend([item for dungeon in self.dungeons if dungeon.name == 'Ganons Castle' for item in dungeon.boss_key])

for item in itempool:
item.world = self
Expand All @@ -318,7 +330,9 @@ def get_unrestricted_dungeon_items(self):
if self.shuffle_smallkeys == 'keysanity':
itempool.extend([item for dungeon in self.dungeons for item in dungeon.small_keys])
if self.shuffle_bosskeys == 'keysanity':
itempool.extend([item for dungeon in self.dungeons for item in dungeon.boss_key])
itempool.extend([item for dungeon in self.dungeons if dungeon.name != 'Ganons Castle' for item in dungeon.boss_key])
if self.shuffle_ganon_bosskey == 'keysanity':
itempool.extend([item for dungeon in self.dungeons if dungeon.name == 'Ganons Castle' for item in dungeon.boss_key])

for item in itempool:
item.world = self
Expand Down
15 changes: 12 additions & 3 deletions data/Glitched World/Overworld.json
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,17 @@
"region_name": "Temple of Time",
"hint": "Temple of Time",
"locations": {
"Zelda": "Shadow_Medallion and Spirit_Medallion and is_adult"
"Zelda": "is_adult and
((lacs_condition == 'vanilla' and Shadow_Medallion and Spirit_Medallion)
or (lacs_condition == 'medallions' and
Forest_Medallion and Fire_Medallion and Water_Medallion and
Shadow_Medallion and Spirit_Medallion and Light_Medallion)
or (lacs_condition == 'dungeons' and
Forest_Medallion and Fire_Medallion and Water_Medallion and
Shadow_Medallion and Spirit_Medallion and Light_Medallion and
Kokiri_Emerald and Goron_Ruby and Zora_Sapphire)
or (lacs_condition == 'stones' and
Kokiri_Emerald and Goron_Ruby and Zora_Sapphire))"
},
"exits": {
"Castle Town": "True",
Expand Down Expand Up @@ -1119,8 +1129,7 @@
"dungeon": "Ganons Castle",
"locations": {
"Ganons Tower Boss Key Chest": "True",
"Ganon": "
(Boss_Key_Ganons_Castle or unlocked_ganondorf) and can_use(Light_Arrows)"
"Ganon": "Boss_Key_Ganons_Castle and can_use(Light_Arrows)"
}
},
{
Expand Down
15 changes: 12 additions & 3 deletions data/World/Overworld.json
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,17 @@
"region_name": "Temple of Time",
"hint": "Temple of Time",
"locations": {
"Zelda": "Shadow_Medallion and Spirit_Medallion and is_adult"
"Zelda": "is_adult and
((lacs_condition == 'vanilla' and Shadow_Medallion and Spirit_Medallion)
or (lacs_condition == 'medallions' and
Forest_Medallion and Fire_Medallion and Water_Medallion and
Shadow_Medallion and Spirit_Medallion and Light_Medallion)
or (lacs_condition == 'dungeons' and
Forest_Medallion and Fire_Medallion and Water_Medallion and
Shadow_Medallion and Spirit_Medallion and Light_Medallion and
Kokiri_Emerald and Goron_Ruby and Zora_Sapphire)
or (lacs_condition == 'stones' and
Kokiri_Emerald and Goron_Ruby and Zora_Sapphire))"
},
"exits": {
"Temple of Time Exterior": "True",
Expand Down Expand Up @@ -1478,8 +1488,7 @@
"dungeon": "Ganons Castle",
"locations": {
"Ganons Tower Boss Key Chest": "True",
"Ganon": "
(Boss_Key_Ganons_Castle or unlocked_ganondorf) and can_use(Light_Arrows)"
"Ganon": "Boss_Key_Ganons_Castle and can_use(Light_Arrows)"
}
},
{
Expand Down
Loading