From ee215dbfa7703cadc7a846c3bf60d86be26d0459 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 2 Feb 2022 19:50:41 -0600 Subject: [PATCH 01/10] Fixed issue with Links House swapped in OW Tile Swap --- OverworldShuffle.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 66d63897..8b847002 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -67,8 +67,12 @@ def link_overworld(world, player): # move both sets if parallel == IsParallel.Yes and not (all(edge in orig_swaps for edge in map(getParallel, forward_set)) and all(edge in orig_swaps for edge in map(getParallel, back_set))): raise Exception('Cannot move a parallel edge without the other') - new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][0].append(forward_set) - new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][1].append(back_set) + new_mode = OpenStd.Open + if tuple((OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)) not in new_groups.keys(): + # when Links House tile is swapped, the DW edges need to get put into existing Standard group + new_mode = OpenStd.Standard + new_groups[(new_mode, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][0].append(forward_set) + new_groups[(new_mode, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][1].append(back_set) for edge in forward_set: swaps.remove(edge) for edge in back_set: From e9fd006d2b1e3bffe83ef77556c93480b9782d79 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 2 Feb 2022 19:53:15 -0600 Subject: [PATCH 02/10] Changed GT/AT swap in Mixed OW to prioritize leaving AT vanilla unless GT is the only one in the starting world --- EntranceShuffle.py | 6 +++--- Rom.py | 17 ++++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 82c9743f..d30404cc 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -90,11 +90,11 @@ def link_entrances(world, player): for entrancename, exitname in default_skulldrop_connections: connect_logical(world, entrancename, exitname, player, False) - if not invFlag: - for entrancename, exitname in open_default_dungeon_connections: + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and (0x03 not in world.owswaps[player][0]) == invFlag: + for entrancename, exitname in inverted_default_dungeon_connections: connect_logical(world, entrancename, exitname, player, True) else: - for entrancename, exitname in inverted_default_dungeon_connections: + for entrancename, exitname in open_default_dungeon_connections: connect_logical(world, entrancename, exitname, player, True) elif world.shuffle[player] == 'dungeonssimple': suppress_spoiler = False diff --git a/Rom.py b/Rom.py index 1bcea73f..cb2fda68 100644 --- a/Rom.py +++ b/Rom.py @@ -2520,13 +2520,16 @@ def set_inverted_mode(world, player, rom, inverted_buffer): write_int16(rom, snes_to_pc(0x02D998), 0x0000) write_int16(rom, snes_to_pc(0x02D9A6), 0x005A) rom.write_byte(snes_to_pc(0x02D9B3), 0x12) - - if world.shuffle[player] == 'vanilla': - rom.write_byte(0xDBB73 + 0x23, 0x37) # switch AT and GT - rom.write_byte(0xDBB73 + 0x36, 0x24) - if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: - write_int16(rom, 0x15AEE + 2*0x38, 0x00E0) - write_int16(rom, 0x15AEE + 2*0x25, 0x000C) + + # switch AT and GT + if world.shuffle[player] == 'vanilla' and \ + (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and \ + (0x03 not in world.owswaps[player][0]) == world.mode[player] == 'inverted': + rom.write_byte(0xDBB73 + 0x23, 0x37) + rom.write_byte(0xDBB73 + 0x36, 0x24) + if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: + write_int16(rom, 0x15AEE + 2*0x38, 0x00E0) + write_int16(rom, 0x15AEE + 2*0x25, 0x000C) if world.is_tile_swapped(0x05, player): From 7e7efb053388845f8f5a2ab290dd557b31bf3d9d Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 3 Feb 2022 00:25:13 -0600 Subject: [PATCH 03/10] Changed GT/AT swap in Mixed OW to prioritize leaving AT vanilla unless GT is the only one in the starting world --- Rules.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Rules.py b/Rules.py index 21a0ffc6..ecff7ba4 100644 --- a/Rules.py +++ b/Rules.py @@ -854,12 +854,13 @@ def default_rules(world, player): def ow_rules(world, player): - if world.mode[player] != 'inverted': + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and \ + (0x03 not in world.owswaps[player][0]) == world.mode[player] == 'inverted': + set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) + else: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('GT Entry Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) set_rule(world.get_entrance('GT Entry Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player) or state.world.shuffle[player] in ('restricted', 'full', 'lite', 'lean', 'crossed', 'insanity')) - else: - set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) if not world.is_tile_swapped(0x00, player): set_rule(world.get_entrance('Lost Woods East Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -1524,7 +1525,8 @@ def swordless_rules(world, player): set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop - if world.mode[player] != 'inverted': + if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and \ + (0x03 not in world.owswaps[player][0]) == world.mode[player] == 'inverted'): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) From 392cb7187c7371db1d94f5fc82597a90b536f609 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 3 Feb 2022 06:12:18 -0600 Subject: [PATCH 04/10] Changed GT/AT swap in Mixed OW to prioritize leaving AT vanilla unless GT is the only one in the starting world --- EntranceShuffle.py | 2 +- Rom.py | 13 +++++++------ Rules.py | 6 ++---- data/base2current.bps | Bin 87420 -> 87439 bytes 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index d30404cc..79bc43f9 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -90,7 +90,7 @@ def link_entrances(world, player): for entrancename, exitname in default_skulldrop_connections: connect_logical(world, entrancename, exitname, player, False) - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and (0x03 not in world.owswaps[player][0]) == invFlag: + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag): for entrancename, exitname in inverted_default_dungeon_connections: connect_logical(world, entrancename, exitname, player, True) else: diff --git a/Rom.py b/Rom.py index cb2fda68..b7981fb9 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'b300438a7242825e33300f9d155814ef' +RANDOMIZERBASEHASH = '9ff49ef63fdddeb32de09646bd459bf8' class JsonRom(object): @@ -877,7 +877,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x138006, 1) # swap in non-ER Lobby Shuffle Inverted - but only then - if world.mode[player] == 'inverted' and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted') and \ + world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': aga_portal = world.get_portal('Agahnims Tower', player) gt_portal = world.get_portal('Ganons Tower', player) aga_portal.exit_offset, gt_portal.exit_offset = gt_portal.exit_offset, aga_portal.exit_offset @@ -1286,6 +1287,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] or world.goal[player] == 'trinity' else 0x00) # pre-open Pyramid Hole rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0 + rom.write_byte(0x18008F, 0x01 if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted') else 0x00) # AT/GT swapped rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness @@ -2168,7 +2170,7 @@ def write_strings(rom, world, player, team): entrances_to_hint = {} entrances_to_hint.update(InconvenientDungeonEntrances) if world.shuffle_ganon: - if world.mode[player] == 'inverted': + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) else: entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'}) @@ -2201,7 +2203,7 @@ def write_strings(rom, world, player, team): if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']: entrances_to_hint.update(ConnectorEntrances) entrances_to_hint.update(DungeonEntrances) - if world.mode[player] == 'inverted': + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): entrances_to_hint.update({'Ganons Tower': 'The dark mountain tower'}) else: entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) @@ -2523,8 +2525,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer): # switch AT and GT if world.shuffle[player] == 'vanilla' and \ - (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and \ - (0x03 not in world.owswaps[player][0]) == world.mode[player] == 'inverted': + (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): rom.write_byte(0xDBB73 + 0x23, 0x37) rom.write_byte(0xDBB73 + 0x36, 0x24) if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: diff --git a/Rules.py b/Rules.py index ecff7ba4..50d808b0 100644 --- a/Rules.py +++ b/Rules.py @@ -854,8 +854,7 @@ def default_rules(world, player): def ow_rules(world, player): - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and \ - (0x03 not in world.owswaps[player][0]) == world.mode[player] == 'inverted': + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) else: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle @@ -1525,8 +1524,7 @@ def swordless_rules(world, player): set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop - if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) and \ - (0x03 not in world.owswaps[player][0]) == world.mode[player] == 'inverted'): + if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted')): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) diff --git a/data/base2current.bps b/data/base2current.bps index efdb59ccceb0b7caa41af37d383dd6b05c25e98b..d588f66b3b9c808ccf8ec17e3a8522db9a3095ca 100644 GIT binary patch delta 4858 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 Date: Thu, 3 Feb 2022 06:14:22 -0600 Subject: [PATCH 05/10] Changed GT/AT swap in Mixed OW to prioritize leaving AT vanilla unless GT is the only one in the starting world --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index b7981fb9..55a1af05 100644 --- a/Rom.py +++ b/Rom.py @@ -1275,7 +1275,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # assorted fixes rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world else 0x00) # remain in real dark world when dying in dark world dungeon before killing aga1 rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. - if world.mode[player] == 'inverted': + if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180173, 0x01) # Bob is enabled From 7d391da21d26b2c51a2708e6270f2ee709853a1e Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 3 Feb 2022 06:35:54 -0600 Subject: [PATCH 06/10] Added Flute Spots to spoiler log --- BaseClasses.py | 12 ++++++++++++ OverworldShuffle.py | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/BaseClasses.py b/BaseClasses.py index 7e17d469..e431cb4a 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3053,6 +3053,18 @@ class Spoiler(object): if self.overworlds: outfile.write('\n\nOverworld:\n\n') + + # flute shuffle + for player in range(1, self.world.players + 1): + if ('flute', player) in self.maps: + outfile.write('Flute Spots:\n') + break + for player in range(1, self.world.players + 1): + if ('flute', player) in self.maps: + if self.world.players > 1: + outfile.write(str('(Player ' + str(player) + ')\n')) # player name + outfile.write(self.maps[('flute', player)]['text'] + '\n\n') + # overworld tile swaps for player in range(1, self.world.players + 1): if ('swaps', player) in self.maps: diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 8b847002..4328101f 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -420,6 +420,24 @@ def link_overworld(world, player): world.owflutespots[player] = new_spots connect_flutes(new_spots) + # update spoiler + s = list(map(lambda x: ' ' if x not in new_spots else 'F', [i for i in range(0x40)])) + text_output = tile_swap_spoiler_table.replace('s', '%s') % ( s[0x02], s[0x07], + s[0x00], s[0x03], s[0x05], + s[0x00], s[0x02],s[0x03], s[0x05], s[0x07], s[0x0a], s[0x0f], + s[0x0a], s[0x0f], + s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], + s[0x18], s[0x1a],s[0x1b], s[0x1d],s[0x1e], + s[0x22], s[0x25], s[0x1a], s[0x1d], + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], s[0x18], s[0x1b], s[0x1e], + s[0x30], s[0x32],s[0x33],s[0x34],s[0x35], s[0x37], s[0x22], s[0x25], + s[0x3a],s[0x3b],s[0x3c], s[0x3f], + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], + s[0x32],s[0x33],s[0x34], s[0x37], + s[0x30], s[0x35], + s[0x3a],s[0x3b],s[0x3c], s[0x3f]) + world.spoiler.set_map('flute', text_output, new_spots, player) + def connect_custom(world, connected_edges, player): if hasattr(world, 'custom_overworld') and world.custom_overworld[player]: for edgename1, edgename2 in world.custom_overworld[player]: From 75d097c4b69866689ff7f24fb6703e2f3b841b4a Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 3 Feb 2022 06:46:06 -0600 Subject: [PATCH 07/10] Changed GT/AT swap in Mixed OW to prioritize leaving AT vanilla unless GT is the only one in the starting world --- EntranceShuffle.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 79bc43f9..e21aca8a 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -288,7 +288,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -361,7 +361,7 @@ def link_entrances(world, player): lw_dungeons.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dw_dungeons.append('Ganons Tower Exit') @@ -449,7 +449,7 @@ def link_entrances(world, player): Dungeon_Exits.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: Dungeon_Exits.append('Ganons Tower Exit') @@ -499,7 +499,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -567,7 +567,7 @@ def link_entrances(world, player): caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) connect_two_way(world, 'Pyramid Entrance' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole', 'Pyramid', player) else: @@ -662,7 +662,7 @@ def link_entrances(world, player): world.ganon_at_pyramid[player] = False # check for Ganon's Tower location - if world.get_entrance('Ganons Tower' if not invFlag else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': + if world.get_entrance('Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': world.ganonstower_vanilla[player] = False @@ -1235,7 +1235,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player): connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dungeon_exits.append('Ganons Tower Exit') From d60b4c61ce968b5fdb69f3f28b35f8c5dad00502 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 3 Feb 2022 06:54:42 -0600 Subject: [PATCH 08/10] Simplified AT/GT swap condition check --- BaseClasses.py | 3 +++ EntranceShuffle.py | 16 ++++++++-------- Rom.py | 14 ++++++-------- Rules.py | 4 ++-- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index e431cb4a..d836b1f3 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -304,6 +304,9 @@ class World(object): def is_tile_swapped(self, owid, player): return (self.mode[player] == 'inverted') != (owid in self.owswaps[player][0] and self.owMixed[player]) + def is_atgt_swapped(self, player): + return (0x03 in self.owswaps[player][0]) == (0x1b in self.owswaps[player][0]) == (self.mode[player] != 'inverted') + def is_bombshop_start(self, player): return self.is_tile_swapped(0x2c, player) and (self.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] or not self.shufflelinks[player]) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index e21aca8a..eb22b044 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -90,7 +90,7 @@ def link_entrances(world, player): for entrancename, exitname in default_skulldrop_connections: connect_logical(world, entrancename, exitname, player, False) - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag): + if world.is_atgt_swapped[player]: for entrancename, exitname in inverted_default_dungeon_connections: connect_logical(world, entrancename, exitname, player, True) else: @@ -288,7 +288,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -361,7 +361,7 @@ def link_entrances(world, player): lw_dungeons.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dw_dungeons.append('Ganons Tower Exit') @@ -449,7 +449,7 @@ def link_entrances(world, player): Dungeon_Exits.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) else: Dungeon_Exits.append('Ganons Tower Exit') @@ -499,7 +499,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -567,7 +567,7 @@ def link_entrances(world, player): caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) connect_two_way(world, 'Pyramid Entrance' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole', 'Pyramid', player) else: @@ -662,7 +662,7 @@ def link_entrances(world, player): world.ganon_at_pyramid[player] = False # check for Ganon's Tower location - if world.get_entrance('Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': + if world.get_entrance('Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': world.ganonstower_vanilla[player] = False @@ -1235,7 +1235,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player): connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (not invFlag)) else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dungeon_exits.append('Ganons Tower Exit') diff --git a/Rom.py b/Rom.py index 55a1af05..b6046c3f 100644 --- a/Rom.py +++ b/Rom.py @@ -877,8 +877,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x138006, 1) # swap in non-ER Lobby Shuffle Inverted - but only then - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted') and \ - world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': + if world.is_atgt_swapped[player] and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': aga_portal = world.get_portal('Agahnims Tower', player) gt_portal = world.get_portal('Ganons Tower', player) aga_portal.exit_offset, gt_portal.exit_offset = gt_portal.exit_offset, aga_portal.exit_offset @@ -1275,7 +1274,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # assorted fixes rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world else 0x00) # remain in real dark world when dying in dark world dungeon before killing aga1 rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): + if world.is_atgt_swapped[player]: rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180173, 0x01) # Bob is enabled @@ -1287,7 +1286,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] or world.goal[player] == 'trinity' else 0x00) # pre-open Pyramid Hole rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0 - rom.write_byte(0x18008F, 0x01 if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted') else 0x00) # AT/GT swapped + rom.write_byte(0x18008F, 0x01 if world.is_atgt_swapped[player] else 0x00) # AT/GT swapped rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness @@ -2170,7 +2169,7 @@ def write_strings(rom, world, player, team): entrances_to_hint = {} entrances_to_hint.update(InconvenientDungeonEntrances) if world.shuffle_ganon: - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): + if world.is_atgt_swapped[player]: entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) else: entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'}) @@ -2203,7 +2202,7 @@ def write_strings(rom, world, player, team): if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']: entrances_to_hint.update(ConnectorEntrances) entrances_to_hint.update(DungeonEntrances) - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): + if world.is_atgt_swapped[player]: entrances_to_hint.update({'Ganons Tower': 'The dark mountain tower'}) else: entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) @@ -2524,8 +2523,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer): rom.write_byte(snes_to_pc(0x02D9B3), 0x12) # switch AT and GT - if world.shuffle[player] == 'vanilla' and \ - (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): + if world.shuffle[player] == 'vanilla' and world.is_atgt_swapped[player]: rom.write_byte(0xDBB73 + 0x23, 0x37) rom.write_byte(0xDBB73 + 0x36, 0x24) if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: diff --git a/Rules.py b/Rules.py index 50d808b0..cabe695e 100644 --- a/Rules.py +++ b/Rules.py @@ -854,7 +854,7 @@ def default_rules(world, player): def ow_rules(world, player): - if (0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted'): + if world.is_atgt_swapped[player]: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) else: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle @@ -1524,7 +1524,7 @@ def swordless_rules(world, player): set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop - if not ((0x03 in world.owswaps[player][0]) == (0x1b in world.owswaps[player][0]) == (world.mode[player] != 'inverted')): + if not world.is_atgt_swapped[player]: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) From 7a186e647f8011d0a2f36b2330cb798ce4a6cc6b Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 3 Feb 2022 06:57:56 -0600 Subject: [PATCH 09/10] Simplified AT/GT swap condition check --- EntranceShuffle.py | 16 ++++++++-------- Rom.py | 12 ++++++------ Rules.py | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index eb22b044..46217d2e 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -90,7 +90,7 @@ def link_entrances(world, player): for entrancename, exitname in default_skulldrop_connections: connect_logical(world, entrancename, exitname, player, False) - if world.is_atgt_swapped[player]: + if world.is_atgt_swapped(player): for entrancename, exitname in inverted_default_dungeon_connections: connect_logical(world, entrancename, exitname, player, True) else: @@ -288,7 +288,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -361,7 +361,7 @@ def link_entrances(world, player): lw_dungeons.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dw_dungeons.append('Ganons Tower Exit') @@ -449,7 +449,7 @@ def link_entrances(world, player): Dungeon_Exits.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: Dungeon_Exits.append('Ganons Tower Exit') @@ -499,7 +499,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -567,7 +567,7 @@ def link_entrances(world, player): caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) connect_two_way(world, 'Pyramid Entrance' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole', 'Pyramid', player) else: @@ -662,7 +662,7 @@ def link_entrances(world, player): world.ganon_at_pyramid[player] = False # check for Ganon's Tower location - if world.get_entrance('Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': + if world.get_entrance('Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': world.ganonstower_vanilla[player] = False @@ -1235,7 +1235,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player): connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped[player] else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dungeon_exits.append('Ganons Tower Exit') diff --git a/Rom.py b/Rom.py index b6046c3f..a71c1a72 100644 --- a/Rom.py +++ b/Rom.py @@ -877,7 +877,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x138006, 1) # swap in non-ER Lobby Shuffle Inverted - but only then - if world.is_atgt_swapped[player] and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': + if world.is_atgt_swapped(player) and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': aga_portal = world.get_portal('Agahnims Tower', player) gt_portal = world.get_portal('Ganons Tower', player) aga_portal.exit_offset, gt_portal.exit_offset = gt_portal.exit_offset, aga_portal.exit_offset @@ -1274,7 +1274,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # assorted fixes rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world else 0x00) # remain in real dark world when dying in dark world dungeon before killing aga1 rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. - if world.is_atgt_swapped[player]: + if world.is_atgt_swapped(player): rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180173, 0x01) # Bob is enabled @@ -1286,7 +1286,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] or world.goal[player] == 'trinity' else 0x00) # pre-open Pyramid Hole rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0 - rom.write_byte(0x18008F, 0x01 if world.is_atgt_swapped[player] else 0x00) # AT/GT swapped + rom.write_byte(0x18008F, 0x01 if world.is_atgt_swapped(player) else 0x00) # AT/GT swapped rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness @@ -2169,7 +2169,7 @@ def write_strings(rom, world, player, team): entrances_to_hint = {} entrances_to_hint.update(InconvenientDungeonEntrances) if world.shuffle_ganon: - if world.is_atgt_swapped[player]: + if world.is_atgt_swapped(player): entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) else: entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'}) @@ -2202,7 +2202,7 @@ def write_strings(rom, world, player, team): if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']: entrances_to_hint.update(ConnectorEntrances) entrances_to_hint.update(DungeonEntrances) - if world.is_atgt_swapped[player]: + if world.is_atgt_swapped(player): entrances_to_hint.update({'Ganons Tower': 'The dark mountain tower'}) else: entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) @@ -2523,7 +2523,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer): rom.write_byte(snes_to_pc(0x02D9B3), 0x12) # switch AT and GT - if world.shuffle[player] == 'vanilla' and world.is_atgt_swapped[player]: + if world.shuffle[player] == 'vanilla' and world.is_atgt_swapped(player): rom.write_byte(0xDBB73 + 0x23, 0x37) rom.write_byte(0xDBB73 + 0x36, 0x24) if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: diff --git a/Rules.py b/Rules.py index cabe695e..7d3227e8 100644 --- a/Rules.py +++ b/Rules.py @@ -854,7 +854,7 @@ def default_rules(world, player): def ow_rules(world, player): - if world.is_atgt_swapped[player]: + if world.is_atgt_swapped(player): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) else: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle @@ -1524,7 +1524,7 @@ def swordless_rules(world, player): set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop - if not world.is_atgt_swapped[player]: + if not world.is_atgt_swapped(player): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) From 08a8d9223cbe9af6e0bea2a5e6b19ed972287740 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 3 Feb 2022 06:59:24 -0600 Subject: [PATCH 10/10] Version bump 0.2.5.3 --- CHANGELOG.md | 6 ++++++ OverworldShuffle.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd5f3cb1..2ef9d2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +### 0.2.5.3 +- Changed AT/GT Swap to favor vanilla, only swapping if GT entrance is the only choice in starting world +- Fixed issue with Links House not swapping in OW Mixed +- Added Flute Spots to spoiler log +- Fixed issue with Light Hype Fairy excluded from bombable door list + ### 0.2.5.1 - Fixed missing rule for Inverted VoO Portal access diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 4328101f..e535366f 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -5,7 +5,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.5.2-u' +__version__ = '0.2.5.3-u' def link_overworld(world, player): # setup mandatory connections