District hint work

This commit is contained in:
aerinon
2022-03-08 08:13:49 -07:00
parent 833a5d8320
commit 341a4f4631
5 changed files with 128 additions and 80 deletions

View File

@@ -31,7 +31,7 @@ from Utils import output_path, parse_player_names
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config
__version__ = '1.0.0.1-u' __version__ = '1.0.0.2-u'
from source.classes.BabelFish import BabelFish from source.classes.BabelFish import BabelFish

View File

@@ -67,7 +67,7 @@ In multiworld, the districts chosen apply to all players.
## New Hints ## New Hints
Based on the district algorithm above (whether it is enabled or not,) new hints can appear stating that a district or dungeon is considered a "foolish" choice. The means there are no advancement items in that district (or that the district was not chosen if the district restriction is used). Based on the district algorithm above (whether it is enabled or not,) new hints can appear about that district or dungeon. For each district and dungeon, it is evaluated whether it contains vital items and how many. If it has not any vital item, items then it moves onto useful items. Useful items are generally safeties or convenience items: shields, mails, half magic, bottles, medallions that aren't required, etc. If it contains none of those and is an overworld district, then it check for a couple more things. First, if dungeons are shuffled, it looks to see if any are in the district, if so, one of those dungeons is picked for the hint. Then, if connectors are shuffled, it checks to see if you can get to unique region through a connector in that district. If none of the above apply, the district or dungeon is considered completely foolish. At least two "foolish" districts are chosen and the rest are random.
### Overworld Map shows dungeon location ### Overworld Map shows dungeon location

View File

@@ -88,17 +88,17 @@ def create_regions(world, player):
create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']),
create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']), create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']),
create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']), create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)'], 'a race against time'),
create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'), create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'),
create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']), create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)'], 'the desert ledge'),
create_lw_region(player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']), create_lw_region(player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']),
create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']), create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']),
create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']), create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)'], 'a sandy vista'),
create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']), create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks'], 'the desert ledge'),
create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']), create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']),
create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'), create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']), create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']),
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']), create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop'], 'the castle rampart'),
create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']), create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']),
@@ -107,7 +107,7 @@ def create_regions(world, player):
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']), create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']), create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']), create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']), create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)'], 'a ledge in the foothills'),
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
@@ -173,10 +173,10 @@ def create_regions(world, player):
create_cave_region(player, 'Red Shield Shop', 'the rare shop', ['Red Shield Shop - Left', 'Red Shield Shop - Middle', 'Red Shield Shop - Right']), create_cave_region(player, 'Red Shield Shop', 'the rare shop', ['Red Shield Shop - Left', 'Red Shield Shop - Middle', 'Red Shield Shop - Right']),
create_cave_region(player, 'Dark Sanctuary Hint', 'a storyteller'), create_cave_region(player, 'Dark Sanctuary Hint', 'a storyteller'),
create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']), create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']), create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot'], 'a ledge with an item'),
create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)',
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']), 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']), create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section'], 'a deep, dark forest'),
create_dw_region(player, 'Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot', 'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot', create_dw_region(player, 'Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot', 'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot',
'Desert Palace Entrance (North) Mirror Spot', 'Dark Desert Hint', 'Dark Desert Fairy']), 'Desert Palace Entrance (North) Mirror Spot', 'Dark Desert Hint', 'Dark Desert Fairy']),
create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']), create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']),
@@ -184,8 +184,8 @@ def create_regions(world, player):
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']), create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)', create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)',
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']), 'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']), create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot'], 'a dark ledge'),
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']), create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance'], 'a dark vista'),
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']), create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']), create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']), create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
@@ -195,7 +195,7 @@ def create_regions(world, player):
create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back Exit']), create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back Exit']),
create_cave_region(player, 'Hookshot Cave (Middle)', 'a connector', None, ['Hookshot Cave Middle to Back', 'Hookshot Cave Middle to Front']), create_cave_region(player, 'Hookshot Cave (Middle)', 'a connector', None, ['Hookshot Cave Middle to Back', 'Hookshot Cave Middle to Front']),
create_dw_region(player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']), create_dw_region(player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot'], 'a dark island'),
create_lw_region(player, 'Death Mountain Floating Island (Light World)', ['Floating Island']), create_lw_region(player, 'Death Mountain Floating Island (Light World)', ['Floating Island']),
create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']), create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']),
create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave']), create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
@@ -887,12 +887,12 @@ def create_menu_region(player, name, locations=None, exits=None):
return _create_region(player, name, RegionType.Menu, 'Menu', locations, exits) return _create_region(player, name, RegionType.Menu, 'Menu', locations, exits)
def create_lw_region(player, name, locations=None, exits=None): def create_lw_region(player, name, locations=None, exits=None, hint='Light World'):
return _create_region(player, name, RegionType.LightWorld, 'Light World', locations, exits) return _create_region(player, name, RegionType.LightWorld, hint, locations, exits)
def create_dw_region(player, name, locations=None, exits=None): def create_dw_region(player, name, locations=None, exits=None, hint='Dark World'):
return _create_region(player, name, RegionType.DarkWorld, 'Dark World', locations, exits) return _create_region(player, name, RegionType.DarkWorld, hint, locations, exits)
def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None): def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None):

