From f762d982a2e062a4809af8180cf698139a778a0d Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sat, 5 Nov 2022 18:26:52 -0500 Subject: [PATCH] Mixed Districts Initial Implementation --- OWEdges.py | 53 ++++++++++++++++ source/item/District.py | 137 +++++++++++++++++----------------------- 2 files changed, 111 insertions(+), 79 deletions(-) diff --git a/OWEdges.py b/OWEdges.py index 4bdd95bd..320f555b 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -1910,4 +1910,57 @@ OWExitTypes = { 'South Pass Mirror Spot', 'Octoballoon Mirror Spot' ] +} + +OWTileDistricts = { + 0x00: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x02: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x03: (None, None, ('Death Mountain', 'Dark Death Mountain')), + 0x05: (None, None, ('Death Mountain', 'Dark Death Mountain')), + 0x07: (None, None, ('Death Mountain', 'Dark Death Mountain')), + 0x0a: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x0f: (None, None, ('Eastern Hyrule', 'East Dark World')), + 0x10: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x11: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x12: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x13: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x14: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x15: (['River Bend Area', 'Qirn Jump Area'], # if region in this list + ('Northwest Hyrule', 'Northwest Dark World'), # use this district + ('Eastern Hyrule', 'East Dark World')), # else this district + 0x16: (None, None, ('Eastern Hyrule', 'East Dark World')), + 0x17: (None, None, ('Eastern Hyrule', 'East Dark World')), + 0x18: (None, None, ('Kakariko', 'Northwest Dark World')), + 0x1a: (None, None, ('Northwest Hyrule', 'Northwest Dark World')), + 0x1b: (None, None, ('Central Hyrule', 'East Dark World')), + 0x1d: (None, None, ('Eastern Hyrule', 'East Dark World')), + 0x1e: (None, None, ('Eastern Hyrule', 'East Dark World')), + 0x22: (None, None, ('Kakariko', 'Northwest Dark World')), + 0x25: (None, None, ('Eastern Hyrule', 'East Dark World')), + 0x28: (None, None, ('Kakariko', 'South Dark World')), + 0x29: (None, None, ('Kakariko', 'South Dark World')), + 0x2a: (None, None, ('Central Hyrule', 'South Dark World')), + 0x2b: (None, None, ('Central Hyrule', 'South Dark World')), + 0x2c: (None, None, ('Central Hyrule', 'South Dark World')), + 0x2d: (['Stone Bridge North Area', 'Hammer Bridge North Area'], + ('Eastern Hyrule', 'East Dark World'), + ('Lake Hylia', 'South Dark World')), + 0x2e: (['Tree Line Area', 'Dark Tree Line Area'], + ('Eastern Hyrule', 'East Dark World'), + ('Lake Hylia', 'South Dark World')), + 0x2f: (None, None, ('Eastern Hyrule', 'East Dark World')), + 0x30: (None, None, ('The Desert Area', 'The Mire Area')), + 0x32: (None, None, ('Central Hyrule', 'South Dark World')), + 0x33: (None, None, ('Central Hyrule', 'South Dark World')), + 0x34: (None, None, ('Central Hyrule', 'South Dark World')), + 0x35: (None, None, ('Lake Hylia', 'South Dark World')), + 0x37: (None, None, ('Lake Hylia', 'South Dark World')), + 0x3a: (None, None, ('The Desert Area', 'South Dark World')), + 0x3b: (None, None, ('Central Hyrule', 'South Dark World')), + 0x3c: (None, None, ('Central Hyrule', 'South Dark World')), + 0x3f: (None, None, ('Lake Hylia', 'South Dark World')), + 0x80: (['Master Sword Meadow'], + ('Northwest Hyrule', 'Northwest Dark World'), + ('Lake Hylia', 'South Dark World')), + 0x81: (None, None, ('Eastern Hyrule', 'East Dark World')) } \ No newline at end of file diff --git a/source/item/District.py b/source/item/District.py index 375138a3..d9e9e23e 100644 --- a/source/item/District.py +++ b/source/item/District.py @@ -2,15 +2,16 @@ from collections import deque from BaseClasses import CollectionState, RegionType from Dungeons import dungeon_table -from OWEdges import OWTileRegions +from OWEdges import OWTileRegions, OWTileDistricts class District(object): - def __init__(self, name, locations, entrances=None, dungeon=None): + def __init__(self, name, dungeon=None): self.name = name self.dungeon = dungeon - self.locations = locations - self.entrances = entrances if entrances else [] + self.regions = list() + self.locations = set() + self.entrances = list() self.sphere_one = False self.dungeons = set() @@ -25,86 +26,33 @@ def create_districts(world): def create_district_helper(world, player): districts = {} - kak_locations = {'Bottle Merchant', 'Kakariko Tavern', 'Maze Race'} - nw_lw_locations = {'Mushroom', 'Master Sword Pedestal'} - central_lw_locations = {'Sunken Treasure', 'Flute Spot'} - desert_locations = {'Purple Chest', 'Desert Ledge', 'Bombos Tablet'} - lake_locations = {'Hobo', 'Lake Hylia Island'} - east_lw_locations = {"Zora's Ledge", 'King Zora'} - lw_dm_locations = {'Old Man', 'Spectacle Rock', 'Ether Tablet', 'Floating Island'} - east_dw_locations = {'Pyramid', 'Catfish'} - south_dw_locations = {'Stumpy', 'Digging Game'} - voo_north_locations = {'Bumper Cave Ledge'} - ddm_locations = set() - - kak_entrances = ['Kakariko Well Cave', 'Bat Cave Cave', 'Elder House (East)', 'Elder House (West)', - 'Two Brothers House (East)', 'Two Brothers House (West)', 'Blinds Hideout', 'Chicken House', - 'Blacksmiths Hut', 'Sick Kids House', 'Snitch Lady (East)', 'Snitch Lady (West)', - 'Bush Covered House', 'Tavern (Front)', 'Light World Bomb Hut', 'Kakariko Shop', 'Library', - 'Kakariko Gamble Game', 'Kakariko Well Drop', 'Bat Cave Drop', 'Tavern North'] - nw_lw_entrances = ['North Fairy Cave', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Sanctuary', - 'Old Man Cave (West)', 'Death Mountain Return Cave (West)', 'Kings Grave', 'Lost Woods Gamble', - 'Fortune Teller (Light)', 'Bonk Rock Cave', 'Lumberjack House', 'North Fairy Cave Drop', - 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave', 'Graveyard Cave'] - central_lw_entrances = ['Links House', 'Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (West)', - 'Hyrule Castle Entrance (East)', 'Agahnims Tower', 'Hyrule Castle Secret Entrance Stairs', - 'Dam', 'Bonk Fairy (Light)', 'Light Hype Fairy', 'Hyrule Castle Secret Entrance Drop', - 'Cave 45'] - desert_entrances = ['Desert Palace Entrance (South)', 'Desert Palace Entrance (West)', - 'Desert Palace Entrance (North)', 'Desert Palace Entrance (East)', 'Desert Fairy', - 'Aginahs Cave', '50 Rupee Cave', 'Checkerboard Cave'] - lake_entrances = ['Capacity Upgrade', 'Mini Moldorm Cave', 'Good Bee Cave', '20 Rupee Cave', 'Ice Rod Cave', - 'Cave Shop (Lake Hylia)', 'Lake Hylia Fortune Teller'] - east_lw_entrances = ['Eastern Palace', 'Waterfall of Wishing', 'Lake Hylia Fairy', 'Sahasrahlas Hut', - 'Long Fairy Cave', 'Potion Shop'] - lw_dm_entrances = ['Tower of Hera', 'Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', - 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave', - 'Spectacle Rock Cave (Bottom)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', - 'Paradox Cave (Top)', 'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', - 'Spiral Cave', 'Spiral Cave (Bottom)', 'Hookshot Fairy', 'Mimic Cave'] - east_dw_entrances = ['Palace of Darkness', 'Pyramid Entrance', 'Pyramid Fairy', 'East Dark World Hint', - 'Palace of Darkness Hint', 'Dark Lake Hylia Fairy', 'Dark World Potion Shop', 'Pyramid Hole'] - south_dw_entrances = ['Ice Palace', 'Swamp Palace', 'Dark Lake Hylia Ledge Fairy', - 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', - 'Bonk Fairy (Dark)', 'Archery Game', 'Big Bomb Shop', 'Dark Lake Hylia Shop', ] - voo_north_entrances = ['Thieves Town', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', - 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section', - 'Bumper Cave (Bottom)', 'Bumper Cave (Top)', 'Brewery', 'C-Shaped House', 'Chest Game', - 'Dark World Hammer Peg Cave', 'Red Shield Shop', 'Dark Sanctuary Hint', - 'Fortune Teller (Dark)', 'Dark World Shop', 'Dark World Lumberjack Shop', - 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (East)', - 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] - mire_entrances = ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy'] - ddm_entrances = ['Turtle Rock', 'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)', - 'Turtle Rock Isolated Ledge Entrance', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)', - 'Hookshot Cave', 'Hookshot Cave Back Entrance', 'Ganons Tower', 'Spike Cave', - 'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Fairy'] - - if world.is_tile_swapped(0x1b, player): - east_dw_entrances.remove('Pyramid Entrance') - east_dw_entrances.remove('Pyramid Hole') - central_lw_entrances.append('Inverted Pyramid Entrance') - central_lw_entrances.append('Inverted Pyramid Hole') - - districts['Kakariko'] = District('Kakariko', kak_locations, entrances=kak_entrances) - districts['Northwest Hyrule'] = District('Northwest Hyrule', nw_lw_locations, entrances=nw_lw_entrances) - districts['Central Hyrule'] = District('Central Hyrule', central_lw_locations, entrances=central_lw_entrances) - districts['The Desert Area'] = District('Desert', desert_locations, entrances=desert_entrances) - districts['Lake Hylia'] = District('Lake Hylia', lake_locations, entrances=lake_entrances) - districts['Eastern Hyrule'] = District('Eastern Hyrule', east_lw_locations, entrances=east_lw_entrances) - districts['Death Mountain'] = District('Death Mountain', lw_dm_locations, entrances=lw_dm_entrances) - districts['East Dark World'] = District('East Dark World', east_dw_locations, entrances=east_dw_entrances) - districts['South Dark World'] = District('South Dark World', south_dw_locations, entrances=south_dw_entrances) - districts['Northwest Dark World'] = District('Northwest Dark World', voo_north_locations, - entrances=voo_north_entrances) - districts['The Mire Area'] = District('The Mire', set(), entrances=mire_entrances) - districts['Dark Death Mountain'] = District('Dark Death Mountain', ddm_locations, entrances=ddm_entrances) - districts.update({x: District(x, set(), dungeon=x) for x in dungeon_table.keys()}) + 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 resolve_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) state = CollectionState(world) state.sweep_for_events() @@ -113,18 +61,49 @@ def resolve_districts(world): 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) + + # 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) + 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 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) 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 not region: + 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: