Files
alttpr-python/source/item/District.py
2023-06-13 18:36:16 -05:00

148 lines
7.1 KiB
Python

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 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:
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'}