96
Rom.py
View File

@@ -2182,29 +2182,60 @@ def write_strings(rom, world, player, team):
else: else:
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
if world.algorithm != 'district': hint_candidates = []
hint_candidates = [] for name, district in world.districts[player].items():
for name, district in world.districts[player].items(): hint_type = 'foolish'
foolish = True choice_set = set()
for loc_name in district.locations: item_count, item_type = 0, 'useful'
location = world.get_location(loc_name, player) for loc_name in district.locations:
if location.item.advancement: location_item = world.get_location(loc_name, player).item
foolish = False if location_item.advancement:
break if 'Heart Container' in location_item.name:
if foolish: continue
hint_candidates.append(f'{name} is a foolish choice') itm_type = 'useful' if useful_item_for_hint(location_item, world) else 'vital'
foolish_choice_hints = min(len(hint_candidates), len(hint_locations)) hint_type = 'path'
for i in range(0, foolish_choice_hints): if item_type == itm_type:
tt[hint_locations.pop(0)] = hint_candidates.pop(0) choice_set.add(location_item)
if world.algorithm == 'district': item_count += 1
hint_candidates = [] elif itm_type == 'vital':
for name, district in world.districts[player].items(): item_type = 'vital'
if name not in world.item_pool_config.recorded_choices and not district.sphere_one: item_count = 1
hint_candidates.append(f'{name} is a foolish choice') choice_set.clear()
random.shuffle(hint_candidates) choice_set.add(location_item)
foolish_choice_hints = min(len(hint_candidates), len(hint_locations)) if hint_type == 'foolish':
for i in range(0, foolish_choice_hints): if district.dungeons and world.shuffle[player] != 'vanilla':
tt[hint_locations.pop(0)] = hint_candidates.pop(0) choice_set.update(district.dungeons)
hint_type = 'dungeon_path'
elif district.access_points and world.shuffle[player] not in ['vanilla', 'dungeonssimple',
'dungeonsfull']:
choice_set.update([x.hint_text for x in district.access_points])
hint_type = 'connector'
if hint_type == 'foolish':
hint_candidates.append((hint_type, f'{name} is a foolish choice'))
elif hint_type == 'dungeon_path':
choices = sorted(list(choice_set))
dungeon_choice = random.choice(choices) # prefer required dungeons...
hint_candidates.append((hint_type, f'{name} is on the path to {dungeon_choice}'))
elif hint_type == 'connector':
choices = sorted(list(choice_set))
access_point = random.choice(choices) # prefer required access...
hint_candidates.append((hint_type, f'{name} can reach {access_point}'))
elif hint_type == 'path':
if item_count == 1:
the_item = text_for_item(next(iter(choice_set)), world, player, team)
hint_candidates.append((hint_type, f'{name} conceals {the_item}'))
else:
hint_candidates.append((hint_type, f'{name} conceals {item_count} {item_type} items'))
district_hints = min(len(hint_candidates), len(hint_locations))
random.shuffle(hint_candidates)
hint_candidates.sort(key=lambda x: 1 if x[0] == 'foolish' else 0)
foolish_only = min(2, district_hints) # 2 foolish only
for i in range(0, foolish_only):
tt[hint_locations.pop(0)] = hint_candidates.pop(0)[1]
random.shuffle(hint_candidates)
district_hints -= foolish_only # the rest can be anything
for i in range(0, district_hints):
tt[hint_locations.pop(0)] = hint_candidates.pop(0)[1]
if len(hint_locations) > 0: if len(hint_locations) > 0:
# All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint # All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint
# isn't selected twice. # isn't selected twice.
@@ -2358,6 +2389,25 @@ def write_strings(rom, world, player, team):
rom.write_bytes(0x181500, data) rom.write_bytes(0x181500, data)
rom.write_bytes(0x76CC0, [byte for p in pointers for byte in [p & 0xFF, p >> 8 & 0xFF]]) rom.write_bytes(0x76CC0, [byte for p in pointers for byte in [p & 0xFF, p >> 8 & 0xFF]])
useful_item_names = {
'Mushroom', 'Shovel', 'Magic Powder', 'Progressive Shield', 'Progressive Armor', 'Blue Mail', 'Red Mail',
'Mirror Shield', 'Blue Boomerang', 'Red Boomerang', 'Bug Catching Net', 'Cane of Byrna', 'Cape',
'Magic Upgrade (1/2)', 'Magic Upgrade (1/4)', 'Ether', 'Quake'}
def useful_item_for_hint(item, world):
return 'Bottle' in item.name or (item.name in useful_item_names
and item.name not in world.required_medallions[item.player])
def text_for_item(item, world, player, team):
if item.player == player:
return item.hint_text
else:
return f'{item.hint_text} for {world.player_names[item.player][team]}'
def set_inverted_mode(world, player, rom): def set_inverted_mode(world, player, rom):
rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals
rom.write_byte(snes_to_pc(0x02B34D), 0xF0) rom.write_byte(snes_to_pc(0x02B34D), 0xF0)

