from collections import deque from BaseClasses import CollectionState, RegionType from Dungeons import dungeon_table from OWEdges import OWTileRegions, OWTileDistricts class District(object): def __init__(self, name, dungeon=None): self.name = name self.dungeon = dungeon self.regions = list() self.locations = set() self.entrances = list() self.sphere_one = False self.dungeons = set() self.access_points = set() def create_districts(world): world.districts = {} for p in range(1, world.players + 1): create_district_helper(world, p) def create_district_helper(world, player): districts = {} districts['Kakariko'] = District('Kakariko') districts['Northwest Hyrule'] = District('Northwest Hyrule') districts['Central Hyrule'] = District('Central Hyrule') districts['The Desert Area'] = District('Desert') districts['Lake Hylia'] = District('Lake Hylia') districts['Eastern Hyrule'] = District('Eastern Hyrule') districts['Death Mountain'] = District('Death Mountain') districts['East Dark World'] = District('East Dark World') districts['South Dark World'] = District('South Dark World') districts['Northwest Dark World'] = District('Northwest Dark World') districts['The Mire Area'] = District('The Mire') districts['Dark Death Mountain'] = District('Dark Death Mountain') districts.update({x: District(x, dungeon=x) for x in dungeon_table.keys()}) world.districts[player] = districts def init_districts(world): def exclude_area(world, owid, area, player): # area can be a region or entrancecurrently, could potentially be a problem later if name collision std_regions = ['Pyramid Ledge', 'Pyramid Hole', 'Pyramid Entrance'] inv_regions = ['Spiral Mimic Ledge Extend', 'Inverted Pyramid Hole', 'Inverted Pyramid Entrance'] if (area in inv_regions and not world.is_tile_swapped(owid, player)) \ or (area in std_regions and world.is_tile_swapped(owid, player)): return True return False create_districts(world) for player in range(1, world.players + 1): # adding regions to districts for owid, (alt_regions, alt_districts, default_districts) in OWTileDistricts.items(): idx = 0 if (world.mode[player] == 'inverted') == world.is_tile_swapped(owid, player) else 1 if owid in OWTileRegions.inverse.keys(): for region in OWTileRegions.inverse[owid]: if exclude_area(world, owid, region, player): continue if alt_regions and region in alt_regions: world.districts[player][alt_districts[idx]].regions.append(region) else: world.districts[player][default_districts[idx]].regions.append(region) if owid + 0x40 in OWTileRegions.inverse.keys(): for region in OWTileRegions.inverse[owid + 0x40]: if exclude_area(world, owid, region, player): continue if alt_regions and region in alt_regions: world.districts[player][alt_districts[(idx + 1) % 2]].regions.append(region) else: world.districts[player][default_districts[(idx + 1) % 2]].regions.append(region) # adding entrances to districts for name, district in world.districts[player].items(): if not district.dungeon: for region_name in district.regions: region = world.get_region(region_name, player) for exit in region.exits: if exit.spot_type == 'Entrance' and not exclude_area(world, OWTileRegions[region.name], exit.name, player): district.entrances.append(exit.name) def resolve_districts(world): state = CollectionState(world) state.sweep_for_events() for player in range(1, world.players + 1): # these are not static for OWR - but still important inaccessible = [r for r in inaccessible_regions_std if not world.is_tile_swapped(OWTileRegions[r], player)] inaccessible = inaccessible + [r for r in inaccessible_regions_inv if world.is_tile_swapped(OWTileRegions[r], player)] check_set = find_reachable_locations(state, player) for name, district in world.districts[player].items(): if district.dungeon: layout = world.dungeon_layouts[player][district.dungeon] district.locations.update([l.name for r in layout.master_sector.regions for l in r.locations if not l.item and l.real]) else: for region_name in district.regions: region = world.get_region(region_name, player) for location in region.locations: if not location.item and location.real: district.locations.add(location.name) for entrance in district.entrances: ent = world.get_entrance(entrance, player) queue = deque([ent.connected_region]) visited = set() while len(queue) > 0: region = queue.pop() if region is None: RuntimeError(f'No region connected to entrance: {ent.name} Likely a missing entry in OWExitTypes') visited.add(region) if region.type == RegionType.Cave: for location in region.locations: if not location.item and location.real: district.locations.add(location.name) for ext in region.exits: if ext.connected_region and ext.connected_region not in visited: queue.appendleft(ext.connected_region) elif region.type == RegionType.Dungeon and region.dungeon: district.dungeons.add(region.dungeon.name) elif region.name in inaccessible: district.access_points.add(region) district.sphere_one = len(check_set.intersection(district.locations)) > 0 def find_reachable_locations(state, player): check_set = set() for region in state.reachable_regions[player]: for location in region.locations: if location.can_reach(state) and not location.forced_item and location.real: check_set.add(location.name) return check_set inaccessible_regions_std = {'Desert Mouth', 'Bumper Cave Ledge', 'Skull Woods Forest (West)', 'Dark Death Mountain Ledge', 'Dark Death Mountain Isolated Ledge', 'Death Mountain Floating Island'} inaccessible_regions_inv = {'Desert Mouth', 'Maze Race Ledge', 'Desert Ledge', 'Desert Ledge Keep', 'Hyrule Castle Ledge', 'Mountain Pass Ledge'}