From be6203e3f042f37817fcff567e0a9c366b14b654 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Mon, 16 Aug 2021 22:46:26 -0500 Subject: [PATCH] Moved Crossed OW Shuffle to its own setting, Renamed Tile Swap to Mixed --- BaseClasses.py | 16 +- CLI.py | 5 +- DoorShuffle.py | 2 +- EntranceShuffle.py | 24 +- ItemList.py | 31 +- Main.py | 6 +- Mystery.py | 4 +- OverworldShuffle.py | 570 ++++++++++++++---- README.md | 28 +- Rom.py | 137 ++--- Rules.py | 82 +-- asm/owrando.asm | 2 +- data/base2current.bps | Bin 141167 -> 141164 bytes mystery_example.yml | 10 +- resources/app/cli/args.json | 13 +- resources/app/cli/lang/en.json | 17 +- resources/app/gui/lang/en.json | 7 +- .../app/gui/randomize/overworld/widgets.json | 17 +- source/classes/constants.py | 3 +- source/gui/randomize/overworld.py | 16 +- 20 files changed, 645 insertions(+), 345 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 32028111..a1d522e8 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -19,13 +19,14 @@ from RoomData import Room class World(object): - def __init__(self, players, owShuffle, owSwap, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, + def __init__(self, players, owShuffle, owCrossed, owMixed, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints): self.players = players self.teams = 1 self.owShuffle = owShuffle.copy() - self.owSwap = owSwap.copy() + self.owCrossed = owCrossed.copy() self.owKeepSimilar = {} + self.owMixed = owMixed.copy() self.owFluteShuffle = {} self.shuffle = shuffle.copy() self.doorShuffle = doorShuffle.copy() @@ -2308,8 +2309,9 @@ class Spoiler(object): 'weapons': self.world.swords, 'goal': self.world.goal, 'ow_shuffle': self.world.owShuffle, - 'ow_swap': self.world.owSwap, + 'ow_crossed': self.world.owCrossed, 'ow_keepsimilar': self.world.owKeepSimilar, + 'ow_mixed': self.world.owMixed, 'ow_fluteshuffle': self.world.owFluteShuffle, 'shuffle': self.world.shuffle, 'door_shuffle': self.world.doorShuffle, @@ -2390,9 +2392,10 @@ class Spoiler(object): outfile.write('Difficulty:'.ljust(line_width) + '%s\n' % self.metadata['item_pool'][player]) outfile.write('Item Functionality:'.ljust(line_width) + '%s\n' % self.metadata['item_functionality'][player]) outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player]) - outfile.write('Overworld Tile Swap:'.ljust(line_width) + '%s\n' % self.metadata['ow_swap'][player]) if self.metadata['ow_shuffle'][player] != 'vanilla': + outfile.write('Crossed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_crossed'][player] else 'No')) outfile.write('Keep Similar OW Edges Together:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_keepsimilar'][player] else 'No')) + outfile.write('Mixed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_mixed'][player] else 'No')) outfile.write('Flute Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_fluteshuffle'][player]) outfile.write('Entrance Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['shuffle'][player]) outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player]) @@ -2555,8 +2558,7 @@ class Pot(object): # byte 0: DDOO OEEE (DR, OR, ER) dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0} -or_mode = {"vanilla": 0, "parallel": 1, "full": 2} -orswap_mode = {"vanilla": 0, "mixed": 1, "crossed": 1} +or_mode = {"vanilla": 0, "parallel": 1, "full": 1} er_mode = {"vanilla": 0, "simple": 1, "restricted": 3, "full": 3, "crossed": 4, "insanity": 5, "dungeonsfull": 7, "dungeonssimple": 7} # byte 1: LLLW WSSR (logic, mode, sword, retro) @@ -2592,7 +2594,7 @@ class Settings(object): @staticmethod def make_code(w, p): code = bytes([ - (dr_mode[w.doorShuffle[p]] << 6) | (or_mode[w.owShuffle[p]] << 4) | (orswap_mode[w.owSwap[p]] << 3) | er_mode[w.shuffle[p]], + (dr_mode[w.doorShuffle[p]] << 6) | (or_mode[w.owShuffle[p]] << 5) | (0x10 if w.owCrossed[p] else 0) | (0x08 if w.owMixed[p] else 0) | er_mode[w.shuffle[p]], (logic_mode[w.logic[p]] << 5) | (world_mode[w.mode[p]] << 3) | (sword_mode[w.swords[p]] << 1) | (1 if w.retro[p] else 0), diff --git a/CLI.py b/CLI.py index fb9367c3..ccc66e00 100644 --- a/CLI.py +++ b/CLI.py @@ -94,7 +94,7 @@ def parse_cli(argv, no_defaults=False): playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True) for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', - 'ow_shuffle', 'ow_swap', 'ow_keepsimilar', 'ow_fluteshuffle', + 'ow_shuffle', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_fluteshuffle', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', 'bombbag', @@ -146,8 +146,9 @@ def parse_settings(): "openpyramid": False, "shuffleganon": True, "ow_shuffle": "vanilla", - "ow_swap": "vanilla", + "ow_crossed": False, "ow_keepsimilar": False, + "ow_mixed": False, "ow_fluteshuffle": "vanilla", "shuffle": "vanilla", "shufflelinks": False, diff --git a/DoorShuffle.py b/DoorShuffle.py index 8ad99965..0a8b33fc 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -214,7 +214,7 @@ def vanilla_key_logic(world, player): analyze_dungeon(key_layout, world, player) world.key_logic[player][builder.name] = key_layout.key_logic log_key_logic(builder.name, key_layout.key_logic) - if world.shuffle[player] == 'vanilla' and world.owShuffle[player] == 'vanilla' and world.owSwap[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]: + if world.shuffle[player] == 'vanilla' and world.owShuffle[player] == 'vanilla' and not world.owCrossed[player] and world.owMixed[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]: validate_vanilla_key_logic(world, player) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index dd98a9ed..0863010b 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -50,18 +50,18 @@ def link_entrances(world, player): # inverted entrance mods for owid in swapped_connections.keys(): - if invFlag != (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if invFlag != (owid in world.owswaps[player][0] and world.owMixed[player]): for (entrancename, exitname) in swapped_connections[owid]: try: connect_two_way(world, entrancename, exitname, player) except RuntimeError: connect_entrance(world, entrancename, exitname, player) - if invFlag != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \ - invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if invFlag != (0x03 in world.owswaps[player][0] and world.owMixed[player]) and \ + invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]): connect_entrance(world, 'Death Mountain Return Cave (West)', 'Dark Death Mountain Healer Fairy', player) - elif invFlag != (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \ - invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + elif invFlag != (0x0a in world.owswaps[player][0] and world.owMixed[player]) and \ + invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]): connect_two_way(world, 'Bumper Cave (Top)', 'Death Mountain Return Cave Exit (West)', player) # dungeon entrance shuffle @@ -225,7 +225,7 @@ def link_entrances(world, player): random.shuffle(remaining_entrances) old_man_entrance = remaining_entrances.pop() - connect_two_way(world, old_man_entrance if invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player) + connect_two_way(world, old_man_entrance if invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]) else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) if invFlag and old_man_exit == 'Spike Cave': bomb_shop_doors.remove('Spike Cave') @@ -791,8 +791,8 @@ def link_entrances(world, 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, 'Pyramid Entrance' if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) - connect_entrance(world, 'Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', 'Pyramid', player) + connect_two_way(world, 'Pyramid Entrance' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole', 'Pyramid', player) else: caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) hole_targets.append('Pyramid') @@ -804,7 +804,7 @@ def link_entrances(world, player): exit_pool.extend(['Agahnims Tower']) doors.extend(['Agahnims Tower']) - if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]): exit_pool.extend(['Pyramid Entrance']) hole_entrances.append('Pyramid Hole') entrances_must_exits.append('Pyramid Entrance') @@ -943,7 +943,7 @@ def link_entrances(world, player): world.powder_patch_required[player] = True # check for ganon location - if world.get_entrance('Pyramid Hole' if invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid': + if world.get_entrance('Pyramid Hole' if invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid': world.ganon_at_pyramid[player] = False # check for Ganon's Tower location @@ -1030,7 +1030,7 @@ def scramble_holes(world, player): ('Lumberjack Tree Exit', 'Lumberjack Tree (top)')] if not world.shuffle_ganon: - if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]): connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) else: @@ -1053,7 +1053,7 @@ def scramble_holes(world, player): if world.shuffle_ganon: random.shuffle(hole_targets) exit, target = hole_targets.pop() - if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]): connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) else: diff --git a/ItemList.py b/ItemList.py index 3d5c3a15..c07da745 100644 --- a/ItemList.py +++ b/ItemList.py @@ -258,7 +258,7 @@ def generate_itempool(world, player): (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray) world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) else: - (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.owShuffle[player], world.owSwap[player], world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player]) + (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player]) if player in world.pool_adjustment.keys(): amt = world.pool_adjustment[player] @@ -727,7 +727,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)' 'Rupees (100)': 100, 'Rupees (300)': 300} -def get_pool_core(progressive, owShuffle, owSwap, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic): +def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic): pool = [] placed_items = {} precollected_items = [] @@ -990,21 +990,20 @@ def test(): for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']: for retro in [True, False]: for door_shuffle in ['basic', 'crossed', 'vanilla']: - for owShuffle in ['full', 'parallel', 'vanilla']: - for owSwap in ['vanilla', 'mixed', 'crossed']: - out = get_pool_core(progressive, owShuffle, owSwap, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic) - count = len(out[0]) + len(out[1]) + for bombbag in [True, False]: + out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic) + count = len(out[0]) + len(out[1]) - correct_count = total_items_to_place - if goal == 'pedestal' and swords != 'vanilla': - # pedestal goals generate one extra item - correct_count += 1 - if retro: - correct_count += 28 - try: - assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag)) - except AssertionError as e: - print(e) + correct_count = total_items_to_place + if goal == 'pedestal' and swords != 'vanilla': + # pedestal goals generate one extra item + correct_count += 1 + if retro: + correct_count += 28 + try: + assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag)) + except AssertionError as e: + print(e) if __name__ == '__main__': test() diff --git a/Main.py b/Main.py index 84d97fb9..b7f0c295 100644 --- a/Main.py +++ b/Main.py @@ -61,7 +61,7 @@ def main(args, seed=None, fish=None): for player, code in args.code.items(): if code: Settings.adjust_args_from_code(code, player, args) - world = World(args.multi, args.ow_shuffle, args.ow_swap, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, + world = World(args.multi, args.ow_shuffle, args.ow_crossed, args.ow_mixed, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints) logger = logging.getLogger('') @@ -263,7 +263,7 @@ def main(args, seed=None, fish=None): customize_shops(world, player) balance_money_progression(world) - if world.owShuffle[1] != 'vanilla' or world.owSwap[1] != 'vanilla' or str(world.seed).startswith('M'): + if world.owShuffle[1] != 'vanilla' or world.owCrossed[1] or world.owMixed[1] or str(world.seed).startswith('M'): outfilebase = f'OR_{args.outputname if args.outputname else world.seed}' else: outfilebase = f'DR_{args.outputname if args.outputname else world.seed}' @@ -369,7 +369,7 @@ def main(args, seed=None, fish=None): def copy_world(world): # ToDo: Not good yet - ret = World(world.players, world.owShuffle, world.owSwap, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, + ret = World(world.players, world.owShuffle, world.owCrossed, world.owMixed, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints) ret.teams = world.teams diff --git a/Mystery.py b/Mystery.py index 88f5d7f0..f92764d9 100644 --- a/Mystery.py +++ b/Mystery.py @@ -133,9 +133,9 @@ def roll_settings(weights): overworld_shuffle = get_choice('overworld_shuffle') ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla' - overworld_swap = get_choice('overworld_swap') - ret.ow_swap = overworld_swap if overworld_swap != 'none' else 'vanilla' + ret.ow_crossed = get_choice('overworld_crossed') ret.ow_keepsimilar = get_choice('overworld_keepsimilar') + ret.ow_mixed = get_choice('overworld_mixed') overworld_flute = get_choice('flute_shuffle') ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla' entrance_shuffle = get_choice('entrance_shuffle') diff --git a/OverworldShuffle.py b/OverworldShuffle.py index d1a3784f..5d1d9669 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -11,9 +11,25 @@ def link_overworld(world, player): for exitname, destname in temporary_mandatory_connections: connect_two_way(world, exitname, destname, player) - # tile shuffle trimmed_groups = copy.deepcopy(OWEdgeGroups) - if world.owSwap[player] != 'vanilla': + + # adjust Frog/Dig Game swap manually due to NP/P relationship with LW + if world.owShuffle[player] == 'parallel' and not world.owKeepSimilar[player]: + for group in trimmed_groups.keys(): + (std, region, axis, terrain, _, _) = group + (forward_edges, back_edges) = trimmed_groups[group] + if ['Dig Game EC', 'Dig Game ES'] in forward_edges: + forward_edges = list(filter((['Dig Game EC', 'Dig Game ES']).__ne__, forward_edges)) + trimmed_groups[(std, region, axis, terrain, IsParallel.Yes, 1)][0].append(['Dig Game ES']) + trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][0].append(['Dig Game EC']) + if ['Frog WC', 'Frog WS'] in back_edges: + back_edges = list(filter((['Frog WC', 'Frog WS']).__ne__, back_edges)) + trimmed_groups[(std, region, axis, terrain, IsParallel.Yes, 1)][1].append(['Frog WS']) + trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][1].append(['Frog WC']) + trimmed_groups[group] = (forward_edges, back_edges) + + # tile shuffle + if world.owMixed[player]: tile_groups = {} for (name, groupType) in OWTileGroups.keys(): if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks']: @@ -44,7 +60,7 @@ def link_overworld(world, player): exist_dw_regions.extend(OWTileRegions.inverse[owid]) tile_groups[(name, groupType)] = (exist_owids, exist_lw_regions, exist_dw_regions) - #tile shuffle happens here, the groups that remain in the list are the tiles that get swapped + # tile shuffle happens here, the groups that remain in the list are the tiles that get swapped removed = list() for group in tile_groups.keys(): if random.randint(0, 1): @@ -52,7 +68,7 @@ def link_overworld(world, player): for group in removed: tile_groups.pop(group, None) - #save shuffled tiles to world object + # save shuffled tiles to world object for group in tile_groups.keys(): (owids, lw_regions, dw_regions) = tile_groups[group] (exist_owids, exist_lw_regions, exist_dw_regions) = world.owswaps[player] @@ -61,7 +77,7 @@ def link_overworld(world, player): exist_dw_regions.extend(dw_regions) world.owswaps[player] = [exist_owids, exist_lw_regions, exist_dw_regions] - #replace LW edges with DW + # replace LW edges with DW ignore_list = list() #TODO: Remove ignore_list when special OW areas are included in pool for edgeset in temporary_mandatory_connections: for edge in edgeset: @@ -108,7 +124,7 @@ def link_overworld(world, player): #TODO: Figure out a way to handle index changes on the fly when removing items logging.getLogger('').warning('OW Tile Swap encountered minor IndexError... retrying') - if 0x28 in world.owswaps[player][0]: #handle Frog/Dig Game swap manually due to NP/P relationship with LW + if world.owShuffle[player] != 'parallel' and 0x28 in world.owswaps[player][0]: # handle Frog/Dig Game swap manually due to NP/P relationship with LW trimmed_groups[(OpenStd.Open, WorldType.Dark, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][0].append(['Maze Race ES']) trimmed_groups[(OpenStd.Open, WorldType.Dark, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][1].append(['Kakariko Suburb WS']) trimmed_groups[(OpenStd.Open, WorldType.Light, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][0].remove(['Maze Race ES']) @@ -139,7 +155,7 @@ def link_overworld(world, player): # make new connections for owid in ow_connections.keys(): - if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owMixed[player]): for (exitname, regionname) in ow_connections[owid][0]: connect_simple(world, exitname, regionname, player) else: @@ -151,66 +167,99 @@ def link_overworld(world, player): connect_custom(world, connected_edges, player) # layout shuffle - if world.owShuffle[player] == 'vanilla': + if world.owShuffle[player] == 'vanilla' and not world.owCrossed[player]: + # vanilla transitions groups = list(trimmed_groups.values()) for (forward_edge_sets, back_edge_sets) in groups: assert len(forward_edge_sets) == len(back_edge_sets) for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets): assert len(forward_set) == len(back_set) for (forward_edge, back_edge) in zip(forward_set, back_set): - connect_two_way(world, forward_edge, back_edge, player) - connected_edges.append(forward_edge) - connected_edges.append(back_edge) + connect_two_way(world, forward_edge, back_edge, player, connected_edges) assert len(connected_edges) == len(default_connections) * 2, connected_edges else: if world.owKeepSimilar[player] and world.owShuffle[player] == 'parallel': for exitname, destname in parallelsimilar_connections: - connect_two_way(world, exitname, destname, player) - connected_edges.append(exitname) - connected_edges.append(destname) + connect_two_way(world, exitname, destname, player, connected_edges) + + if world.owShuffle[player] == 'vanilla' and world.owCrossed[player]: + if world.mode[player] == 'standard': + # connect vanilla std + for group in trimmed_groups.keys(): + (std, _, _, _, _, _) = group + if std == OpenStd.Standard: + (forward_set, back_set) = trimmed_groups[group] + for (forward_edges, back_edges) in zip(forward_set, back_set): + for (forward_edge, back_edge) in zip(forward_edges, back_edges): + connect_two_way(world, forward_edge, back_edge, player, connected_edges) + + # connect non-parallel edges + for group in trimmed_groups.keys(): + (_, _, _, _, parallel, _) = group + if parallel == IsParallel.No: + (forward_set, back_set) = trimmed_groups[group] + for (forward_edges, back_edges) in zip(forward_set, back_set): + for (forward_edge, back_edge) in zip(forward_edges, back_edges): + if forward_edge not in connected_edges and back_edge not in connected_edges: + connect_two_way(world, forward_edge, back_edge, player, connected_edges) #TODO: Remove, just for testing for exitname, destname in test_connections: - connect_two_way(world, exitname, destname, player) - connected_edges.append(exitname) - connected_edges.append(destname) + connect_two_way(world, exitname, destname, player, connected_edges) trimmed_groups = remove_reserved(world, trimmed_groups, connected_edges, player) groups = reorganize_groups(world, trimmed_groups, player) - #all shuffling occurs here - random.shuffle(groups) - for (forward_edge_sets, back_edge_sets) in groups: - assert len(forward_edge_sets) == len(back_edge_sets) - random.shuffle(back_edge_sets) - for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets): - assert len(forward_set) == len(back_set) - for (forward_edge, back_edge) in zip(forward_set, back_set): - connect_two_way(world, forward_edge, back_edge, player) - connected_edges.append(forward_edge) - connected_edges.append(back_edge) - if world.owShuffle[player] == 'parallel': - if forward_edge in parallel_links.keys() or forward_edge in parallel_links.inverse.keys(): - try: - parallel_forward_edge = parallel_links[forward_edge] if forward_edge in parallel_links.keys() else parallel_links.inverse[forward_edge][0] - parallel_back_edge = parallel_links[back_edge] if back_edge in parallel_links.keys() else parallel_links.inverse[back_edge][0] - connect_two_way(world, parallel_forward_edge, parallel_back_edge, player) - connected_edges.append(parallel_forward_edge) - connected_edges.append(parallel_back_edge) - except KeyError: - # TODO: Figure out why non-parallel edges are getting into parallel groups - raise KeyError('No parallel edge for edge %d' % back_edge) + # all layout shuffling occurs here + if world.owShuffle[player] != 'vanilla': + # layout shuffle + random.shuffle(groups) + for (forward_edge_sets, back_edge_sets) in groups: + assert len(forward_edge_sets) == len(back_edge_sets) + random.shuffle(forward_edge_sets) + random.shuffle(back_edge_sets) + if len(forward_edge_sets) > 0: + f = 0 + b = 0 + while f < len(forward_edge_sets) and b < len(back_edge_sets): + forward_set = forward_edge_sets[f] + back_set = back_edge_sets[b] + while forward_set[0] in connected_edges: + f += 1 + if f < len(forward_edge_sets): + forward_set = forward_edge_sets[f] + f += 1 + while back_set[0] in connected_edges: + b += 1 + if b < len(back_edge_sets): + back_set = back_edge_sets[b] + b += 1 + assert len(forward_set) == len(back_set) + for (forward_edge, back_edge) in zip(forward_set, back_set): + connect_two_way(world, forward_edge, back_edge, player, connected_edges) + else: + # vanilla/crossed shuffle + for (forward_edge_sets, back_edge_sets) in groups: + assert len(forward_edge_sets) == len(back_edge_sets) + for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets): + assert len(forward_set) == len(back_set) + swapped = random.randint(0, 1) + for (forward_edge, back_edge) in zip(forward_set, back_set): + if forward_edge not in connected_edges and back_edge not in connected_edges: + if swapped: + forward_edge = parallel_links[forward_edge] if forward_edge in parallel_links else parallel_links.inverse[forward_edge][0] + connect_two_way(world, forward_edge, back_edge, player, connected_edges) assert len(connected_edges) == len(default_connections) * 2, connected_edges - + # flute shuffle def connect_flutes(flute_destinations): for o in range(0, len(flute_destinations)): owslot = flute_destinations[o] regions = flute_data[owslot][0] - if (world.mode[player] == 'inverted') == (flute_data[owslot][1] in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (flute_data[owslot][1] in world.owswaps[player][0] and world.owMixed[player]): connect_simple(world, 'Flute Spot ' + str(o + 1), regions[0], player) else: connect_simple(world, 'Flute Spot ' + str(o + 1), regions[1], player) @@ -232,7 +281,7 @@ def link_overworld(world, player): new_ignored.add(exit.connected_region.name) getIgnored(exit.connected_region.name, base_owid, OWTileRegions[exit.connected_region.name]) - if (world.mode[player] == 'inverted') == (flute_data[owid][1] in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (flute_data[owid][1] in world.owswaps[player][0] and world.owMixed[player]): new_region = flute_data[owid][0][0] else: new_region = flute_data[owid][0][1] @@ -269,18 +318,15 @@ def link_overworld(world, player): world.owflutespots[player] = new_spots connect_flutes(new_spots) - 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]: - connect_two_way(world, edgename1, edgename2, player) - connected_edges.append(edgename1) - connected_edges.append(edgename2) + connect_two_way(world, edgename1, edgename2, player, connected_edges) def connect_simple(world, exitname, regionname, player): world.get_entrance(exitname, player).connect(world.get_region(regionname, player)) -def connect_two_way(world, edgename1, edgename2, player): +def connect_two_way(world, edgename1, edgename2, player, connected_edges=None): edge1 = world.get_entrance(edgename1, player) edge2 = world.get_entrance(edgename2, player) @@ -302,23 +348,41 @@ def connect_two_way(world, edgename1, edgename2, player): x.dest = y y.dest = x - if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla': + if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player]: world.spoiler.set_overworld(edgename2, edgename1, 'both', player) + if connected_edges is not None: + connected_edges.append(edgename1) + connected_edges.append(edgename2) + + # connecting parallel connections + if world.owShuffle[player] == 'parallel' or (world.owShuffle[player] == 'vanilla' and world.owCrossed[player]): + if (edgename1 in parallel_links.keys() or edgename1 in parallel_links.inverse.keys()): + try: + parallel_forward_edge = parallel_links[edgename1] if edgename1 in parallel_links.keys() else parallel_links.inverse[edgename1][0] + parallel_back_edge = parallel_links[edgename2] if edgename2 in parallel_links.keys() else parallel_links.inverse[edgename2][0] + if not (parallel_forward_edge in connected_edges) and not (parallel_back_edge in connected_edges): + connect_two_way(world, parallel_forward_edge, parallel_back_edge, player, connected_edges) + except KeyError: + # TODO: Figure out why non-parallel edges are getting into parallel groups + raise KeyError('No parallel edge for edge %s' % edgename2) + def remove_reserved(world, groupedlist, connected_edges, player): new_grouping = {} for group in groupedlist.keys(): new_grouping[group] = ([], []) for group in groupedlist.keys(): - (std, region, axis, terrain, parallel, count) = group + (_, region, _, _, _, _) = group (forward_edges, back_edges) = groupedlist[group] + # remove edges already connected (thru plando and other forced connections) for edge in connected_edges: forward_edges = list(list(filter((edge).__ne__, i)) for i in forward_edges) back_edges = list(list(filter((edge).__ne__, i)) for i in back_edges) - - if world.owShuffle[player] == 'parallel' and region == WorldType.Dark: + + # remove parallel edges from pool, since they get added during shuffle + if (not world.owCrossed[player] and world.owShuffle[player] == 'parallel') and region == WorldType.Dark: for edge in parallel_links: forward_edges = list(list(filter((parallel_links[edge]).__ne__, i)) for i in forward_edges) back_edges = list(list(filter((parallel_links[edge]).__ne__, i)) for i in back_edges) @@ -329,8 +393,6 @@ def remove_reserved(world, groupedlist, connected_edges, player): forward_edges = list(filter(([]).__ne__, forward_edges)) back_edges = list(filter(([]).__ne__, back_edges)) - #TODO: Remove edges set in connect_custom. The lists above can be left with invalid counts if connect_custom removes entries, they need to get put into their appropriate group - (exist_forward_edges, exist_back_edges) = new_grouping[group] exist_forward_edges.extend(forward_edges) exist_back_edges.extend(back_edges) @@ -340,11 +402,324 @@ def remove_reserved(world, groupedlist, connected_edges, player): return new_grouping def reorganize_groups(world, groups, player): + # predefined shuffle groups get reorganized here + # this restructures the candidate pool based on the chosen settings if world.owShuffle[player] == 'full': - #predefined shuffle groups get reorganized here + if world.owCrossed[player]: + if world.owKeepSimilar[player]: + if world.mode[player] == 'standard': + # tuple goes to (A,_,C,D,_,F) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (std, _, axis, terrain, _, count) = group + new_grouping[(std, axis, terrain, count)] = ([], []) + + for group in grouping.keys(): + (std, _, axis, terrain, _, count) = group + (forward_edges, back_edges) = grouping[group] + (exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, count)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(std, axis, terrain, count)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + # tuple goes to (_,_,C,D,_,F) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, _, axis, terrain, _, count) = group + new_grouping[(axis, terrain, count)] = ([], []) + + for group in grouping.keys(): + (_, _, axis, terrain, _, count) = group + (forward_edges, back_edges) = grouping[group] + (exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, count)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(axis, terrain, count)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + if world.mode[player] == 'standard': + # tuple goes to (A,_,C,D,_,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (std, _, axis, terrain, _, _) = group + new_grouping[(std, axis, terrain)] = ([], []) + + for group in grouping.keys(): + (std, _, axis, terrain, _, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(std, axis, terrain)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + # tuple goes to (_,_,C,D,_,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, _, axis, terrain, _, _) = group + new_grouping[(axis, terrain)] = ([], []) + + for group in grouping.keys(): + (_, _, axis, terrain, _, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(axis, terrain)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + if world.owKeepSimilar[player]: + if world.mode[player] == 'standard': + # tuple goes to (A,B,C,D,_,F) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (std, region, axis, terrain, _, count) = group + new_grouping[(std, region, axis, terrain, count)] = ([], []) + + for group in grouping.keys(): + (std, region, axis, terrain, _, count) = group + (forward_edges, back_edges) = grouping[group] + (exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain, count)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(std, region, axis, terrain, count)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + # tuple goes to (_,B,C,D,_,F) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, region, axis, terrain, _, count) = group + new_grouping[(region, axis, terrain, count)] = ([], []) + + for group in grouping.keys(): + (_, region, axis, terrain, _, count) = group + (forward_edges, back_edges) = grouping[group] + (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, count)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(region, axis, terrain, count)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + if world.mode[player] == 'standard': + # tuple goes to (A,B,C,D,_,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (std, region, axis, terrain, _, _) = group + new_grouping[(std, region, axis, terrain)] = ([], []) + + for group in grouping.keys(): + (std, region, axis, terrain, _, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(std, region, axis, terrain)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + # tuple goes to (_,B,C,D,_,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, region, axis, terrain, _, _) = group + new_grouping[(region, axis, terrain)] = ([], []) + + for group in grouping.keys(): + (_, region, axis, terrain, _, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(region, axis, terrain)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + elif world.owShuffle[player] == 'parallel': + if world.owCrossed[player]: + if world.owKeepSimilar[player]: + if world.mode[player] == 'standard': + # tuple goes to (A,_,C,D,E,F) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (std, _, axis, terrain, parallel, count) = group + new_grouping[(std, axis, terrain, parallel, count)] = ([], []) + + for group in grouping.keys(): + (std, _, axis, terrain, parallel, count) = group + (forward_edges, back_edges) = grouping[group] + (exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, parallel, count)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(std, axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + # tuple goes to (_,_,C,D,E,F) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, _, axis, terrain, parallel, count) = group + new_grouping[(axis, terrain, parallel, count)] = ([], []) + + for group in grouping.keys(): + (_, _, axis, terrain, parallel, count) = group + (forward_edges, back_edges) = grouping[group] + (exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, parallel, count)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + if world.mode[player] == 'standard': + # tuple goes to (A,_,C,D,E,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (std, _, axis, terrain, parallel, _) = group + new_grouping[(std, axis, terrain, parallel)] = ([], []) + + for group in grouping.keys(): + (std, _, axis, terrain, parallel, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, parallel)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(std, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + # tuple goes to (_,_,C,D,E,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, _, axis, terrain, parallel, _) = group + new_grouping[(axis, terrain, parallel)] = ([], []) + + for group in grouping.keys(): + (_, _, axis, terrain, parallel, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, parallel)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + if world.owKeepSimilar[player]: + if world.mode[player] == 'standard': + # tuple stays (A,B,C,D,E,F) + for grouping in (groups,): + return list(grouping.values()) + else: + # tuple goes to (_,B,C,D,E,F) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, region, axis, terrain, parallel, count) = group + new_grouping[(region, axis, terrain, parallel, count)] = ([], []) + + for group in grouping.keys(): + (_, region, axis, terrain, parallel, count) = group + (forward_edges, back_edges) = grouping[group] + (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, parallel, count)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(region, axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + if world.mode[player] == 'standard': + # tuple goes to (A,B,C,D,E,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (std, region, axis, terrain, parallel, _) = group + new_grouping[(std, region, axis, terrain, parallel)] = ([], []) + + for group in grouping.keys(): + (std, region, axis, terrain, parallel, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain, parallel)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(std, region, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + else: + # tuple goes to (_,B,C,D,E,_) + for grouping in (groups,): + new_grouping = {} + + for group in grouping.keys(): + (_, region, axis, terrain, parallel, _) = group + new_grouping[(region, axis, terrain, parallel)] = ([], []) + + for group in grouping.keys(): + (_, region, axis, terrain, parallel, _) = group + (forward_edges, back_edges) = grouping[group] + forward_edges = [[i] for l in forward_edges for i in l] + back_edges = [[i] for l in back_edges for i in l] + + (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, parallel)] + exist_forward_edges.extend(forward_edges) + exist_back_edges.extend(back_edges) + new_grouping[(region, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) + + return list(new_grouping.values()) + elif world.owShuffle[player] == 'vanilla' and world.owCrossed[player]: if world.owKeepSimilar[player]: if world.mode[player] == 'standard': - #tuple stays (A,B,C,D,_,F) + # tuple goes to (A,B,C,D,_,F) for grouping in (groups,): new_grouping = {} @@ -362,7 +737,7 @@ def reorganize_groups(world, groups, player): return list(new_grouping.values()) else: - #tuple goes to (_,B,C,D,_,F) + # tuple goes to (_,B,C,D,_,F) for grouping in (groups,): new_grouping = {} @@ -381,7 +756,7 @@ def reorganize_groups(world, groups, player): return list(new_grouping.values()) else: if world.mode[player] == 'standard': - #tuple stays (A,B,C,D,_,_) + # tuple goes to (A,B,C,D,_,_) for grouping in (groups,): new_grouping = {} @@ -402,7 +777,7 @@ def reorganize_groups(world, groups, player): return list(new_grouping.values()) else: - #tuple goes to (_,B,C,D,_,_) + # tuple goes to (_,B,C,D,_,_) for grouping in (groups,): new_grouping = {} @@ -421,82 +796,14 @@ def reorganize_groups(world, groups, player): exist_back_edges.extend(back_edges) new_grouping[(region, axis, terrain)] = (exist_forward_edges, exist_back_edges) - return list(new_grouping.values()) - elif world.owShuffle[player] == 'parallel': - #predefined shuffle groups get reorganized here - if world.owKeepSimilar[player]: - if world.mode[player] == 'standard': - #tuple stays (A,B,C,D,E,F) - for grouping in (groups,): - return list(grouping.values()) - else: - #tuple goes to (_,B,C,D,E,F) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, count) = group - new_grouping[(region, axis, terrain, parallel, count)] = ([], []) - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, count) = group - (forward_edges, back_edges) = grouping[group] - (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, parallel, count)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(region, axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - else: - if world.mode[player] == 'standard': - #tuple stays (A,B,C,D,E,_) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (std, region, axis, terrain, parallel, _) = group - new_grouping[(std, region, axis, terrain, parallel)] = ([], []) - - for group in grouping.keys(): - (std, region, axis, terrain, parallel, _) = group - (forward_edges, back_edges) = grouping[group] - forward_edges = [[i] for l in forward_edges for i in l] - back_edges = [[i] for l in back_edges for i in l] - - (exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain, parallel)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(std, region, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) - - return list(new_grouping.values()) - else: - #tuple goes to (_,B,C,D,E,_) - for grouping in (groups,): - new_grouping = {} - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, _) = group - new_grouping[(region, axis, terrain, parallel)] = ([], []) - - for group in grouping.keys(): - (_, region, axis, terrain, parallel, _) = group - (forward_edges, back_edges) = grouping[group] - forward_edges = [[i] for l in forward_edges for i in l] - back_edges = [[i] for l in back_edges for i in l] - - (exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, parallel)] - exist_forward_edges.extend(forward_edges) - exist_back_edges.extend(back_edges) - new_grouping[(region, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges) - return list(new_grouping.values()) else: raise NotImplementedError('Shuffling not supported yet') def create_flute_exits(world, player): for region in (r for r in world.regions if r.player == player and r.terrain == Terrain.Land and r.name not in ['Zoras Domain', 'Master Sword Meadow', 'Hobo Bridge']): - if (world.owSwap[player] != 'mixed' and region.type == RegionType.LightWorld) \ - or (world.owSwap[player] == 'mixed' and region.type in [RegionType.LightWorld, RegionType.DarkWorld] \ + if (not world.owMixed[player] and region.type == RegionType.LightWorld) \ + or (world.owMixed[player] and region.type in [RegionType.LightWorld, RegionType.DarkWorld] \ and (region.name not in world.owswaps[player][1] or region.name in world.owswaps[player][2])): exitname = 'Flute From ' + region.name exit = Entrance(region.player, exitname, region) @@ -506,7 +813,7 @@ def create_flute_exits(world, player): world.initialize_regions() def update_world_regions(world, player): - if world.owSwap[player] == 'mixed': + if world.owMixed[player]: for name in world.owswaps[player][1]: world.get_region(name, player).type = RegionType.DarkWorld for name in world.owswaps[player][2]: @@ -1110,9 +1417,8 @@ ow_connections = { } parallelsimilar_connections = [('Maze Race ES', 'Kakariko Suburb WS'), - ('Dig Game EC', 'Frog WC'), - ('Dig Game ES', 'Frog WS') - ] + ('Dig Game EC', 'Frog WC') + ] # non shuffled overworld default_connections = [#('Lost Woods NW', 'Master Sword Meadow SC'), diff --git a/README.md b/README.md index 3ecdb94f..7bf01573 100644 --- a/README.md +++ b/README.md @@ -66,19 +66,9 @@ OW Transitions are shuffled, but both worlds will have a matching layout. OW Transitions are shuffled within each world separately. -## Overworld Tile Swap (--ow_swap) +## Crossed (--ow_crossed) -### Vanilla - -OW tiles remain in their original worlds. - -### Mixed - -OW tiles are randomly chosen to become a part of the opposite world - -### Crossed - -OW tiles remain in their original world, but transitions can now be travel cross-world. +This allows OW connections to be shuffled cross-world. ## Visual Representation of Main OW Shuffle Settings @@ -88,6 +78,10 @@ OW tiles remain in their original world, but transitions can now be travel cross This keeps similar edge transitions together. ie. The 2 west edges of Potion Shop will be paired to another set of two similar edges +## Mixed Overworld (--ow_mixed) + +OW tiles are randomly chosen to become a part of the opposite world + ## Flute Shuffle (--ow_fluteshuffle) When enabled, new flute spots are generated and gives the player the option to cancel out of the flute menu by pressing X. @@ -120,10 +114,10 @@ Show the help message and exit. For specifying the overworld layout shuffle you want as above. (default: vanilla) ``` ---ow_swap +--ow_crossed ``` -For specifying the overworld tile swap you want as above. (default: vanilla) +This allows cross-world connections on the overworld ``` --ow_keepsimilar @@ -131,6 +125,12 @@ For specifying the overworld tile swap you want as above. (default: vanilla) This keeps similar edge transitions paired together with other pairs of transitions +``` +--ow_mixed +``` + +This gives each OW tile a random chance to be swapped to the opposite world + ``` --ow_fluteshuffle ``` diff --git a/Rom.py b/Rom.py index 217582c1..26911e9f 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'cc8fc59caa0bbe6d26ac64b9d2893709' +RANDOMIZERBASEHASH = '99f3f57ab2c9449172cade4927a462d6' class JsonRom(object): @@ -612,71 +612,20 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if world.pot_contents[player]: write_pots_to_rom(rom, world.pot_contents[player]) - # patch overworld edges - inverted_buffer = [0] * 0x82 - if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla': - owMode = 0 - if world.owShuffle[player] == 'parallel': - owMode = 1 - elif world.owShuffle[player] == 'full': - owMode = 2 - - if world.owSwap[player] == 'mixed': - owMode |= 0x100 - world.fix_fake_world[player] = True - elif world.owSwap[player] == 'crossed': - owMode |= 0x200 - world.fix_fake_world[player] = True - - write_int16(rom, 0x150002, owMode) - - owFlags = 0 - if world.owKeepSimilar[player]: - owFlags |= 0x1 - if world.owFluteShuffle[player] != 'vanilla': - owFlags |= 0x100 - - write_int16(rom, 0x150004, owFlags) - - rom.write_byte(0x18004C, 0x01) # patch for allowing Frogsmith to enter multi-entrance caves - - # patches map data specific for OW Shuffle - #inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM - inverted_buffer[0x1A] = inverted_buffer[0x1A] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x1B] = inverted_buffer[0x1B] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x22] = inverted_buffer[0x22] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x3F] = inverted_buffer[0x3F] | 0x2 # added C to terrain - #inverted_buffer[0x43] = inverted_buffer[0x43] | 0x2 # convenient portal on WDDM - inverted_buffer[0x5A] = inverted_buffer[0x5A] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x5B] = inverted_buffer[0x5B] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x62] = inverted_buffer[0x62] | 0x2 # rocks added to prevent OWG hardlock - inverted_buffer[0x7F] = inverted_buffer[0x7F] | 0x2 # added C to terrain - - if world.owSwap[player] == 'mixed': - for b in world.owswaps[player][0]: - # load inverted maps - inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2) - - # set world flag - rom.write_byte(0x153A00 + b, 0x00 if b >= 0x40 else 0x40) - - for edge in world.owedges: - if edge.dest is not None and isinstance(edge.dest, OWEdge) and edge.player == player: - write_int16(rom, edge.getAddress() + 0x0a, edge.vramLoc) - write_int16(rom, edge.getAddress() + 0x0e, edge.getTarget()) - # patch flute spots + owFlags = 0 if world.owFluteShuffle[player] == 'vanilla': flute_spots = default_flute_connections else: flute_spots = world.owflutespots[player] + owFlags |= 0x100 for o in range(0, len(flute_spots)): owslot = flute_spots[o] offset = 0 data = flute_data[owslot] - if (world.mode[player] == 'inverted') != (data[1] in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (data[1] in world.owswaps[player][0] and world.owMixed[player]): offset = 0x40 write_int16(rom, snes_to_pc(0x02E849 + (o * 2)), data[1] + offset) # owid @@ -695,7 +644,55 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(snes_to_pc(0x0AB78B + o), data[12] // 0x100) # X high byte rom.write_byte(snes_to_pc(0x0AB793 + o), data[11] & 0xff) # Y low byte rom.write_byte(snes_to_pc(0x0AB79B + o), data[11] // 0x100) # Y high byte + + # patch overworld edges + inverted_buffer = [0] * 0x82 + if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] or world.owMixed[player]: + owMode = 0 + if world.owShuffle[player] == 'parallel': + owMode = 1 + elif world.owShuffle[player] == 'full': + owMode = 2 + + if world.owKeepSimilar[player] and (world.owShuffle[player] != 'vanilla' or world.owCrossed[player]): + owMode |= 0x100 + if world.owCrossed[player]: + owMode |= 0x200 + world.fix_fake_world[player] = True + if world.owMixed[player]: + owMode |= 0x400 + write_int16(rom, 0x150002, owMode) + + write_int16(rom, 0x150004, owFlags) + + rom.write_byte(0x18004C, 0x01) # patch for allowing Frogsmith to enter multi-entrance caves + + # patches map data specific for OW Shuffle + #inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM + inverted_buffer[0x1A] = inverted_buffer[0x1A] | 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x1B] = inverted_buffer[0x1B] | 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x22] = inverted_buffer[0x22] | 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x3F] = inverted_buffer[0x3F] | 0x2 # added C to terrain + #inverted_buffer[0x43] = inverted_buffer[0x43] | 0x2 # convenient portal on WDDM + inverted_buffer[0x5A] = inverted_buffer[0x5A] | 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x5B] = inverted_buffer[0x5B] | 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x62] = inverted_buffer[0x62] | 0x2 # rocks added to prevent OWG hardlock + inverted_buffer[0x7F] = inverted_buffer[0x7F] | 0x2 # added C to terrain + + if world.owMixed[player]: + for b in world.owswaps[player][0]: + # load inverted maps + inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2) + + # set world flag + rom.write_byte(0x153A00 + b, 0x00 if b >= 0x40 else 0x40) + + for edge in world.owedges: + if edge.dest is not None and isinstance(edge.dest, OWEdge) and edge.player == player: + write_int16(rom, edge.getAddress() + 0x0a, edge.vramLoc) + write_int16(rom, edge.getAddress() + 0x0e, edge.getTarget()) + # patch entrance/exits/holes for region in world.regions: @@ -2177,7 +2174,7 @@ def write_strings(rom, world, player, team): if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']: entrances_to_hint.update(InsanityEntrances) if world.shuffle_ganon: - if world.mode[player] == 'inverted' != (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if world.mode[player] == 'inverted' != (0x1b in world.owswaps[player][0] and world.owMixed[player]): entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'}) else: entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'}) @@ -2247,7 +2244,7 @@ def write_strings(rom, world, player, team): tt[hint_locations.pop(0)] = this_hint # Adding a guaranteed hint for the Flute in overworld shuffle. - if world.owShuffle[player] in ['parallel','full']: + if world.owShuffle[player] != 'vanilla' or world.owMixed[player]: this_location = world.find_items_not_key_only('Ocarina', player) if this_location: this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.' @@ -2255,7 +2252,7 @@ def write_strings(rom, world, player, team): # Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well. items_to_hint = RelevantItems.copy() - if world.owShuffle[player] in ['parallel','full']: + if world.owShuffle[player] != 'vanilla' or world.owMixed[player]: items_to_hint.remove('Ocarina') if world.keyshuffle[player]: items_to_hint.extend(SmallKeys) @@ -2264,7 +2261,7 @@ def write_strings(rom, world, player, team): random.shuffle(items_to_hint) hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8 hint_count += 2 if world.doorShuffle[player] == 'crossed' else 0 - hint_count += 1 if world.owShuffle[player] in ['parallel', 'full'] else 0 + hint_count += 1 if world.owShuffle[player] != 'vanilla' or world.owMixed[player] else 0 while hint_count > 0: this_item = items_to_hint.pop(0) this_location = world.find_items_not_key_only(this_item, player) @@ -2475,7 +2472,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer): 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.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owMixed[player]): if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple']: rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) # mountain cave starts on OW @@ -2498,18 +2495,18 @@ def set_inverted_mode(world, player, rom, inverted_buffer): rom.write_byte(snes_to_pc(0x02D9B8), 0x12) rom.write_bytes(0x180247, [0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00]) #indicates the overworld door being used for the single entrance spawn point - if (world.mode[player] == 'inverted') != (0x05 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x05 in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) # add warp under rock - if (world.mode[player] == 'inverted') != (0x07 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x07 in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x1BC387), [0xDD, 0xD1]) # add warps under rocks rom.write_bytes(snes_to_pc(0x1BD1DD), [0xA4, 0x06, 0x82, 0x9E, 0x06, 0x82, 0xFF, 0xFF]) # add warps under rocks rom.write_byte(0x180089, 0x01) # open TR after exit rom.write_bytes(0x0086E, [0x5C, 0x00, 0xA0, 0xA1]) # TR tail if world.shuffle[player] in ['vanilla']: world.fix_trock_doors[player] = True - if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warp under rock - if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owMixed[player]): write_int16(rom, 0x15AEE + 2 * 0x06, 0x0020) # post aga hyrule castle spawn rom.write_byte(0x15B8C + 0x06, 0x1B) write_int16(rom, 0x15BDB + 2 * 0x06, 0x00AE) @@ -2597,21 +2594,21 @@ def set_inverted_mode(world, player, rom, inverted_buffer): write_int16(rom, 0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door write_int16(rom, 0xDBA71 + 2 * 0x35, 0x011C) - if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) # frog pickup on contact - if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owMixed[player]): if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: rom.write_byte(0x15B8C, 0x6C) # exit links at bomb shop area rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house rom.write_byte(0xDBB73 + 0x52, 0x01) - if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) # add warp under rock - if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) # add warp under rock - if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x1BC3DF), [0xD8, 0xD1]) # add warp under rock rom.write_bytes(snes_to_pc(0x1BD1D8), [0xA8, 0x02, 0x82, 0xFF, 0xFF]) # add warp under rock - if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owMixed[player]): rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock # apply inverted map changes diff --git a/Rules.py b/Rules.py index 3cd00a14..3e50c6d4 100644 --- a/Rules.py +++ b/Rules.py @@ -844,7 +844,7 @@ def ow_rules(world, player): else: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) - if (world.mode[player] == 'inverted') == (0x00 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x00 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Lost Woods East Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lost Woods Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lost Woods Pedestal Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -859,12 +859,12 @@ def ow_rules(world, player): set_rule(world.get_entrance('Skull Woods Forgotten (Middle) Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Skull Woods Front Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x02 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x02 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Lumberjack Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dark Lumberjack Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('West Death Mountain (Top) Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Spectacle Rock Mirror Spot', player), lambda state: state.has_Mirror(player)) else: @@ -872,7 +872,7 @@ def ow_rules(world, player): set_rule(world.get_entrance('Bubble Boy Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('West Dark Death Mountain (Bottom) Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('East Death Mountain (Top West) Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('East Death Mountain (Top East) Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Mimic Cave Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -894,7 +894,7 @@ def ow_rules(world, player): set_rule(world.get_entrance('Dark Floating Island Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dark Death Mountain Teleporter (East)', player), lambda state: state.can_lift_heavy_rocks(player)) - if (world.mode[player] == 'inverted') == (0x07 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x07 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('TR Pegs Area Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('TR Pegs Teleporter', player), lambda state: state.has('Hammer', player)) else: @@ -902,7 +902,7 @@ def ow_rules(world, player): set_rule(world.get_entrance('Turtle Rock Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) - if (world.mode[player] == 'inverted') == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x0a in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Mountain Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Mountain Entry Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Mountain Entry Entrance Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -911,12 +911,12 @@ def ow_rules(world, player): set_rule(world.get_entrance('Bumper Cave Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x0f in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x0f in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Zora Waterfall Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Catfish Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x10 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x10 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Lost Woods Pass West Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lost Woods Pass East Top Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lost Woods Pass East Bottom Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -929,24 +929,24 @@ def ow_rules(world, player): set_rule(world.get_entrance('West Dark World Teleporter (Hammer)', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) set_rule(world.get_entrance('West Dark World Teleporter (Rock)', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) # bunny cannot lift bushes - if (world.mode[player] == 'inverted') == (0x11 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x11 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Kakariko Fortune Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Outcast Fortune Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x12 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x12 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Kakariko Pond Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Outcast Pond Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x13 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x13 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Sanctuary Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Bonk Rock Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dark Chapel Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dark Chapel Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x14 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x14 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Graveyard Ledge Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player)) set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player)) else: @@ -956,28 +956,28 @@ def ow_rules(world, player): set_rule(world.get_entrance('Dark Graveyard Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dark Graveyard Grave Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x15 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x15 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('River Bend Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('River Bend East Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Qirn Jump Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Qirn Jump East Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x16 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x16 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Potion Shop Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Potion Shop Northeast Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dark Witch Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dark Witch Northeast Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x17 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x17 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Zora Approach Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Zora Approach Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Catfish Approach Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Catfish Approach Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x18 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x18 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Kakariko Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Kakariko Grass Mirror Spot', player), lambda state: state.has_Mirror(player)) else: @@ -985,13 +985,13 @@ def ow_rules(world, player): set_rule(world.get_entrance('Village of Outcasts Southwest Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Hammer House Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x1a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x1a in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Forgotton Forest Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Forgotton Forest Fence Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Shield Shop Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x1b in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: False) set_rule(world.get_entrance('Inverted Pyramid Entrance', player), lambda state: False) set_rule(world.get_entrance('Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player) or world.open_pyramid[player]) @@ -1017,7 +1017,7 @@ def ow_rules(world, player): set_rule(world.get_entrance('Pyramid Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Post Aga Inverted Teleporter', player), lambda state: state.has('Beat Agahnim 1', player)) - if (world.mode[player] == 'inverted') == (0x1d in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x1d in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Wooden Bridge Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Wooden Bridge Northeast Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Wooden Bridge West Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -1026,12 +1026,12 @@ def ow_rules(world, player): set_rule(world.get_entrance('Broken Bridge East Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Broken Bridge Northeast Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x1e in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x1e in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Eastern Palace Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Palace of Darkness Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x22 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x22 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Blacksmith Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Blacksmith Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Bat Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -1039,19 +1039,19 @@ def ow_rules(world, player): set_rule(world.get_entrance('Hammer Pegs Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Hammer Pegs Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x25 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x25 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Sand Dunes Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dark Dunes Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x28 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x28 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Maze Race Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Maze Race Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dig Game Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dig Game Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x29 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x29 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Kakariko Suburb Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Kakariko Suburb South Mirror Spot', player), lambda state: state.has_Mirror(player)) else: @@ -1059,24 +1059,24 @@ def ow_rules(world, player): set_rule(world.get_entrance('Frog Prison Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Archery Game Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x2a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x2a in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Flute Boy Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Flute Boy Pass Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Stumpy Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Stumpy Pass Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x2b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x2b in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Central Bonk Rocks Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dark Bonk Rocks Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x2c in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x2c in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Links House Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Big Bomb Shop Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x2d in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x2d in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Stone Bridge Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Stone Bridge South Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Hobo Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player)) @@ -1085,19 +1085,19 @@ def ow_rules(world, player): set_rule(world.get_entrance('Hammer Bridge South Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dark Hobo Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player)) - if (world.mode[player] == 'inverted') == (0x2e in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x2e in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Tree Line Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dark Tree Line Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x2f in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x2f in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Eastern Nook Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer else: set_rule(world.get_entrance('Darkness Nook Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) - if (world.mode[player] == 'inverted') == (0x30 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x30 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Checkerboard Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Desert Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Desert Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -1112,14 +1112,14 @@ def ow_rules(world, player): set_rule(world.get_entrance('Misery Mire Main Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Misery Mire Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - if (world.mode[player] == 'inverted') == (0x32 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x32 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Cave 45 Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Flute Boy Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Stumpy Approach Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Stumpy Bush Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x33 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x33 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('C Whirlpool Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('C Whirlpool Outer Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer @@ -1130,12 +1130,12 @@ def ow_rules(world, player): set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) set_rule(world.get_entrance('South Teleporter Cliff Ledge Drop', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) - if (world.mode[player] == 'inverted') == (0x34 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x34 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Statues Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Hype Cave Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x35 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x35 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Lake Hylia Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lake Hylia Northeast Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Lake Hylia Island Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player)) @@ -1152,12 +1152,12 @@ def ow_rules(world, player): set_rule(world.get_entrance('Ice Palace Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Ice Palace Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - if (world.mode[player] == 'inverted') == (0x37 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x37 in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Ice Cave Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Shopping Mall Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x3a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x3a in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Desert Pass Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Desert Pass Mirror Spot', player), lambda state: state.has_Mirror(player)) else: @@ -1165,17 +1165,17 @@ def ow_rules(world, player): set_rule(world.get_entrance('Swamp Nook Southeast Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Swamp Nook Pegs Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x3b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x3b in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Dam Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Swamp Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x3c in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x3c in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('South Pass Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Dark South Pass Mirror Spot', player), lambda state: state.has_Mirror(player)) - if (world.mode[player] == 'inverted') == (0x3f in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if (world.mode[player] == 'inverted') == (0x3f in world.owswaps[player][0] and world.owMixed[player]): set_rule(world.get_entrance('Octoballoon Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Bomber Corner Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -2050,7 +2050,7 @@ def set_inverted_big_bomb_rules(world, player): else: raise Exception('No logic found for routing from %s to the pyramid.' % bombshop_entrance.name) - if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla': + if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player]: add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: False) #temp disable progression until routing to Pyramid get be guaranteed diff --git a/asm/owrando.asm b/asm/owrando.asm index 8cd7bcf1..98be93c0 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -341,7 +341,7 @@ OWNewDestination: sep #$30 : lda OWOppSlotOffset,y : !add $04 : asl : and #$7f : sta $700 ; crossed OW shuffle - lda.l OWMode+1 : and #$ff : cmp #$02 : bne .return + lda.l OWMode+1 : and.b #$02 : beq .return ldx $05 : lda.l OWTileWorldAssoc,x : sta.l $7ef3ca ; change world ; toggle bunny mode diff --git a/data/base2current.bps b/data/base2current.bps index 6a4a34e265eac6877f69ae5e8469811901ebcee0..24256d1451f20acfbeb2557b8a911cb00c387721 100644 GIT binary patch delta 111 zcmV-#0FeLh&Is(z2(U>31cfhp(6dbesPzq(U6GXr&;ektZJSR$vjg`#4*><2KL!CF z0s`=tVFm#g5(*Kftw})1K+qATUC9^l7Na$niUt8113%y%x2FaH&QJjtmv&bHGyyrc Rt5*TM&j|HQVkzZ8KJ-`mDcJx3 delta 134 zcmV;10D1rH&Is?$2(U>31m7+o(X&kfsPzt$d%>9i1JD6ruWg$z4YLCGI}ZU3mp%po z9t8i%0??OU1_2i@4iToUNkGX!&=I9w$rtbzqd9n{oq!9aUC9^F1(OGWnWY7Vio!5p oqaRB2H{c?-qy_=bPyrK{b5{X00X?^