View File

@@ -13,6 +13,9 @@ class District(object):
self.entrances = entrances if entrances else [] self.entrances = entrances if entrances else []
self.sphere_one = False self.sphere_one = False
self.dungeons = set()
self.access_points = set()
def create_districts(world): def create_districts(world):
world.districts = {} world.districts = {}
@@ -26,14 +29,14 @@ def create_district_helper(world, player):
kak_locations = {'Bottle Merchant', 'Kakariko Tavern', 'Maze Race'} kak_locations = {'Bottle Merchant', 'Kakariko Tavern', 'Maze Race'}
nw_lw_locations = {'Mushroom', 'Master Sword Pedestal'} nw_lw_locations = {'Mushroom', 'Master Sword Pedestal'}
central_lw_locations = {'Sunken Treasure', 'Flute Spot'} central_lw_locations = {'Sunken Treasure', 'Flute Spot'}
desert_locations = {'Purple Chest', 'Desert Ledge'} desert_locations = {'Purple Chest', 'Desert Ledge', 'Bombos Tablet'}
lake_locations = {'Hobo'} lake_locations = {'Hobo', 'Lake Hylia Island'}
east_lw_locations = {"Zora's Ledge", 'King Zora'} east_lw_locations = {"Zora's Ledge", 'King Zora'}
lw_dm_locations = {'Old Man', 'Spectacle Rock', 'Ether Tablet'} lw_dm_locations = {'Old Man', 'Spectacle Rock', 'Ether Tablet', 'Floating Island'}
east_dw_locations = {'Pyramid', 'Catfish'} east_dw_locations = {'Pyramid', 'Catfish'}
south_dw_locations = {'Stumpy', 'Digging Game', 'Bombos Tablet', 'Lake Hylia Island'} south_dw_locations = {'Stumpy', 'Digging Game'}
voo_north_locations = {'Bumper Cave Ledge'} voo_north_locations = {'Bumper Cave Ledge'}
ddm_locations = {'Floating Island'} ddm_locations = set()
kak_entrances = ['Kakariko Well Cave', 'Bat Cave Cave', 'Elder House (East)', 'Elder House (West)', 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', 'Two Brothers House (East)', 'Two Brothers House (West)', 'Blinds Hideout', 'Chicken House',
@@ -43,67 +46,48 @@ def create_district_helper(world, player):
nw_lw_entrances = ['North Fairy Cave', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Sanctuary', 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', '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', 'Fortune Teller (Light)', 'Bonk Rock Cave', 'Lumberjack House', 'North Fairy Cave Drop',
'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave'] 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave', 'Graveyard Cave']
central_lw_entrances = ['Links House', 'Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (West)', central_lw_entrances = ['Links House', 'Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (West)',
'Hyrule Castle Entrance (East)', 'Agahnims Tower', 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (East)', 'Agahnims Tower', 'Hyrule Castle Secret Entrance Stairs',
'Dam', 'Bonk Fairy (Light)', 'Light Hype Fairy', 'Cave Shop (Lake Hylia)', 'Dam', 'Bonk Fairy (Light)', 'Light Hype Fairy', 'Hyrule Castle Secret Entrance Drop',
'Lake Hylia Fortune Teller', 'Hyrule Castle Secret Entrance Drop'] 'Cave 45']
desert_entrances = ['Desert Palace Entrance (South)', 'Desert Palace Entrance (West)', desert_entrances = ['Desert Palace Entrance (South)', 'Desert Palace Entrance (West)',
'Desert Palace Entrance (North)', 'Desert Palace Entrance (East)', 'Desert Fairy', 'Desert Palace Entrance (North)', 'Desert Palace Entrance (East)', 'Desert Fairy',
'Aginahs Cave', '50 Rupee Cave'] 'Aginahs Cave', '50 Rupee Cave', 'Checkerboard Cave']
lake_entrances = ['Capacity Upgrade', 'Mini Moldorm Cave', 'Good Bee Cave', '20 Rupee Cave', 'Ice Rod 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', east_lw_entrances = ['Eastern Palace', 'Waterfall of Wishing', 'Lake Hylia Fairy', 'Sahasrahlas Hut',
'Long Fairy Cave', 'Potion Shop'] 'Long Fairy Cave', 'Potion Shop']
lw_dm_entrances = ['Tower of Hera', 'Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 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', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave',
'Spectacle Rock Cave (Bottom)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Spectacle Rock Cave (Bottom)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)',
'Paradox Cave (Top)', 'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Paradox Cave (Top)', 'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)',
'Spiral Cave', 'Spiral Cave (Bottom)', 'Hookshot Fairy'] 'Spiral Cave', 'Spiral Cave (Bottom)', 'Hookshot Fairy', 'Mimic Cave']
east_dw_entrances = ['Palace of Darkness', 'Pyramid Entrance', 'Pyramid Fairy', 'East Dark World Hint', 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'] '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', 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', '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', 'Cave 45'] '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)', 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', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section',
'Bumper Cave (Bottom)', 'Bumper Cave (Top)', 'Brewery', 'C-Shaped House', 'Chest Game', 'Bumper Cave (Bottom)', 'Bumper Cave (Top)', 'Brewery', 'C-Shaped House', 'Chest Game',
'Dark World Hammer Peg Cave', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Dark World Hammer Peg Cave', 'Red Shield Shop', 'Dark Sanctuary Hint',
'Fortune Teller (Dark)', 'Dark World Shop', 'Dark World Lumberjack Shop', 'Graveyard Cave', '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 (West)', 'Skull Woods First Section Hole (East)',
'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
mire_entrances = ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy', 'Checkerboard Cave'] 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)', 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)', 'Turtle Rock Isolated Ledge Entrance', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)',
'Hookshot Cave', 'Hookshot Cave Back Entrance', 'Ganons Tower', 'Spike Cave', 'Hookshot Cave', 'Hookshot Cave Back Entrance', 'Ganons Tower', 'Spike Cave',
'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Fairy', 'Mimic Cave'] 'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Fairy']
if inverted: if inverted:
south_dw_locations.remove('Bombos Tablet')
south_dw_locations.remove('Lake Hylia Island')
voo_north_locations.remove('Bumper Cave Ledge')
ddm_locations.remove('Floating Island')
desert_locations.add('Bombos Tablet')
lake_locations.add('Lake Hylia Island')
nw_lw_locations.add('Bumper Cave Ledge')
lw_dm_locations.add('Floating Island')
south_dw_entrances.remove('Cave 45')
central_lw_entrances.append('Cave 45')
voo_north_entrances.remove('Graveyard Cave')
nw_lw_entrances.append('Graveyard Cave')
mire_entrances.remove('Checkerboard Cave')
desert_entrances.append('Checkerboard Cave')
ddm_entrances.remove('Mimic Cave')
lw_dm_entrances.append('Mimic Cave')
south_dw_entrances.remove('Big Bomb Shop') south_dw_entrances.remove('Big Bomb Shop')
central_lw_entrances.append('Inverted Big Bomb Shop') central_lw_entrances.append('Inverted Big Bomb Shop')
central_lw_entrances.remove('Links House') central_lw_entrances.remove('Links House')
south_dw_entrances.append('Inverted Links House') south_dw_entrances.append('Inverted Links House')
voo_north_entrances.remove('Dark Sanctuary') voo_north_entrances.remove('Dark Sanctuary')
voo_north_entrances.append('Inverted Dark Sanctuary') voo_north_entrances.append('Inverted Dark Sanctuary')
voo_north_entrances.remove('Bumper Cave (Top)')
nw_lw_entrances.append('Bumper Cave (Top)')
ddm_entrances.remove('Ganons Tower') ddm_entrances.remove('Ganons Tower')
central_lw_entrances.append('Inverted Ganons Tower') central_lw_entrances.append('Inverted Ganons Tower')
central_lw_entrances.remove('Agahnims Tower') central_lw_entrances.remove('Agahnims Tower')
@@ -116,7 +100,7 @@ def create_district_helper(world, player):
districts['Kakariko'] = District('Kakariko', kak_locations, entrances=kak_entrances) districts['Kakariko'] = District('Kakariko', kak_locations, entrances=kak_entrances)
districts['Northwest Hyrule'] = District('Northwest Hyrule', nw_lw_locations, entrances=nw_lw_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['Central Hyrule'] = District('Central Hyrule', central_lw_locations, entrances=central_lw_entrances)
districts['Desert'] = District('Desert', desert_locations, entrances=desert_entrances) districts['The Desert Area'] = District('Desert', desert_locations, entrances=desert_entrances)
districts['Lake Hylia'] = District('Lake Hylia', lake_locations, entrances=lake_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['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['Death Mountain'] = District('Death Mountain', lw_dm_locations, entrances=lw_dm_entrances)
@@ -124,7 +108,7 @@ def create_district_helper(world, player):
districts['South Dark World'] = District('South Dark World', south_dw_locations, entrances=south_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, districts['Northwest Dark World'] = District('Northwest Dark World', voo_north_locations,
entrances=voo_north_entrances) entrances=voo_north_entrances)
districts['The Mire'] = District('The Mire', set(), entrances=mire_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['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.update({x: District(x, set(), dungeon=x) for x in dungeon_table.keys()})
@@ -136,8 +120,9 @@ def resolve_districts(world):
state = CollectionState(world) state = CollectionState(world)
state.sweep_for_events() state.sweep_for_events()
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
# these are not static for OWR - but still important
inaccessible = inaccessible_regions_inv if world.mode[player] == 'inverted' else inaccessible_regions_std
check_set = find_reachable_locations(state, player) check_set = find_reachable_locations(state, player)
used_locations = {l for d in world.districts[player].values() for l in d.locations}
for name, district in world.districts[player].items(): for name, district in world.districts[player].items():
if district.dungeon: if district.dungeon:
layout = world.dungeon_layouts[player][district.dungeon] layout = world.dungeon_layouts[player][district.dungeon]
@@ -153,12 +138,16 @@ def resolve_districts(world):
visited.add(region) visited.add(region)
if region.type == RegionType.Cave: if region.type == RegionType.Cave:
for location in region.locations: for location in region.locations:
if location.name not in used_locations and not location.item and location.real: if not location.item and location.real:
district.locations.add(location.name) district.locations.add(location.name)
used_locations.add(location.name)
for ext in region.exits: for ext in region.exits:
if ext.connected_region not in visited: if ext.connected_region not in visited:
queue.appendleft(ext.connected_region) 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 district.sphere_one = len(check_set.intersection(district.locations)) > 0
@@ -169,3 +158,12 @@ def find_reachable_locations(state, player):
if location.can_reach(state) and not location.forced_item and location.real: if location.can_reach(state) and not location.forced_item and location.real:
check_set.add(location.name) check_set.add(location.name)
return check_set return check_set
inaccessible_regions_std = {'Desert Palace Lone Stairs', 'Bumper Cave Ledge', 'Skull Woods Forest (West)',
'Dark Death Mountain Ledge', 'Dark Death Mountain Isolated Ledge',
'Death Mountain Floating Island (Dark World)'}
inaccessible_regions_inv = {'Desert Palace Lone Stairs', 'Maze Race Ledge', 'Desert Ledge',
'Desert Palace Entrance (North) Spot', 'Hyrule Castle Ledge', 'Death Mountain Return Ledge'}