diff --git a/ItemList.py b/ItemList.py index 740d5ff1..205a41af 100644 --- a/ItemList.py +++ b/ItemList.py @@ -387,13 +387,6 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r if swords == 'swordless': pool.extend(diff.swordless) - elif mode == 'standard': - if want_progressives(): - placed_items.append(('Link\'s Uncle', 'Progressive Sword')) - pool.extend(diff.progressivesword) - else: - placed_items.append(('Link\'s Uncle', 'Fighter Sword')) - pool.extend(diff.basicsword) else: if want_progressives(): pool.extend(diff.progressivesword) @@ -557,14 +550,6 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s itemtotal = itemtotal + 1 if mode == 'standard': - if progressive == 'off': - placed_items.append(('Link\'s Uncle', 'Fighter Sword')) - pool.extend(['Fighter Sword'] * max((customitemarray[32] - 1), 0)) - pool.extend(['Progressive Sword'] * customitemarray[36]) - else: - placed_items.append(('Link\'s Uncle', 'Progressive Sword')) - pool.extend(['Fighter Sword'] * customitemarray[32]) - pool.extend(['Progressive Sword'] * max((customitemarray[36] - 1), 0)) if retro: key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) placed_items.append((key_location, 'Small Key (Universal)')) @@ -572,10 +557,11 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s else: pool.extend(['Small Key (Universal)'] * customitemarray[68]) else: - pool.extend(['Fighter Sword'] * customitemarray[32]) - pool.extend(['Progressive Sword'] * customitemarray[36]) pool.extend(['Small Key (Universal)'] * customitemarray[68]) + pool.extend(['Fighter Sword'] * customitemarray[32]) + pool.extend(['Progressive Sword'] * customitemarray[36]) + if shuffle == 'insanity_legacy': placed_items.append(('Link\'s House', 'Magic Mirror')) placed_items.append(('Sanctuary', 'Moon Pearl')) diff --git a/Rom.py b/Rom.py index 580c23f7..3d88033d 100644 --- a/Rom.py +++ b/Rom.py @@ -551,7 +551,13 @@ def patch_rom(world, player, rom): # set open mode: if world.mode in ['open', 'inverted']: rom.write_byte(0x180032, 0x01) # open mode + if world.mode == 'inverted': + set_inverted_mode(world, rom) + elif world.mode == 'standard': + rom.write_byte(0x180032, 0x00) # standard mode + uncle_location = world.get_location('Link\'s Uncle', player) + if uncle_location.item is None or uncle_location.item.name not in ['Master Sword', 'Tempered Sword', 'Fighter Sword', 'Golden Sword', 'Progressive Sword']: # disable sword sprite from uncle rom.write_bytes(0x6D263, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) rom.write_bytes(0x6D26B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) @@ -563,10 +569,6 @@ def patch_rom(world, player, rom): rom.write_bytes(0x6D2EB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E]) rom.write_bytes(0x6D31B, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) rom.write_bytes(0x6D323, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) - if world.mode == 'inverted': - set_inverted_mode(world, rom) - elif world.mode == 'standard': - rom.write_byte(0x180032, 0x00) # standard mode # set light cones rom.write_byte(0x180038, 0x01 if world.sewer_light_cone else 0x00) @@ -866,12 +868,6 @@ def patch_rom(world, player, rom): rom.write_bytes(0x180080, [50, 50, 70, 70]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10) rom.write_byte(0x18004D, 0x00) # Escape assist (off) - rom.write_byte(0x18004E, 0x00) # escape fills - rom.write_int16(0x180183, 0) # rupee fill (for bow if rupee arrows enabled) - rom.write_bytes(0x180185, [0x00, 0x00, 0x00]) # uncle item refills - rom.write_bytes(0x180188, [0x00, 0x00, 0x00]) # zelda item refills - rom.write_bytes(0x18018B, [0x00, 0x00, 0x00]) # uncle item refills - if world.goal in ['pedestal', 'triforcehunt']: rom.write_byte(0x18003E, 0x01) # make ganon invincible @@ -950,6 +946,24 @@ def patch_rom(world, player, rom): rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E]) rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) + rom.write_byte(0x18004E, 0) # Escape Fill (nothing) + rom.write_int16(0x180183, 300) # Escape fill rupee bow + rom.write_bytes(0x180185, [0,0,0]) # Uncle respawn refills (magic, bombs, arrows) + rom.write_bytes(0x180188, [0,0,0]) # Zelda respawn refills (magic, bombs, arrows) + rom.write_bytes(0x18018B, [0,0,0]) # Mantle respawn refills (magic, bombs, arrows) + if world.mode == 'standard': + if uncle_location.item is not None and uncle_location.item.name in ['Bow', 'Progressive Bow']: + rom.write_byte(0x18004E, 1) # Escape Fill (arrows) + rom.write_int16(0x180183, 300) # Escape fill rupee bow + rom.write_bytes(0x180185, [0,0,70]) # Uncle respawn refills (magic, bombs, arrows) + rom.write_bytes(0x180188, [0,0,10]) # Zelda respawn refills (magic, bombs, arrows) + rom.write_bytes(0x18018B, [0,0,10]) # Mantle respawn refills (magic, bombs, arrows) + elif uncle_location.item is not None and uncle_location.item.name in ['Cane of Somaria', 'Cane of Byrna', 'Fire Rod']: + rom.write_byte(0x18004E, 4) # Escape Fill (magic) + rom.write_bytes(0x180185, [0x80,0,0]) # Uncle respawn refills (magic, bombs, arrows) + rom.write_bytes(0x180188, [0x20,0,0]) # Zelda respawn refills (magic, bombs, arrows) + rom.write_bytes(0x18018B, [0x20,0,0]) # Mantle respawn refills (magic, bombs, arrows) + # patch swamp: Need to enable permanent drain of water as dam or swamp were moved rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required[player] else 0x00) diff --git a/Rules.py b/Rules.py index 0339ec98..116c4148 100644 --- a/Rules.py +++ b/Rules.py @@ -1,5 +1,6 @@ import collections import logging +from BaseClasses import CollectionState def set_rules(world, player): @@ -7,17 +8,17 @@ def set_rules(world, player): if world.logic == 'nologic': logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!') if world.mode != 'inverted': - world.get_region('Links House', player).can_reach = lambda state: True - world.get_region('Sanctuary', player).can_reach = lambda state: True + world.get_region('Links House', player).can_reach_private = lambda state: True + world.get_region('Sanctuary', player).can_reach_private = lambda state: True old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) return else: - world.get_region('Inverted Links House', player).can_reach = lambda state: True - world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach = lambda state: True + world.get_region('Inverted Links House', player).can_reach_private = lambda state: True + world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True if world.shuffle != 'vanilla': old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) return if world.mode != 'inverted': global_rules(world, player) @@ -90,6 +91,9 @@ def forbid_item(location, item, player): old_rule = location.item_rule location.item_rule = lambda i: (i.name != item or i.player != player) and old_rule(i) +def add_item_rule(location, rule): + old_rule = location.item_rule + location.item_rule = lambda item: rule(item) and old_rule(item) def item_in_locations(state, item, player, locations): for location in locations: @@ -111,15 +115,20 @@ def global_rules(world, player): forbid_item(location, 'Triforce Piece', player) # ganon can only carry triforce - world.get_location('Ganon', player).item_rule = lambda item: item.name == 'Triforce' and item.player == player + add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) - # these are default save&quit points and always accessible - world.get_region('Links House', player).can_reach = lambda state: True - world.get_region('Sanctuary', player).can_reach = lambda state: True + if world.mode == 'standard': + world.get_region('Hyrule Castle Secret Entrance', player).can_reach_private = lambda state: True + old_rule = world.get_region('Links House', player).can_reach + world.get_region('Links House', player).can_reach_private = lambda state: state.can_reach('Sanctuary', 'Region', player) or old_rule(state) + else: + # these are default save&quit points and always accessible + world.get_region('Links House', player).can_reach_private = lambda state: True + world.get_region('Sanctuary', player).can_reach_private = lambda state: True # we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled! old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) # overworld requirements set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has_Boots(player)) @@ -454,14 +463,20 @@ def global_rules(world, player): set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player)) def inverted_rules(world, player): - world.get_location('Ganon', player).item_rule = lambda item: item.name == 'Triforce' and item.player == player - world.get_region('Inverted Links House', player).can_reach = lambda state: True - world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach = lambda state: True + if world.goal == 'triforcehunt': + for location in world.get_locations(): + if location.player != player: + forbid_item(location, 'Triforce Piece', player) + + add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) + + world.get_region('Inverted Links House', player).can_reach_private = lambda state: True + world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True # we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled! if world.shuffle != 'vanilla': old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) # overworld requirements set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player)) set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has_Pearl(player)) @@ -930,9 +945,21 @@ def swordless_rules(world, player): def standard_rules(world, player): - for loc in ['Sanctuary', 'Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', - 'Sewers - Secret Room - Right']: - add_rule(world.get_location(loc, player), lambda state: state.can_kill_most_things(player) and state.has_key('Small Key (Escape)', player)) + add_rule(world.get_entrance('Sewers Door', player), lambda state: state.can_kill_most_things(player)) + + set_rule(world.get_entrance('Hyrule Castle Exit (East)', player), lambda state: state.can_reach('Sanctuary', 'Region', player)) + set_rule(world.get_entrance('Hyrule Castle Exit (West)', player), lambda state: state.can_reach('Sanctuary', 'Region', player)) + + # ensures the required weapon for escape lands on uncle (unless player has it pre-equipped) + add_rule(world.get_location('Secret Passage', player), lambda state: state.can_kill_most_things(player)) + add_rule(world.get_location('Hyrule Castle - Map Chest', player), lambda state: state.can_kill_most_things(player)) + + def uncle_item_rule(item): + copy_state = CollectionState(world) + copy_state.collect(item) + return copy_state.can_reach('Sanctuary', 'Region', player) + + add_item_rule(world.get_location('Link\'s Uncle', player), uncle_item_rule) # easiest way to enforce key placement not relevant for open set_rule(world.get_location('Sewers - Dark Cross', player), lambda state: state.can_kill_most_things(player))