From 6351ea634b2066dd740333403a62763c9d72c71f Mon Sep 17 00:00:00 2001 From: codemann8 Date: Tue, 27 Dec 2022 06:33:25 -0600 Subject: [PATCH] Fixed follower logic to use new dynamic mirror connections --- BaseClasses.py | 75 +++++++++++++++++++++------------------------ OverworldShuffle.py | 36 +++++++++++++++++----- 2 files changed, 64 insertions(+), 47 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 9a66a3d7..3e83b69f 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1562,7 +1562,7 @@ class Entrance(object): def can_reach(self, state): # Destination Pickup OW Only No Ledges Can S&Q Allow Mirror - multi_step_locations = { 'Pyramid Area': ('Big Bomb', True, True, False, True), + multi_step_locations = { 'Pyramid Crack': ('Big Bomb', True, True, False, True), 'Missing Smith': ('Frog', True, False, True, True), 'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True, True), 'Old Man Drop Off': ('Lost Old Man', True, False, False, False), @@ -1581,7 +1581,13 @@ class Entrance(object): while len(self.temp_path): exit = self.temp_path.pop(0) path = (exit.name, (exit.parent_region.name, path)) - item_name = self.connected_region.locations[0].item.name if self.connected_region.locations[0].item else 'Deliver Item' + item_name = 'Deliver Item' + if len(self.connected_region.locations) > 0 and self.connected_region.locations[0].item: + item_name = self.connected_region.locations[0].item.name + for loc in self.parent_region.locations: + if loc.event and not loc.real and loc.item.name.find('Farmable') < 0: + item_name = loc.item.name + break path = (item_name, (self.parent_region.name, path)) state.path[self] = (self.name, path) return True @@ -1626,45 +1632,39 @@ class Entrance(object): exits_to_traverse = list() found = False - if not found and allow_mirror_reentry and state.has('Magic Mirror', self.player): + if not found and allow_mirror_reentry and state.has_Mirror(self.player): # check for path using mirror portal re-entry at location of the follower pickup # this is checked first as this often the shortest path follower_region = start_region if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: follower_region = [i for i in start_region.entrances if i.parent_region.name != 'Menu'][0].parent_region if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld): - from OWEdges import OWTileRegions - from OverworldShuffle import ow_connections - owid = OWTileRegions[follower_region.name] - (mirror_map_orig, other_world) = ow_connections[owid % 0x40] - mirror_map = list(mirror_map_orig).copy() - mirror_map.extend(other_world) - mirror_exit = None - while len(mirror_map): + from OverworldShuffle import get_mirror_edges + mirror_map = get_mirror_edges(follower_region.world, follower_region, self.player) + while len(mirror_map) and not found: if mirror_map[0][1] == follower_region.name: mirror_exit = mirror_map[0][0] - break + mirror_region = follower_region.world.get_entrance(mirror_exit, self.player).parent_region + if mirror_exit and mirror_region: + if mirror_region.can_reach(state): + traverse_paths(mirror_region, self.parent_region) + break # no need to continue if there is no path from the mirror re-entry to dest mirror_map.pop(0) - if mirror_exit: - mirror_region = follower_region.world.get_entrance(mirror_exit, self.player).parent_region - if mirror_region.can_reach(state): - traverse_paths(mirror_region, self.parent_region) - if found: - path = state.path.get(mirror_region, (mirror_region.name, None)) - path = (follower_region.name, (mirror_exit, path)) - item_name = step_location.item.name if step_location.item else 'Pick Up Item' - if start_region.name != follower_region.name: - path = (start_region.name, (start_region.entrances[0].name, path)) - path = (f'{step_location.parent_region.name} Exit', ('Leave Item Area', (item_name, path))) - else: - path = (item_name, path) - path = ('Use Mirror Portal', (follower_region.name, path)) - while len(self.temp_path): - exit = self.temp_path.pop(0) - path = (exit.name, (exit.parent_region.name, path)) - item_name = self.connected_region.locations[0].item.name if self.connected_region.locations[0].item else 'Deliver Item' - path = (self.parent_region.name, path) - state.path[self] = (self.name, path) + if found: + path = state.path.get(mirror_region, (mirror_region.name, None)) + path = (follower_region.name, (mirror_exit, path)) + item_name = step_location.item.name if step_location.item else 'Pick Up Item' + if start_region.name != follower_region.name: + path = (start_region.name, (start_region.entrances[0].name, path)) + path = (f'{step_location.parent_region.name} Exit', ('Leave Item Area', (item_name, path))) + else: + path = (item_name, path) + path = ('Use Mirror Portal', (follower_region.name, path)) + while len(self.temp_path): + exit = self.temp_path.pop(0) + path = (exit.name, (exit.parent_region.name, path)) + path = (self.parent_region.name, path) + state.path[self] = (self.name, path) if not found: # check normal paths @@ -1675,7 +1675,7 @@ class Entrance(object): exit = self.parent_region.world.get_entrance('Links House S&Q', self.player) traverse_paths(exit.connected_region, self.parent_region, [exit]) - if not found and allow_mirror_reentry and state.has('Magic Mirror', self.player): + if not found and allow_mirror_reentry and state.has_Mirror(self.player): # check for paths using mirror portal re-entry at location of final destination # this is checked last as this is the most complicated/exhaustive check follower_region = start_region @@ -1686,14 +1686,9 @@ class Entrance(object): if dest_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: dest_region = start_region.entrances[0].parent_region if (dest_region.world.mode[self.player] != 'inverted') != (dest_region.type == RegionType.LightWorld): - from OWEdges import OWTileRegions - from OverworldShuffle import ow_connections - owid = OWTileRegions[dest_region.name] - (mirror_map_orig, other_world) = ow_connections.copy()[owid % 0x40] - mirror_map = list(mirror_map_orig).copy() - mirror_map.extend(other_world) - mirror_map = [(x, d) for (x, d) in mirror_map if x in [e.name for e in dest_region.exits]] # loop thru potential places to leave a mirror portal + from OverworldShuffle import get_mirror_edges + mirror_map = get_mirror_edges(dest_region.world, dest_region, self.player) while len(mirror_map) and not found: mirror_exit = dest_region.world.get_entrance(mirror_map[0][0], self.player) if mirror_exit.connected_region.type != dest_region.type: diff --git a/OverworldShuffle.py b/OverworldShuffle.py index d61e82bf..4def7446 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -837,22 +837,44 @@ def create_flute_exits(world, player): exit.connect(world.get_region('Flute Sky', player)) region.exits.append(exit) +def get_mirror_exit_name(from_region, to_region): + if from_region in mirror_connections and to_region in mirror_connections[from_region]: + if len(mirror_connections[from_region]) == 1: + return f'Mirror From {from_region}' + else: + return f'Mirror To {to_region}' + return None + +def get_mirror_edges(world, region, player): + mirror_exits = list() + if (world.mode[player] != 'inverted') == (region.type == RegionType.DarkWorld): + # get mirror edges leaving the region + if region.name in mirror_connections: + for dest_region_name in mirror_connections[region.name]: + mirror_exits.append(tuple([get_mirror_exit_name(region.name, dest_region_name), dest_region_name])) + else: + # get mirror edges leading into the region + owid = OWTileRegions[region.name] + for other_world_region_name in OWTileRegions.inverse[(owid + 0x40) % 0x80]: + if other_world_region_name in mirror_connections: + for dest_region_name in mirror_connections[other_world_region_name]: + if dest_region_name == region.name: + mirror_exits.append(tuple([get_mirror_exit_name(other_world_region_name, region.name), region.name])) + return mirror_exits + def create_mirror_exits(world, player): mirror_exits = set() for region in (r for r in world.regions if r.player == player and r.name not in ['Zoras Domain', 'Master Sword Meadow', 'Hobo Bridge']): if region.type == (RegionType.DarkWorld if world.mode[player] != 'inverted' else RegionType.LightWorld): if region.name in mirror_connections: for region_dest_name in mirror_connections[region.name]: - to_region = world.get_region(region_dest_name, player) - if len(mirror_connections[region.name]) == 1: - exitname = 'Mirror From ' + region.name - else: - exitname = 'Mirror To ' + region_dest_name + exitname = get_mirror_exit_name(region.name, region_dest_name) - assert(exitname not in mirror_exits, f'Mirror Exit with name already exists: {exitname}') + assert exitname not in mirror_exits, f'Mirror Exit with name already exists: {exitname}' exit = Entrance(region.player, exitname, region) exit.spot_type = 'Mirror' + to_region = world.get_region(region_dest_name, player) if region.terrain == Terrain.Water or to_region.terrain == Terrain.Water: exit.access_rule = lambda state: state.has('Flippers', player) and state.has_Pearl(player) and state.has_Mirror(player) else: @@ -862,7 +884,7 @@ def create_mirror_exits(world, player): mirror_exits.add(exitname) elif region.terrain == Terrain.Land: - logging.getLogger('').debug(f'Region has no mirror exit: {region.name}') + pass def create_dynamic_exits(world, player): create_flute_exits(world, player)