Multiworld core implementation By Bonta0

Does not include the server/client code or the rom writes
specific to it. Indeed it cannot write multiworld roms at
all right now, pending addition future updates to support
the official ALTTPR Multiworld client.

Includes some GUI changes by Alaszun

Co-authored-by: Alaszun <koelze@google.com>
This commit is contained in:
Bonta-kun 2019-04-18 11:23:24 +02:00 committed by Kevin Cathcart
parent d44d194de7
commit 1a62b1da28
16 changed files with 1821 additions and 1611 deletions

3
.gitignore vendored
View File

@ -8,3 +8,6 @@ build
bundle/components.wxs bundle/components.wxs
dist dist
README.html README.html
.vs/
*multidata
*multisave

View File

@ -21,7 +21,7 @@ def adjust(args):
outfilebase = os.path.basename(args.rom)[:-4] + '_adjusted' outfilebase = os.path.basename(args.rom)[:-4] + '_adjusted'
if os.stat(args.rom).st_size == 2097152 and os.path.splitext(args.rom)[-1].lower() == '.sfc': if os.stat(args.rom).st_size in (0x200000, 0x400000) and os.path.splitext(args.rom)[-1].lower() == '.sfc':
rom = LocalRom(args.rom, False) rom = LocalRom(args.rom, False)
else: else:
raise RuntimeError('Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.') raise RuntimeError('Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.')

View File

@ -7,7 +7,8 @@ from Utils import int16_as_bytes
class World(object): class World(object):
def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints): def __init__(self, players, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints):
self.players = players
self.shuffle = shuffle self.shuffle = shuffle
self.logic = logic self.logic = logic
self.mode = mode self.mode = mode
@ -22,7 +23,7 @@ class World(object):
self.itempool = [] self.itempool = []
self.seed = None self.seed = None
self.state = CollectionState(self) self.state = CollectionState(self)
self.required_medallions = ['Ether', 'Quake'] self.required_medallions = dict([(player, ['Ether', 'Quake']) for player in range(1, players + 1)])
self._cached_entrances = None self._cached_entrances = None
self._cached_locations = None self._cached_locations = None
self._entrance_cache = {} self._entrance_cache = {}
@ -32,10 +33,10 @@ class World(object):
self.required_locations = [] self.required_locations = []
self.place_dungeon_items = place_dungeon_items # configurable in future self.place_dungeon_items = place_dungeon_items # configurable in future
self.shuffle_bonk_prizes = False self.shuffle_bonk_prizes = False
self.swamp_patch_required = False self.swamp_patch_required = {player: False for player in range(1, players + 1)}
self.powder_patch_required = False self.powder_patch_required = {player: False for player in range(1, players + 1)}
self.ganon_at_pyramid = True self.ganon_at_pyramid = {player: True for player in range(1, players + 1)}
self.ganonstower_vanilla = True self.ganonstower_vanilla = {player: True for player in range(1, players + 1)}
self.sewer_light_cone = mode == 'standard' self.sewer_light_cone = mode == 'standard'
self.light_world_light_cone = False self.light_world_light_cone = False
self.dark_world_light_cone = False self.dark_world_light_cone = False
@ -78,52 +79,52 @@ class World(object):
for region in self.regions: for region in self.regions:
region.world = self region.world = self
def get_region(self, regionname): def get_region(self, regionname, player):
if isinstance(regionname, Region): if isinstance(regionname, Region):
return regionname return regionname
try: try:
return self._region_cache[regionname] return self._region_cache[(regionname, player)]
except KeyError: except KeyError:
for region in self.regions: for region in self.regions:
if region.name == regionname: if region.name == regionname and region.player == player:
self._region_cache[regionname] = region self._region_cache[(regionname, player)] = region
return region return region
raise RuntimeError('No such region %s' % regionname) raise RuntimeError('No such region %s for player %d' % (regionname, player))
def get_entrance(self, entrance): def get_entrance(self, entrance, player):
if isinstance(entrance, Entrance): if isinstance(entrance, Entrance):
return entrance return entrance
try: try:
return self._entrance_cache[entrance] return self._entrance_cache[(entrance, player)]
except KeyError: except KeyError:
for region in self.regions: for region in self.regions:
for exit in region.exits: for exit in region.exits:
if exit.name == entrance: if exit.name == entrance and exit.player == player:
self._entrance_cache[entrance] = exit self._entrance_cache[(entrance, player)] = exit
return exit return exit
raise RuntimeError('No such entrance %s' % entrance) raise RuntimeError('No such entrance %s for player %d' % (entrance, player))
def get_location(self, location): def get_location(self, location, player):
if isinstance(location, Location): if isinstance(location, Location):
return location return location
try: try:
return self._location_cache[location] return self._location_cache[(location, player)]
except KeyError: except KeyError:
for region in self.regions: for region in self.regions:
for r_location in region.locations: for r_location in region.locations:
if r_location.name == location: if r_location.name == location and r_location.player == player:
self._location_cache[location] = r_location self._location_cache[(location, player)] = r_location
return r_location return r_location
raise RuntimeError('No such location %s' % location) raise RuntimeError('No such location %s for player %d' % (location, player))
def get_dungeon(self, dungeonname): def get_dungeon(self, dungeonname, player):
if isinstance(dungeonname, Dungeon): if isinstance(dungeonname, Dungeon):
return dungeonname return dungeonname
for dungeon in self.dungeons: for dungeon in self.dungeons:
if dungeon.name == dungeonname: if dungeon.name == dungeonname and dungeon.player == player:
return dungeon return dungeon
raise RuntimeError('No such dungeon %s' % dungeonname) raise RuntimeError('No such dungeon %s for player %d' % (dungeonname, player))
def get_all_state(self, keys=False): def get_all_state(self, keys=False):
ret = CollectionState(self) ret = CollectionState(self)
@ -131,58 +132,61 @@ class World(object):
def soft_collect(item): def soft_collect(item):
if item.name.startswith('Progressive '): if item.name.startswith('Progressive '):
if 'Sword' in item.name: if 'Sword' in item.name:
if ret.has('Golden Sword'): if ret.has('Golden Sword', item.player):
pass pass
elif ret.has('Tempered Sword') and self.difficulty_requirements.progressive_sword_limit >= 4: elif ret.has('Tempered Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 4:
ret.prog_items.append('Golden Sword') ret.prog_items.append(('Golden Sword', item.player))
elif ret.has('Master Sword') and self.difficulty_requirements.progressive_sword_limit >= 3: elif ret.has('Master Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 3:
ret.prog_items.append('Tempered Sword') ret.prog_items.append(('Tempered Sword', item.player))
elif ret.has('Fighter Sword') and self.difficulty_requirements.progressive_sword_limit >= 2: elif ret.has('Fighter Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 2:
ret.prog_items.append('Master Sword') ret.prog_items.append(('Master Sword', item.player))
elif self.difficulty_requirements.progressive_sword_limit >= 1: elif self.difficulty_requirements.progressive_sword_limit >= 1:
ret.prog_items.append('Fighter Sword') ret.prog_items.append(('Fighter Sword', item.player))
elif 'Glove' in item.name: elif 'Glove' in item.name:
if ret.has('Titans Mitts'): if ret.has('Titans Mitts', item.player):
pass pass
elif ret.has('Power Glove'): elif ret.has('Power Glove', item.player):
ret.prog_items.append('Titans Mitts') ret.prog_items.append(('Titans Mitts', item.player))
else: else:
ret.prog_items.append('Power Glove') ret.prog_items.append(('Power Glove', item.player))
elif 'Shield' in item.name: elif 'Shield' in item.name:
if ret.has('Mirror Shield'): if ret.has('Mirror Shield', item.player):
pass pass
elif ret.has('Red Shield') and self.difficulty_requirements.progressive_shield_limit >= 3: elif ret.has('Red Shield', item.player) and self.difficulty_requirements.progressive_shield_limit >= 3:
ret.prog_items.append('Mirror Shield') ret.prog_items.append(('Mirror Shield', item.player))
elif ret.has('Blue Shield') and self.difficulty_requirements.progressive_shield_limit >= 2: elif ret.has('Blue Shield', item.player) and self.difficulty_requirements.progressive_shield_limit >= 2:
ret.prog_items.append('Red Shield') ret.prog_items.append(('Red Shield', item.player))
elif self.difficulty_requirements.progressive_shield_limit >= 1: elif self.difficulty_requirements.progressive_shield_limit >= 1:
ret.prog_items.append('Blue Shield') ret.prog_items.append(('Blue Shield', item.player))
elif item.name.startswith('Bottle'): elif item.name.startswith('Bottle'):
if ret.bottle_count() < self.difficulty_requirements.progressive_bottle_limit: if ret.bottle_count(item.player) < self.difficulty_requirements.progressive_bottle_limit:
ret.prog_items.append(item.name) ret.prog_items.append((item.name, item.player))
elif item.advancement or item.key: elif item.advancement or item.key:
ret.prog_items.append(item.name) ret.prog_items.append((item.name, item.player))
for item in self.itempool: for item in self.itempool:
soft_collect(item) soft_collect(item)
if keys: if keys:
from Items import ItemFactory for p in range(1, self.players + 1):
for item in ItemFactory(['Small Key (Escape)', 'Big Key (Eastern Palace)', 'Big Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Tower of Hera)', 'Small Key (Tower of Hera)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', from Items import ItemFactory
'Big Key (Palace of Darkness)'] + ['Small Key (Palace of Darkness)'] * 6 + ['Big Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Skull Woods)'] + ['Small Key (Skull Woods)'] * 3 + ['Big Key (Swamp Palace)', for item in ItemFactory(['Small Key (Escape)', 'Big Key (Eastern Palace)', 'Big Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Tower of Hera)', 'Small Key (Tower of Hera)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)',
'Small Key (Swamp Palace)', 'Big Key (Ice Palace)'] + ['Small Key (Ice Palace)'] * 2 + ['Big Key (Misery Mire)', 'Big Key (Turtle Rock)', 'Big Key (Ganons Tower)'] + ['Small Key (Misery Mire)'] * 3 + ['Small Key (Turtle Rock)'] * 4 + ['Small Key (Ganons Tower)'] * 4): 'Big Key (Palace of Darkness)'] + ['Small Key (Palace of Darkness)'] * 6 + ['Big Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Skull Woods)'] + ['Small Key (Skull Woods)'] * 3 + ['Big Key (Swamp Palace)',
soft_collect(item) 'Small Key (Swamp Palace)', 'Big Key (Ice Palace)'] + ['Small Key (Ice Palace)'] * 2 + ['Big Key (Misery Mire)', 'Big Key (Turtle Rock)', 'Big Key (Ganons Tower)'] + ['Small Key (Misery Mire)'] * 3 + ['Small Key (Turtle Rock)'] * 4 + ['Small Key (Ganons Tower)'] * 4,
p):
soft_collect(item)
ret.sweep_for_events() ret.sweep_for_events()
return ret return ret
def get_items(self): def get_items(self):
return [loc.item for loc in self.get_filled_locations()] + self.itempool return [loc.item for loc in self.get_filled_locations()] + self.itempool
def find_items(self, item): def find_items(self, item, player):
return [location for location in self.get_locations() if location.item is not None and location.item.name == item] return [location for location in self.get_locations() if location.item is not None and location.item.name == item and location.item.player == player]
def push_item(self, location, item, collect=True): def push_item(self, location, item, collect=True):
if not isinstance(location, Location): if not isinstance(location, Location):
location = self.get_location(location) raise RuntimeError('Cannot assign item %s to location %s (player %d).' % (item, location, item.player))
if location.can_fill(self.state, item, False): if location.can_fill(self.state, item, False):
location.item = item location.item = item
@ -214,21 +218,21 @@ class World(object):
def clear_location_cache(self): def clear_location_cache(self):
self._cached_locations = None self._cached_locations = None
def get_unfilled_locations(self): def get_unfilled_locations(self, player=None):
return [location for location in self.get_locations() if location.item is None] return [location for location in self.get_locations() if (player is None or location.player == player) and location.item is None]
def get_filled_locations(self): def get_filled_locations(self, player=None):
return [location for location in self.get_locations() if location.item is not None] return [location for location in self.get_locations() if (player is None or location.player == player) and location.item is not None]
def get_reachable_locations(self, state=None): def get_reachable_locations(self, state=None, player=None):
if state is None: if state is None:
state = self.state state = self.state
return [location for location in self.get_locations() if state.can_reach(location)] return [location for location in self.get_locations() if (player is None or location.player == player) and state.can_reach(location)]
def get_placeable_locations(self, state=None): def get_placeable_locations(self, state=None, player=None):
if state is None: if state is None:
state = self.state state = self.state
return [location for location in self.get_locations() if location.item is None and state.can_reach(location)] return [location for location in self.get_locations() if (player is None or location.player == player) and location.item is None and state.can_reach(location)]
def unlocks_new_location(self, item): def unlocks_new_location(self, item):
temp_state = self.state.copy() temp_state = self.state.copy()
@ -241,11 +245,10 @@ class World(object):
return False return False
def has_beaten_game(self, state): def has_beaten_game(self, state):
if state.has('Triforce'): if all([state.has('Triforce', player) for player in range(1, self.players + 1)]):
return True
if self.goal in ['triforcehunt'] and all([((state.item_count('Triforce Piece', player) + state.item_count('Power Star', player)) > self.treasure_hunt_count) for player in range(1, self.players + 1)]):
return True return True
if self.goal in ['triforcehunt']:
if state.item_count('Triforce Piece') + state.item_count('Power Star') > self.treasure_hunt_count:
return True
return False return False
def can_beat_game(self, starting_state=None): def can_beat_game(self, starting_state=None):
@ -259,17 +262,21 @@ class World(object):
prog_locations = [location for location in self.get_locations() if location.item is not None and (location.item.advancement or location.event) and location not in state.locations_checked] prog_locations = [location for location in self.get_locations() if location.item is not None and (location.item.advancement or location.event) and location not in state.locations_checked]
treasure_pieces_collected = state.item_count('Triforce Piece') + state.item_count('Power Star') treasure_pieces_collected = dict([(player, state.item_count('Triforce Piece', player) + state.item_count('Power Star', player)) for player in range(1, self.players + 1)])
triforces_collected = dict([(player, state.has('Triforce', player)) for player in range(1, self.players + 1)])
while prog_locations: while prog_locations:
sphere = [] sphere = []
# build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres # build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres
for location in prog_locations: for location in prog_locations:
if state.can_reach(location): if state.can_reach(location):
if location.item.name == 'Triforce': if location.item.name == 'Triforce':
return True triforces_collected[location.item.player] = True
if all(triforces_collected.values()):
return True
elif location.item.name in ['Triforce Piece', 'Power Star']: elif location.item.name in ['Triforce Piece', 'Power Star']:
treasure_pieces_collected += 1 treasure_pieces_collected[location.item.player] += 1
if self.goal in ['triforcehunt'] and treasure_pieces_collected >= self.treasure_hunt_count: if self.goal in ['triforcehunt'] and all([treasure_pieces_collected[player] >= self.treasure_hunt_count for player in range(1, self.players + 1)]):
return True return True
sphere.append(location) sphere.append(location)
@ -283,8 +290,7 @@ class World(object):
return False return False
@property def option_identifier(self, maxbytes, player):
def option_identifier(self):
id_value = 0 id_value = 0
id_value_max = 1 id_value_max = 1
@ -309,10 +315,11 @@ class World(object):
markbool(self.shuffle_ganon) markbool(self.shuffle_ganon)
markbool(self.keysanity) markbool(self.keysanity)
markbool(self.retro) markbool(self.retro)
assert id_value_max <= 0xFFFFFFFF marksequence(range(1, 256), self.players)
marksequence(range(1, self.players + 1), player)
assert id_value_max < (1 << (maxbytes * 8))
return id_value return id_value
class CollectionState(object): class CollectionState(object):
def __init__(self, parent): def __init__(self, parent):
@ -326,7 +333,6 @@ class CollectionState(object):
def update_reachable_regions(self): def update_reachable_regions(self):
self.stale=False self.stale=False
new_regions = True new_regions = True
reachable_regions_count = len(self.reachable_regions) reachable_regions_count = len(self.reachable_regions)
while new_regions: while new_regions:
@ -347,149 +353,149 @@ class CollectionState(object):
ret.stale = True ret.stale = True
return ret return ret
def can_reach(self, spot, resolution_hint=None): def can_reach(self, spot, resolution_hint=None, player=None):
try: try:
spot_type = spot.spot_type spot_type = spot.spot_type
except AttributeError: except AttributeError:
# try to resolve a name # try to resolve a name
if resolution_hint == 'Location': if resolution_hint == 'Location':
spot = self.world.get_location(spot) spot = self.world.get_location(spot, player)
elif resolution_hint == 'Entrance': elif resolution_hint == 'Entrance':
spot = self.world.get_entrance(spot) spot = self.world.get_entrance(spot, player)
else: else:
# default to Region # default to Region
spot = self.world.get_region(spot) spot = self.world.get_region(spot, player)
return spot.can_reach(self) return spot.can_reach(self)
def sweep_for_events(self, key_only=False): def sweep_for_events(self, key_only=False, locations=None):
# this may need improvement # this may need improvement
new_locations = True new_locations = True
checked_locations = 0 checked_locations = 0
while new_locations: while new_locations:
reachable_events = [location for location in self.world.get_filled_locations() if location.event and (not key_only or location.item.key) and self.can_reach(location)] if locations is None:
locations = self.world.get_filled_locations()
reachable_events = [location for location in locations if location.event and (not key_only or location.item.key) and self.can_reach(location)]
for event in reachable_events: for event in reachable_events:
if event.name not in self.events: if (event.name, event.player) not in self.events:
self.events.append(event.name) self.events.append((event.name, event.player))
self.collect(event.item, True, event) self.collect(event.item, True, event)
new_locations = len(reachable_events) > checked_locations new_locations = len(reachable_events) > checked_locations
checked_locations = len(reachable_events) checked_locations = len(reachable_events)
def has(self, item, player, count=1):
def has(self, item, count=1):
if count == 1: if count == 1:
return item in self.prog_items return (item, player) in self.prog_items
return self.item_count(item) >= count return self.item_count(item, player) >= count
def has_key(self, item, count=1): def has_key(self, item, player, count=1):
if self.world.retro: if self.world.retro:
return self.can_buy_unlimited('Small Key (Universal)') return self.can_buy_unlimited('Small Key (Universal)', player)
if count == 1: if count == 1:
return item in self.prog_items return (item, player) in self.prog_items
return self.item_count(item) >= count return self.item_count(item, player) >= count
def can_buy_unlimited(self, item): def can_buy_unlimited(self, item, player):
for shop in self.world.shops: for shop in self.world.shops:
if shop.has_unlimited(item) and shop.region.can_reach(self): if shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self):
return True return True
return False return False
def item_count(self, item): def item_count(self, item, player):
return len([pritem for pritem in self.prog_items if pritem == item]) return len([pritem for pritem in self.prog_items if pritem == (item, player)])
def can_lift_rocks(self): def can_lift_rocks(self, player):
return self.has('Power Glove') or self.has('Titans Mitts') return self.has('Power Glove', player) or self.has('Titans Mitts', player)
def has_bottle(self): def has_bottle(self, player):
return self.bottle_count() > 0 return self.bottle_count(player) > 0
def bottle_count(self): def bottle_count(self, player):
return len([pritem for pritem in self.prog_items if pritem.startswith('Bottle')]) return len([pritem for pritem in self.prog_items if pritem[0].startswith('Bottle') and pritem[1] == player])
def has_hearts(self, count): def has_hearts(self, player, count):
# Warning: This only considers items that are marked as advancement items # Warning: This only considers items that are marked as advancement items
return self.heart_count() >= count return self.heart_count(player) >= count
def heart_count(self): def heart_count(self, player):
# Warning: This only considers items that are marked as advancement items # Warning: This only considers items that are marked as advancement items
return ( return (
self.item_count('Boss Heart Container') self.item_count('Boss Heart Container', player)
+ self.item_count('Sanctuary Heart Container') + self.item_count('Sanctuary Heart Container', player)
+ self.item_count('Piece of Heart') // 4 + self.item_count('Piece of Heart', player) // 4
+ 3 # starting hearts + 3 # starting hearts
) )
def can_lift_heavy_rocks(self): def can_lift_heavy_rocks(self, player):
return self.has('Titans Mitts') return self.has('Titans Mitts', player)
def can_extend_magic(self, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has. def can_extend_magic(self, player, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has.
basemagic = 8 basemagic = 8
if self.has('Quarter Magic'): if self.has('Quarter Magic', player):
basemagic = 32 basemagic = 32
elif self.has('Half Magic'): elif self.has('Half Magic', player):
basemagic = 16 basemagic = 16
if self.can_buy_unlimited('Green Potion') or self.can_buy_unlimited('Blue Potion'): if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player):
if self.world.difficulty == 'hard' and not fullrefill: if self.world.difficulty == 'hard' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count()) basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player))
elif self.world.difficulty == 'expert' and not fullrefill: elif self.world.difficulty == 'expert' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count()) basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count(player))
elif self.world.difficulty == 'insane' and not fullrefill: elif self.world.difficulty == 'insane' and not fullrefill:
basemagic = basemagic basemagic = basemagic
else: else:
basemagic = basemagic + basemagic * self.bottle_count() basemagic = basemagic + basemagic * self.bottle_count(player)
return basemagic >= smallmagic return basemagic >= smallmagic
def can_kill_most_things(self, enemies=5): def can_kill_most_things(self, player, enemies=5):
return (self.has_blunt_weapon() return (self.has_blunt_weapon(player)
or self.has('Cane of Somaria') or self.has('Cane of Somaria', player)
or (self.has('Cane of Byrna') and (enemies < 6 or self.can_extend_magic())) or (self.has('Cane of Byrna', player) and (enemies < 6 or self.can_extend_magic(player)))
or self.can_shoot_arrows() or self.can_shoot_arrows(player)
or self.has('Fire Rod') or self.has('Fire Rod', player)
) )
def can_shoot_arrows(self): def can_shoot_arrows(self, player):
if self.world.retro: if self.world.retro:
#TODO: need to decide how we want to handle wooden arrows longer-term (a can-buy-a check, or via dynamic shop location) #TODO: need to decide how we want to handle wooden arrows longer-term (a can-buy-a check, or via dynamic shop location)
#FIXME: Should do something about hard+ ganon only silvers. For the moment, i believe they effective grant wooden, so we are safe #FIXME: Should do something about hard+ ganon only silvers. For the moment, i believe they effective grant wooden, so we are safe
return self.has('Bow') and (self.has('Silver Arrows') or self.can_buy_unlimited('Single Arrow')) return self.has('Bow', player) and (self.has('Silver Arrows', player) or self.can_buy_unlimited('Single Arrow', player))
return self.has('Bow') return self.has('Bow', player)
def can_get_good_bee(self): def can_get_good_bee(self, player):
cave = self.world.get_region('Good Bee Cave') cave = self.world.get_region('Good Bee Cave', player)
return ( return (
self.has_bottle() and self.has_bottle(player) and
self.has('Bug Catching Net') and self.has('Bug Catching Net', player) and
(self.has_Boots() or (self.has_sword() and self.has('Quake'))) and (self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))) and
cave.can_reach(self) and cave.can_reach(self) and
(cave.is_light_world or self.has_Pearl()) (cave.is_light_world or self.has_Pearl(player))
) )
def has_sword(self): def has_sword(self, player):
return self.has('Fighter Sword') or self.has('Master Sword') or self.has('Tempered Sword') or self.has('Golden Sword') return self.has('Fighter Sword', player) or self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', player)
def has_beam_sword(self): def has_beam_sword(self, player):
return self.has('Master Sword') or self.has('Tempered Sword') or self.has('Golden Sword') return self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', player)
def has_blunt_weapon(self): def has_blunt_weapon(self, player):
return self.has_sword() or self.has('Hammer') return self.has_sword(player) or self.has('Hammer', player)
def has_Mirror(self): def has_Mirror(self, player):
return self.has('Magic Mirror') return self.has('Magic Mirror', player)
def has_Boots(self): def has_Boots(self, player):
return self.has('Pegasus Boots') return self.has('Pegasus Boots', player)
def has_Pearl(self): def has_Pearl(self, player):
return self.has('Moon Pearl') return self.has('Moon Pearl', player)
def has_fire_source(self): def has_fire_source(self, player):
return self.has('Fire Rod') or self.has('Lamp') return self.has('Fire Rod', player) or self.has('Lamp', player)
def has_misery_mire_medallion(self): def has_misery_mire_medallion(self, player):
return self.has(self.world.required_medallions[0]) return self.has(self.world.required_medallions[player][0], player)
def has_turtle_rock_medallion(self): def has_turtle_rock_medallion(self, player):
return self.has(self.world.required_medallions[1]) return self.has(self.world.required_medallions[player][1], player)
def collect(self, item, event=False, location=None): def collect(self, item, event=False, location=None):
if location: if location:
@ -497,47 +503,47 @@ class CollectionState(object):
changed = False changed = False
if item.name.startswith('Progressive '): if item.name.startswith('Progressive '):
if 'Sword' in item.name: if 'Sword' in item.name:
if self.has('Golden Sword'): if self.has('Golden Sword', item.player):
pass pass
elif self.has('Tempered Sword') and self.world.difficulty_requirements.progressive_sword_limit >= 4: elif self.has('Tempered Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 4:
self.prog_items.append('Golden Sword') self.prog_items.append(('Golden Sword', item.player))
changed = True changed = True
elif self.has('Master Sword') and self.world.difficulty_requirements.progressive_sword_limit >= 3: elif self.has('Master Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 3:
self.prog_items.append('Tempered Sword') self.prog_items.append(('Tempered Sword', item.player))
changed = True changed = True
elif self.has('Fighter Sword') and self.world.difficulty_requirements.progressive_sword_limit >= 2: elif self.has('Fighter Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 2:
self.prog_items.append('Master Sword') self.prog_items.append(('Master Sword', item.player))
changed = True changed = True
elif self.world.difficulty_requirements.progressive_sword_limit >= 1: elif self.world.difficulty_requirements.progressive_sword_limit >= 1:
self.prog_items.append('Fighter Sword') self.prog_items.append(('Fighter Sword', item.player))
changed = True changed = True
elif 'Glove' in item.name: elif 'Glove' in item.name:
if self.has('Titans Mitts'): if self.has('Titans Mitts', item.player):
pass pass
elif self.has('Power Glove'): elif self.has('Power Glove', item.player):
self.prog_items.append('Titans Mitts') self.prog_items.append(('Titans Mitts', item.player))
changed = True changed = True
else: else:
self.prog_items.append('Power Glove') self.prog_items.append(('Power Glove', item.player))
changed = True changed = True
elif 'Shield' in item.name: elif 'Shield' in item.name:
if self.has('Mirror Shield'): if self.has('Mirror Shield', item.player):
pass pass
elif self.has('Red Shield') and self.world.difficulty_requirements.progressive_shield_limit >= 3: elif self.has('Red Shield', item.player) and self.world.difficulty_requirements.progressive_shield_limit >= 3:
self.prog_items.append('Mirror Shield') self.prog_items.append(('Mirror Shield', item.player))
changed = True changed = True
elif self.has('Blue Shield') and self.world.difficulty_requirements.progressive_shield_limit >= 2: elif self.has('Blue Shield', item.player) and self.world.difficulty_requirements.progressive_shield_limit >= 2:
self.prog_items.append('Red Shield') self.prog_items.append(('Red Shield', item.player))
changed = True changed = True
elif self.world.difficulty_requirements.progressive_shield_limit >= 1: elif self.world.difficulty_requirements.progressive_shield_limit >= 1:
self.prog_items.append('Blue Shield') self.prog_items.append(('Blue Shield', item.player))
changed = True changed = True
elif item.name.startswith('Bottle'): elif item.name.startswith('Bottle'):
if self.bottle_count() < self.world.difficulty_requirements.progressive_bottle_limit: if self.bottle_count(item.player) < self.world.difficulty_requirements.progressive_bottle_limit:
self.prog_items.append(item.name) self.prog_items.append((item.name, item.player))
changed = True changed = True
elif event or item.advancement: elif event or item.advancement:
self.prog_items.append(item.name) self.prog_items.append((item.name, item.player))
changed = True changed = True
self.stale = True self.stale = True
@ -551,27 +557,27 @@ class CollectionState(object):
to_remove = item.name to_remove = item.name
if to_remove.startswith('Progressive '): if to_remove.startswith('Progressive '):
if 'Sword' in to_remove: if 'Sword' in to_remove:
if self.has('Golden Sword'): if self.has('Golden Sword', item.player):
to_remove = 'Golden Sword' to_remove = 'Golden Sword'
elif self.has('Tempered Sword'): elif self.has('Tempered Sword', item.player):
to_remove = 'Tempered Sword' to_remove = 'Tempered Sword'
elif self.has('Master Sword'): elif self.has('Master Sword', item.player):
to_remove = 'Master Sword' to_remove = 'Master Sword'
elif self.has('Fighter Sword'): elif self.has('Fighter Sword', item.player):
to_remove = 'Fighter Sword' to_remove = 'Fighter Sword'
else: else:
to_remove = None to_remove = None
elif 'Glove' in item.name: elif 'Glove' in item.name:
if self.has('Titans Mitts'): if self.has('Titans Mitts', item.player):
to_remove = 'Titans Mitts' to_remove = 'Titans Mitts'
elif self.has('Power Glove'): elif self.has('Power Glove', item.player):
to_remove = 'Power Glove' to_remove = 'Power Glove'
else: else:
to_remove = None to_remove = None
if to_remove is not None: if to_remove is not None:
try: try:
self.prog_items.remove(to_remove) self.prog_items.remove((to_remove, item.player))
except ValueError: except ValueError:
return return
@ -582,8 +588,8 @@ class CollectionState(object):
def __getattr__(self, item): def __getattr__(self, item):
if item.startswith('can_reach_'): if item.startswith('can_reach_'):
return self.can_reach(item[10]) return self.can_reach(item[10])
elif item.startswith('has_'): #elif item.startswith('has_'):
return self.has(item[4]) # return self.has(item[4])
raise RuntimeError('Cannot parse %s.' % item) raise RuntimeError('Cannot parse %s.' % item)
@ -602,7 +608,7 @@ class RegionType(Enum):
class Region(object): class Region(object):
def __init__(self, name, type, hint): def __init__(self, name, type, hint, player):
self.name = name self.name = name
self.type = type self.type = type
self.entrances = [] self.entrances = []
@ -616,6 +622,7 @@ class Region(object):
self.spot_type = 'Region' self.spot_type = 'Region'
self.hint_text = hint self.hint_text = hint
self.recursion_count = 0 self.recursion_count = 0
self.player = player
def can_reach(self, state): def can_reach(self, state):
if state.stale: if state.stale:
@ -634,7 +641,7 @@ class Region(object):
is_dungeon_item = item.key or item.map or item.compass is_dungeon_item = item.key or item.map or item.compass
sewer_hack = self.world.mode == 'standard' and item.name == 'Small Key (Escape)' sewer_hack = self.world.mode == 'standard' and item.name == 'Small Key (Escape)'
if sewer_hack or (is_dungeon_item and not self.world.keysanity): if sewer_hack or (is_dungeon_item and not self.world.keysanity):
return self.dungeon and self.dungeon.is_dungeon_item(item) return self.dungeon and self.dungeon.is_dungeon_item(item) and (item.player == self.player or self.world.keysanity)
return True return True
@ -642,12 +649,12 @@ class Region(object):
return str(self.__unicode__()) return str(self.__unicode__())
def __unicode__(self): def __unicode__(self):
return '%s' % self.name return '%s (Player %d)' % (self.name, self.player)
class Entrance(object): class Entrance(object):
def __init__(self, name='', parent=None): def __init__(self, player, name='', parent=None):
self.name = name self.name = name
self.parent_region = parent self.parent_region = parent
self.connected_region = None self.connected_region = None
@ -657,6 +664,7 @@ class Entrance(object):
self.recursion_count = 0 self.recursion_count = 0
self.vanilla = None self.vanilla = None
self.access_rule = lambda state: True self.access_rule = lambda state: True
self.player = player
def can_reach(self, state): def can_reach(self, state):
if state.can_reach(self.parent_region) and self.access_rule(state): if state.can_reach(self.parent_region) and self.access_rule(state):
@ -677,18 +685,19 @@ class Entrance(object):
return str(self.__unicode__()) return str(self.__unicode__())
def __unicode__(self): def __unicode__(self):
return '%s' % self.name return '%s (Player %d)' % (self.name, self.player)
class Dungeon(object): class Dungeon(object):
def __init__(self, name, regions, big_key, small_keys, dungeon_items): def __init__(self, name, regions, big_key, small_keys, dungeon_items, player):
self.name = name self.name = name
self.regions = regions self.regions = regions
self.big_key = big_key self.big_key = big_key
self.small_keys = small_keys self.small_keys = small_keys
self.dungeon_items = dungeon_items self.dungeon_items = dungeon_items
self.bosses = dict() self.bosses = dict()
self.player = player
@property @property
def boss(self): def boss(self):
@ -707,25 +716,26 @@ class Dungeon(object):
return self.dungeon_items + self.keys return self.dungeon_items + self.keys
def is_dungeon_item(self, item): def is_dungeon_item(self, item):
return item.name in [dungeon_item.name for dungeon_item in self.all_items] return item.player == self.player and item.name in [dungeon_item.name for dungeon_item in self.all_items]
def __str__(self): def __str__(self):
return str(self.__unicode__()) return str(self.__unicode__())
def __unicode__(self): def __unicode__(self):
return '%s' % self.name return '%s (Player %d)' % (self.name, self.player)
class Boss(object): class Boss(object):
def __init__(self, name, enemizer_name, defeat_rule): def __init__(self, name, enemizer_name, defeat_rule, player):
self.name = name self.name = name
self.enemizer_name = enemizer_name self.enemizer_name = enemizer_name
self.defeat_rule = defeat_rule self.defeat_rule = defeat_rule
self.player = player
def can_defeat(self, state): def can_defeat(self, state):
return self.defeat_rule(state) return self.defeat_rule(state, self.player)
class Location(object): class Location(object):
def __init__(self, name='', address=None, crystal=False, hint_text=None, parent=None): def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None):
self.name = name self.name = name
self.parent_region = parent self.parent_region = parent
self.item = None self.item = None
@ -739,6 +749,7 @@ class Location(object):
self.always_allow = lambda item, state: False self.always_allow = lambda item, state: False
self.access_rule = lambda state: True self.access_rule = lambda state: True
self.item_rule = lambda item: True self.item_rule = lambda item: True
self.player = player
def can_fill(self, state, item, check_access=True): def can_fill(self, state, item, check_access=True):
return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state))) return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state)))
@ -752,12 +763,12 @@ class Location(object):
return str(self.__unicode__()) return str(self.__unicode__())
def __unicode__(self): def __unicode__(self):
return '%s' % self.name return '%s (Player %d)' % (self.name, self.player)
class Item(object): class Item(object):
def __init__(self, name='', advancement=False, priority=False, type=None, code=None, pedestal_hint=None, pedestal_credit=None, sickkid_credit=None, zora_credit=None, witch_credit=None, fluteboy_credit=None, hint_text=None): def __init__(self, name='', advancement=False, priority=False, type=None, code=None, pedestal_hint=None, pedestal_credit=None, sickkid_credit=None, zora_credit=None, witch_credit=None, fluteboy_credit=None, hint_text=None, player=None):
self.name = name self.name = name
self.advancement = advancement self.advancement = advancement
self.priority = priority self.priority = priority
@ -771,6 +782,7 @@ class Item(object):
self.hint_text = hint_text self.hint_text = hint_text
self.code = code self.code = code
self.location = None self.location = None
self.player = player
@property @property
def key(self): def key(self):
@ -792,7 +804,7 @@ class Item(object):
return str(self.__unicode__()) return str(self.__unicode__())
def __unicode__(self): def __unicode__(self):
return '%s' % self.name return '%s (Player %d)' % (self.name, self.player)
# have 6 address that need to be filled # have 6 address that need to be filled
@ -874,11 +886,14 @@ class Spoiler(object):
self.shops = [] self.shops = []
self.bosses = OrderedDict() self.bosses = OrderedDict()
def set_entrance(self, entrance, exit, direction): def set_entrance(self, entrance, exit, direction, player):
self.entrances[(entrance, direction)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)]) self.entrances[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)])
def parse_data(self): def parse_data(self):
self.medallions = OrderedDict([('Misery Mire', self.world.required_medallions[0]), ('Turtle Rock', self.world.required_medallions[1])]) self.medallions = OrderedDict()
for player in range(1, self.world.players + 1):
self.medallions['Misery Mire (Player %d)' % player] = self.world.required_medallions[player][0]
self.medallions['Turtle Rock (Player %d)' % player] = self.world.required_medallions[player][1]
self.locations = OrderedDict() self.locations = OrderedDict()
listed_locations = set() listed_locations = set()
@ -897,7 +912,7 @@ class Spoiler(object):
for dungeon in self.world.dungeons: for dungeon in self.world.dungeons:
dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon] dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon]
self.locations[dungeon.name] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations]) self.locations[str(dungeon)] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations])
listed_locations.update(dungeon_locations) listed_locations.update(dungeon_locations)
other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations] other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations]
@ -905,10 +920,11 @@ class Spoiler(object):
self.locations['Other Locations'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in other_locations]) self.locations['Other Locations'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in other_locations])
listed_locations.update(other_locations) listed_locations.update(other_locations)
self.shops = []
for shop in self.world.shops: for shop in self.world.shops:
if not shop.active: if not shop.active:
continue continue
shopdata = {'location': shop.region.name, shopdata = {'location': str(shop.region),
'type': 'Take Any' if shop.type == ShopType.TakeAny else 'Shop' 'type': 'Take Any' if shop.type == ShopType.TakeAny else 'Shop'
} }
for index, item in enumerate(shop.inventory): for index, item in enumerate(shop.inventory):
@ -917,22 +933,24 @@ class Spoiler(object):
shopdata['item_{}'.format(index)] = "{}{}".format(item['item'], item['price']) if item['price'] else item['item'] shopdata['item_{}'.format(index)] = "{}{}".format(item['item'], item['price']) if item['price'] else item['item']
self.shops.append(shopdata) self.shops.append(shopdata)
self.bosses["Eastern Palace"] = self.world.get_dungeon("Eastern Palace").boss.name for player in range(1, self.world.players + 1):
self.bosses["Desert Palace"] = self.world.get_dungeon("Desert Palace").boss.name self.bosses[str(player)] = OrderedDict()
self.bosses["Tower Of Hera"] = self.world.get_dungeon("Tower of Hera").boss.name self.bosses[str(player)]["Eastern Palace"] = self.world.get_dungeon("Eastern Palace", player).boss.name
self.bosses["Hyrule Castle"] = "Agahnim" self.bosses[str(player)]["Desert Palace"] = self.world.get_dungeon("Desert Palace", player).boss.name
self.bosses["Palace Of Darkness"] = self.world.get_dungeon("Palace of Darkness").boss.name self.bosses[str(player)]["Tower Of Hera"] = self.world.get_dungeon("Tower of Hera", player).boss.name
self.bosses["Swamp Palace"] = self.world.get_dungeon("Swamp Palace").boss.name self.bosses[str(player)]["Hyrule Castle"] = "Agahnim"
self.bosses["Skull Woods"] = self.world.get_dungeon("Skull Woods").boss.name self.bosses[str(player)]["Palace Of Darkness"] = self.world.get_dungeon("Palace of Darkness", player).boss.name
self.bosses["Thieves Town"] = self.world.get_dungeon("Thieves Town").boss.name self.bosses[str(player)]["Swamp Palace"] = self.world.get_dungeon("Swamp Palace", player).boss.name
self.bosses["Ice Palace"] = self.world.get_dungeon("Ice Palace").boss.name self.bosses[str(player)]["Skull Woods"] = self.world.get_dungeon("Skull Woods", player).boss.name
self.bosses["Misery Mire"] = self.world.get_dungeon("Misery Mire").boss.name self.bosses[str(player)]["Thieves Town"] = self.world.get_dungeon("Thieves Town", player).boss.name
self.bosses["Turtle Rock"] = self.world.get_dungeon("Turtle Rock").boss.name self.bosses[str(player)]["Ice Palace"] = self.world.get_dungeon("Ice Palace", player).boss.name
self.bosses["Ganons Tower Basement"] = self.world.get_dungeon('Ganons Tower').bosses['bottom'].name self.bosses[str(player)]["Misery Mire"] = self.world.get_dungeon("Misery Mire", player).boss.name
self.bosses["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower').bosses['middle'].name self.bosses[str(player)]["Turtle Rock"] = self.world.get_dungeon("Turtle Rock", player).boss.name
self.bosses["Ganons Tower Top"] = self.world.get_dungeon('Ganons Tower').bosses['top'].name self.bosses[str(player)]["Ganons Tower Basement"] = self.world.get_dungeon('Ganons Tower', player).bosses['bottom'].name
self.bosses["Ganons Tower"] = "Agahnim 2" self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower', player).bosses['middle'].name
self.bosses["Ganon"] = "Ganon" self.bosses[str(player)]["Ganons Tower Top"] = self.world.get_dungeon('Ganons Tower', player).bosses['top'].name
self.bosses[str(player)]["Ganons Tower"] = "Agahnim 2"
self.bosses[str(player)]["Ganon"] = "Ganon"
from Main import __version__ as ERVersion from Main import __version__ as ERVersion
@ -951,7 +969,8 @@ class Spoiler(object):
'quickswap': self.world.quickswap, 'quickswap': self.world.quickswap,
'fastmenu': self.world.fastmenu, 'fastmenu': self.world.fastmenu,
'disable_music': self.world.disable_music, 'disable_music': self.world.disable_music,
'keysanity': self.world.keysanity} 'keysanity': self.world.keysanity,
'players': self.world.players}
def to_json(self): def to_json(self):
self.parse_data() self.parse_data()
@ -982,13 +1001,15 @@ class Spoiler(object):
outfile.write('Maps and Compasses in Dungeons: %s\n' % ('Yes' if self.metadata['dungeonitems'] else 'No')) outfile.write('Maps and Compasses in Dungeons: %s\n' % ('Yes' if self.metadata['dungeonitems'] else 'No'))
outfile.write('L\\R Quickswap enabled: %s\n' % ('Yes' if self.metadata['quickswap'] else 'No')) outfile.write('L\\R Quickswap enabled: %s\n' % ('Yes' if self.metadata['quickswap'] else 'No'))
outfile.write('Menu speed: %s\n' % self.metadata['fastmenu']) outfile.write('Menu speed: %s\n' % self.metadata['fastmenu'])
outfile.write('Keysanity enabled: %s' % ('Yes' if self.metadata['keysanity'] else 'No')) outfile.write('Keysanity enabled: %s\n' % ('Yes' if self.metadata['keysanity'] else 'No'))
outfile.write('Players: %d' % self.metadata['players'])
if self.entrances: if self.entrances:
outfile.write('\n\nEntrances:\n\n') outfile.write('\n\nEntrances:\n\n')
outfile.write('\n'.join(['%s %s %s' % (entry['entrance'], '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', entry['exit']) for entry in self.entrances.values()])) outfile.write('\n'.join(['Player %d: %s %s %s' % (entry['player'], entry['entrance'], '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', entry['exit']) for entry in self.entrances.values()]))
outfile.write('\n\nMedallions') outfile.write('\n\nMedallions\n')
outfile.write('\n\nMisery Mire Medallion: %s' % self.medallions['Misery Mire']) for player in range(1, self.world.players + 1):
outfile.write('\nTurtle Rock Medallion: %s' % self.medallions['Turtle Rock']) outfile.write('\nMisery Mire Medallion (Player %d): %s' % (player, self.medallions['Misery Mire (Player %d)' % player]))
outfile.write('\nTurtle Rock Medallion (Player %d): %s' % (player, self.medallions['Turtle Rock (Player %d)' % player]))
outfile.write('\n\nLocations:\n\n') outfile.write('\n\nLocations:\n\n')
outfile.write('\n'.join(['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in grouping.items()])) outfile.write('\n'.join(['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in grouping.items()]))
outfile.write('\n\nShops:\n\n') outfile.write('\n\nShops:\n\n')

104
Bosses.py
View File

@ -4,101 +4,101 @@ import random
from BaseClasses import Boss from BaseClasses import Boss
from Fill import FillError from Fill import FillError
def BossFactory(boss): def BossFactory(boss, player):
if boss is None: if boss is None:
return None return None
if boss in boss_table: if boss in boss_table:
enemizer_name, defeat_rule = boss_table[boss] enemizer_name, defeat_rule = boss_table[boss]
return Boss(boss, enemizer_name, defeat_rule) return Boss(boss, enemizer_name, defeat_rule, player)
logging.getLogger('').error('Unknown Boss: %s', boss) logging.getLogger('').error('Unknown Boss: %s', boss)
return None return None
def ArmosKnightsDefeatRule(state): def ArmosKnightsDefeatRule(state, player):
# Magic amounts are probably a bit overkill # Magic amounts are probably a bit overkill
return ( return (
state.has_blunt_weapon() or state.has_blunt_weapon(player) or
(state.has('Cane of Somaria') and state.can_extend_magic(10)) or (state.has('Cane of Somaria', player) and state.can_extend_magic(player, 10)) or
(state.has('Cane of Byrna') and state.can_extend_magic(16)) or (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or
(state.has('Ice Rod') and state.can_extend_magic(32)) or (state.has('Ice Rod', player) and state.can_extend_magic(player, 32)) or
(state.has('Fire Rod') and state.can_extend_magic(32)) or (state.has('Fire Rod', player) and state.can_extend_magic(player, 32)) or
state.has('Blue Boomerang') or state.has('Blue Boomerang', player) or
state.has('Red Boomerang')) state.has('Red Boomerang', player))
def LanmolasDefeatRule(state): def LanmolasDefeatRule(state, player):
# TODO: Allow the canes here? # TODO: Allow the canes here?
return ( return (
state.has_blunt_weapon() or state.has_blunt_weapon(player) or
state.has('Fire Rod') or state.has('Fire Rod', player) or
state.has('Ice Rod') or state.has('Ice Rod', player) or
state.can_shoot_arrows()) state.can_shoot_arrows(player))
def MoldormDefeatRule(state): def MoldormDefeatRule(state, player):
return state.has_blunt_weapon() return state.has_blunt_weapon(player)
def HelmasaurKingDefeatRule(state): def HelmasaurKingDefeatRule(state, player):
return state.has_blunt_weapon() or state.can_shoot_arrows() return state.has_blunt_weapon(player) or state.can_shoot_arrows(player)
def ArrghusDefeatRule(state): def ArrghusDefeatRule(state, player):
if not state.has('Hookshot'): if not state.has('Hookshot', player):
return False return False
# TODO: ideally we would have a check for bow and silvers, which combined with the # TODO: ideally we would have a check for bow and silvers, which combined with the
# hookshot is enough. This is not coded yet because the silvers that only work in pyramid feature # hookshot is enough. This is not coded yet because the silvers that only work in pyramid feature
# makes this complicated # makes this complicated
if state.has_blunt_weapon(): if state.has_blunt_weapon(player):
return True return True
return ((state.has('Fire Rod') and (state.can_shoot_arrows() or state.can_extend_magic(12))) or #assuming mostly gitting two puff with one shot return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 12))) or #assuming mostly gitting two puff with one shot
(state.has('Ice Rod') and (state.can_shoot_arrows() or state.can_extend_magic(16)))) (state.has('Ice Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16))))
def MothulaDefeatRule(state): def MothulaDefeatRule(state, player):
return ( return (
state.has_blunt_weapon() or state.has_blunt_weapon(player) or
(state.has('Fire Rod') and state.can_extend_magic(10)) or (state.has('Fire Rod', player) and state.can_extend_magic(player, 10)) or
# TODO: Not sure how much (if any) extend magic is needed for these two, since they only apply # TODO: Not sure how much (if any) extend magic is needed for these two, since they only apply
# to non-vanilla locations, so are harder to test, so sticking with what VT has for now: # to non-vanilla locations, so are harder to test, so sticking with what VT has for now:
(state.has('Cane of Somaria') and state.can_extend_magic(16)) or (state.has('Cane of Somaria', player) and state.can_extend_magic(player, 16)) or
(state.has('Cane of Byrna') and state.can_extend_magic(16)) or (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or
state.can_get_good_bee() state.can_get_good_bee(player)
) )
def BlindDefeatRule(state): def BlindDefeatRule(state, player):
return state.has_blunt_weapon() or state.has('Cane of Somaria') or state.has('Cane of Byrna') return state.has_blunt_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player)
def KholdstareDefeatRule(state): def KholdstareDefeatRule(state, player):
return ( return (
( (
state.has('Fire Rod') or state.has('Fire Rod', player) or
( (
state.has('Bombos') and state.has('Bombos', player) and
# FIXME: the following only actually works for the vanilla location for swordless mode # FIXME: the following only actually works for the vanilla location for swordless mode
(state.has_sword() or state.world.mode == 'swordless') (state.has_sword(player) or state.world.mode == 'swordless')
) )
) and ) and
( (
state.has_blunt_weapon() or state.has_blunt_weapon(player) or
(state.has('Fire Rod') and state.can_extend_magic(20)) or (state.has('Fire Rod', player) and state.can_extend_magic(player, 20)) or
# FIXME: this actually only works for the vanilla location for swordless mode # FIXME: this actually only works for the vanilla location for swordless mode
( (
state.has('Fire Rod') and state.has('Fire Rod', player) and
state.has('Bombos') and state.has('Bombos', player) and
state.world.mode == 'swordless' and state.world.mode == 'swordless' and
state.can_extend_magic(16) state.can_extend_magic(player, 16)
) )
) )
) )
def VitreousDefeatRule(state): def VitreousDefeatRule(state, player):
return state.can_shoot_arrows() or state.has_blunt_weapon() return state.can_shoot_arrows(player) or state.has_blunt_weapon(player)
def TrinexxDefeatRule(state): def TrinexxDefeatRule(state, player):
if not (state.has('Fire Rod') and state.has('Ice Rod')): if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)):
return False return False
return state.has('Hammer') or state.has_beam_sword() or (state.has_sword() and state.can_extend_magic(32)) return state.has('Hammer', player) or state.has_beam_sword(player) or (state.has_sword(player) and state.can_extend_magic(player, 32))
def AgahnimDefeatRule(state): def AgahnimDefeatRule(state, player):
return state.has_sword() or state.has('Hammer') or state.has('Bug Catching Net') return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player)
boss_table = { boss_table = {
'Armos Knights': ('Armos', ArmosKnightsDefeatRule), 'Armos Knights': ('Armos', ArmosKnightsDefeatRule),
@ -137,7 +137,7 @@ def can_place_boss(world, boss, dungeon_name, level=None):
return False return False
return True return True
def place_bosses(world): def place_bosses(world, player):
if world.boss_shuffle == 'none': if world.boss_shuffle == 'none':
return return
# Most to least restrictive order # Most to least restrictive order
@ -162,7 +162,7 @@ def place_bosses(world):
if world.boss_shuffle in ["basic", "normal"]: if world.boss_shuffle in ["basic", "normal"]:
# temporary hack for swordless kholdstare: # temporary hack for swordless kholdstare:
if world.mode == 'swordless': if world.mode == 'swordless':
world.get_dungeon('Ice Palace').boss = BossFactory('Kholdstare') world.get_dungeon('Ice Palace', player).boss = BossFactory('Kholdstare', player)
logging.getLogger('').debug('Placing boss Kholdstare at Ice Palace') logging.getLogger('').debug('Placing boss Kholdstare at Ice Palace')
boss_locations.remove(['Ice Palace', None]) boss_locations.remove(['Ice Palace', None])
placeable_bosses.remove('Kholdstare') placeable_bosses.remove('Kholdstare')
@ -183,7 +183,7 @@ def place_bosses(world):
bosses.remove(boss) bosses.remove(boss)
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text) logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
world.get_dungeon(loc).bosses[level] = BossFactory(boss) world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player)
elif world.boss_shuffle == "chaos": #all bosses chosen at random elif world.boss_shuffle == "chaos": #all bosses chosen at random
for [loc, level] in boss_locations: for [loc, level] in boss_locations:
loc_text = loc + (' ('+level+')' if level else '') loc_text = loc + (' ('+level+')' if level else '')
@ -193,4 +193,4 @@ def place_bosses(world):
raise FillError('Could not place boss for location %s' % loc_text) raise FillError('Could not place boss for location %s' % loc_text)
logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text) logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text)
world.get_dungeon(loc).bosses[level] = BossFactory(boss) world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player)

View File

@ -6,44 +6,45 @@ from Fill import fill_restrictive
from Items import ItemFactory from Items import ItemFactory
def create_dungeons(world): def create_dungeons(world, player):
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items): def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro else small_keys, dungeon_items) dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro else small_keys, dungeon_items, player)
dungeon.boss = BossFactory(default_boss) dungeon.boss = BossFactory(default_boss, player)
for region in dungeon.regions: for region in dungeon.regions:
world.get_region(region).dungeon = dungeon world.get_region(region, player).dungeon = dungeon
return dungeon return dungeon
ES = make_dungeon('Hyrule Castle', None, ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'], None, [ItemFactory('Small Key (Escape)')], [ItemFactory('Map (Escape)')]) ES = make_dungeon('Hyrule Castle', None, ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'], None, [ItemFactory('Small Key (Escape)', player)], [ItemFactory('Map (Escape)', player)])
EP = make_dungeon('Eastern Palace', 'Armos Knights', ['Eastern Palace'], ItemFactory('Big Key (Eastern Palace)'), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'])) EP = make_dungeon('Eastern Palace', 'Armos Knights', ['Eastern Palace'], ItemFactory('Big Key (Eastern Palace)', player), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], player))
DP = make_dungeon('Desert Palace', 'Lanmolas', ['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)', 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)'), [ItemFactory('Small Key (Desert Palace)')], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'])) DP = make_dungeon('Desert Palace', 'Lanmolas', ['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)', 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)', player), [ItemFactory('Small Key (Desert Palace)', player)], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
ToH = make_dungeon('Tower of Hera', 'Moldorm', ['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'], ItemFactory('Big Key (Tower of Hera)'), [ItemFactory('Small Key (Tower of Hera)')], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'])) ToH = make_dungeon('Tower of Hera', 'Moldorm', ['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'], ItemFactory('Big Key (Tower of Hera)', player), [ItemFactory('Small Key (Tower of Hera)', player)], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2), []) AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
PoD = make_dungeon('Palace of Darkness', 'Helmasaur King', ['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)', 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)', 'Palace of Darkness (North)', 'Palace of Darkness (Maze)', 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'], ItemFactory('Big Key (Palace of Darkness)'), ItemFactory(['Small Key (Palace of Darkness)'] * 6), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'])) PoD = make_dungeon('Palace of Darkness', 'Helmasaur King', ['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)', 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)', 'Palace of Darkness (North)', 'Palace of Darkness (Maze)', 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'], ItemFactory('Big Key (Palace of Darkness)', player), ItemFactory(['Small Key (Palace of Darkness)'] * 6, player), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], player))
TT = make_dungeon('Thieves Town', 'Blind', ['Thieves Town (Entrance)', 'Thieves Town (Deep)', 'Blind Fight'], ItemFactory('Big Key (Thieves Town)'), [ItemFactory('Small Key (Thieves Town)')], ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'])) TT = make_dungeon('Thieves Town', 'Blind', ['Thieves Town (Entrance)', 'Thieves Town (Deep)', 'Blind Fight'], ItemFactory('Big Key (Thieves Town)', player), [ItemFactory('Small Key (Thieves Town)', player)], ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'], player))
SW = make_dungeon('Skull Woods', 'Mothula', ['Skull Woods Final Section (Entrance)', 'Skull Woods First Section', 'Skull Woods Second Section', 'Skull Woods Second Section (Drop)', 'Skull Woods Final Section (Mothula)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)'], ItemFactory('Big Key (Skull Woods)'), ItemFactory(['Small Key (Skull Woods)'] * 2), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'])) SW = make_dungeon('Skull Woods', 'Mothula', ['Skull Woods Final Section (Entrance)', 'Skull Woods First Section', 'Skull Woods Second Section', 'Skull Woods Second Section (Drop)', 'Skull Woods Final Section (Mothula)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)'], ItemFactory('Big Key (Skull Woods)', player), ItemFactory(['Small Key (Skull Woods)'] * 2, player), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player))
SP = make_dungeon('Swamp Palace', 'Arrghus', ['Swamp Palace (Entrance)', 'Swamp Palace (First Room)', 'Swamp Palace (Starting Area)', 'Swamp Palace (Center)', 'Swamp Palace (North)'], ItemFactory('Big Key (Swamp Palace)'), [ItemFactory('Small Key (Swamp Palace)')], ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'])) SP = make_dungeon('Swamp Palace', 'Arrghus', ['Swamp Palace (Entrance)', 'Swamp Palace (First Room)', 'Swamp Palace (Starting Area)', 'Swamp Palace (Center)', 'Swamp Palace (North)'], ItemFactory('Big Key (Swamp Palace)', player), [ItemFactory('Small Key (Swamp Palace)', player)], ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], player))
IP = make_dungeon('Ice Palace', 'Kholdstare', ['Ice Palace (Entrance)', 'Ice Palace (Main)', 'Ice Palace (East)', 'Ice Palace (East Top)', 'Ice Palace (Kholdstare)'], ItemFactory('Big Key (Ice Palace)'), ItemFactory(['Small Key (Ice Palace)'] * 2), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'])) IP = make_dungeon('Ice Palace', 'Kholdstare', ['Ice Palace (Entrance)', 'Ice Palace (Main)', 'Ice Palace (East)', 'Ice Palace (East Top)', 'Ice Palace (Kholdstare)'], ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
MM = make_dungeon('Misery Mire', 'Vitreous', ['Misery Mire (Entrance)', 'Misery Mire (Main)', 'Misery Mire (West)', 'Misery Mire (Final Area)', 'Misery Mire (Vitreous)'], ItemFactory('Big Key (Misery Mire)'), ItemFactory(['Small Key (Misery Mire)'] * 3), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'])) MM = make_dungeon('Misery Mire', 'Vitreous', ['Misery Mire (Entrance)', 'Misery Mire (Main)', 'Misery Mire (West)', 'Misery Mire (Final Area)', 'Misery Mire (Vitreous)'], ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
TR = make_dungeon('Turtle Rock', 'Trinexx', ['Turtle Rock (Entrance)', 'Turtle Rock (First Section)', 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Turtle Rock (Crystaroller Room)', 'Turtle Rock (Dark Room)', 'Turtle Rock (Eye Bridge)', 'Turtle Rock (Trinexx)'], ItemFactory('Big Key (Turtle Rock)'), ItemFactory(['Small Key (Turtle Rock)'] * 4), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'])) TR = make_dungeon('Turtle Rock', 'Trinexx', ['Turtle Rock (Entrance)', 'Turtle Rock (First Section)', 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Turtle Rock (Crystaroller Room)', 'Turtle Rock (Dark Room)', 'Turtle Rock (Eye Bridge)', 'Turtle Rock (Trinexx)'], ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
GT = make_dungeon('Ganons Tower', 'Agahnim2', ['Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)', 'Ganons Tower (Compass Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)', 'Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)', 'Ganons Tower (Top)', 'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)', 'Agahnim 2'], ItemFactory('Big Key (Ganons Tower)'), ItemFactory(['Small Key (Ganons Tower)'] * 4), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'])) GT = make_dungeon('Ganons Tower', 'Agahnim2', ['Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)', 'Ganons Tower (Compass Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)', 'Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)', 'Ganons Tower (Top)', 'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)', 'Agahnim 2'], ItemFactory('Big Key (Ganons Tower)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
GT.bosses['bottom'] = BossFactory('Armos Knights') GT.bosses['bottom'] = BossFactory('Armos Knights', player)
GT.bosses['middle'] = BossFactory('Lanmolas') GT.bosses['middle'] = BossFactory('Lanmolas', player)
GT.bosses['top'] = BossFactory('Moldorm') GT.bosses['top'] = BossFactory('Moldorm', player)
world.dungeons = [ES, EP, DP, ToH, AT, PoD, TT, SW, SP, IP, MM, TR, GT] world.dungeons += [ES, EP, DP, ToH, AT, PoD, TT, SW, SP, IP, MM, TR, GT]
def fill_dungeons(world): def fill_dungeons(world):
freebes = ['Ganons Tower - Map Chest', 'Palace of Darkness - Harmless Hellway', 'Palace of Darkness - Big Key Chest', 'Turtle Rock - Big Key Chest'] freebes = ['Ganons Tower - Map Chest', 'Palace of Darkness - Harmless Hellway', 'Palace of Darkness - Big Key Chest', 'Turtle Rock - Big Key Chest']
all_state_base = world.get_all_state() all_state_base = world.get_all_state()
if world.retro: for player in range(1, world.players + 1):
world.push_item(world.get_location('Skull Woods - Pinball Room'), ItemFactory('Small Key (Universal)'), False) if world.retro:
else: world.push_item(world.get_location('Skull Woods - Pinball Room', player), ItemFactory('Small Key (Universal)', player), False)
world.push_item(world.get_location('Skull Woods - Pinball Room'), ItemFactory('Small Key (Skull Woods)'), False) else:
world.get_location('Skull Woods - Pinball Room').event = True world.push_item(world.get_location('Skull Woods - Pinball Room', player), ItemFactory('Small Key (Skull Woods)', player), False)
world.get_location('Skull Woods - Pinball Room', player).event = True
dungeons = [(list(dungeon.regions), dungeon.big_key, list(dungeon.small_keys), list(dungeon.dungeon_items)) for dungeon in world.dungeons] dungeons = [(list(dungeon.regions), dungeon.big_key, list(dungeon.small_keys), list(dungeon.dungeon_items)) for dungeon in world.dungeons]
@ -88,7 +89,7 @@ def fill_dungeons(world):
small_keys.append(small_key) small_keys.append(small_key)
dungeons.append((dungeon_regions, big_key, small_keys, dungeon_items)) dungeons.append((dungeon_regions, big_key, small_keys, dungeon_items))
# infinite regression protection # infinite regression protection
if loopcnt < 30: if loopcnt < (30 * world.players):
break break
else: else:
raise RuntimeError('No suitable location for %s' % small_key) raise RuntimeError('No suitable location for %s' % small_key)
@ -114,13 +115,14 @@ def get_dungeon_item_pool(world):
def fill_dungeons_restrictive(world, shuffled_locations): def fill_dungeons_restrictive(world, shuffled_locations):
all_state_base = world.get_all_state() all_state_base = world.get_all_state()
skull_woods_big_chest = world.get_location('Skull Woods - Pinball Room') for player in range(1, world.players + 1):
if world.retro: skull_woods_big_chest = world.get_location('Skull Woods - Pinball Room', player)
world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Universal)'), False) if world.retro:
else: world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Universal)', player), False)
world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)'), False) else:
skull_woods_big_chest.event = True world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)', player), False)
shuffled_locations.remove(skull_woods_big_chest) skull_woods_big_chest.event = True
shuffled_locations.remove(skull_woods_big_chest)
if world.keysanity: if world.keysanity:
#in keysanity dungeon items are distributed as part of the normal item pool #in keysanity dungeon items are distributed as part of the normal item pool

View File

@ -8,7 +8,7 @@ import sys
from Gui import guiMain from Gui import guiMain
from Main import main from Main import main
from Utils import is_bundled, close_console from Utils import is_bundled, close_console, output_path
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
@ -213,8 +213,15 @@ def start():
Output .json patch to stdout instead of a patched rom. Used Output .json patch to stdout instead of a patched rom. Used
for VT site integration, do not use otherwise. for VT site integration, do not use otherwise.
''') ''')
parser.add_argument('--multi', default=1, type=lambda value: min(max(int(value), 1), 255))
parser.add_argument('--skip_playthrough', action='store_true', default=False)
parser.add_argument('--outputpath')
args = parser.parse_args() args = parser.parse_args()
if args.outputpath and os.path.isdir(args.outputpath):
output_path.cached_path = args.outputpath
if is_bundled() and len(sys.argv) == 1: if is_bundled() and len(sys.argv) == 1:
# for the bundled builds, if we have no arguments, the user # for the bundled builds, if we have no arguments, the user
# probably wants the gui. Users of the bundled build who want the command line # probably wants the gui. Users of the bundled build who want the command line

File diff suppressed because it is too large Load Diff

162
Fill.py
View File

@ -1,6 +1,9 @@
import random import random
import logging import logging
from BaseClasses import CollectionState
class FillError(RuntimeError): class FillError(RuntimeError):
pass pass
@ -167,31 +170,41 @@ def fill_restrictive(world, base_state, locations, itempool):
return new_state return new_state
while itempool and locations: while itempool and locations:
item_to_place = itempool.pop() items_to_place = []
nextpool = []
placing_players = set()
for item in reversed(itempool):
if item.player not in placing_players:
placing_players.add(item.player)
items_to_place.append(item)
else:
nextpool.insert(0, item)
itempool = nextpool
maximum_exploration_state = sweep_from_pool() maximum_exploration_state = sweep_from_pool()
perform_access_check = True perform_access_check = True
if world.check_beatable_only: if world.check_beatable_only:
perform_access_check = not world.has_beaten_game(maximum_exploration_state) perform_access_check = not world.has_beaten_game(maximum_exploration_state)
for item_to_place in items_to_place:
spot_to_fill = None
for location in locations:
if location.can_fill(maximum_exploration_state, item_to_place, perform_access_check):
spot_to_fill = location
break
spot_to_fill = None if spot_to_fill is None:
for location in locations: # we filled all reachable spots. Maybe the game can be beaten anyway?
if location.can_fill(maximum_exploration_state, item_to_place, perform_access_check): if world.can_beat_game():
spot_to_fill = location if not world.check_beatable_only:
break logging.getLogger('').warning('Not all items placed. Game beatable anyway. (Could not place %s)' % item_to_place)
continue
raise FillError('No more spots to place %s' % item_to_place)
if spot_to_fill is None: world.push_item(spot_to_fill, item_to_place, False)
# we filled all reachable spots. Maybe the game can be beaten anyway? locations.remove(spot_to_fill)
if world.can_beat_game(): spot_to_fill.event = True
if not world.check_beatable_only:
logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
break
raise FillError('No more spots to place %s' % item_to_place)
world.push_item(spot_to_fill, item_to_place, False)
locations.remove(spot_to_fill)
spot_to_fill.event = True
def distribute_items_restrictive(world, gftower_trash_count=0, fill_locations=None): def distribute_items_restrictive(world, gftower_trash_count=0, fill_locations=None):
@ -207,20 +220,25 @@ def distribute_items_restrictive(world, gftower_trash_count=0, fill_locations=No
restitempool = [item for item in world.itempool if not item.advancement and not item.priority] restitempool = [item for item in world.itempool if not item.advancement and not item.priority]
# fill in gtower locations with trash first # fill in gtower locations with trash first
if world.ganonstower_vanilla: for player in range(1, world.players + 1):
gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name] if world.ganonstower_vanilla[player]:
random.shuffle(gtower_locations) gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player]
trashcnt = 0 random.shuffle(gtower_locations)
while gtower_locations and restitempool and trashcnt < gftower_trash_count: trashcnt = 0
spot_to_fill = gtower_locations.pop() while gtower_locations and restitempool and trashcnt < gftower_trash_count:
item_to_place = restitempool.pop() spot_to_fill = gtower_locations.pop()
world.push_item(spot_to_fill, item_to_place, False) item_to_place = restitempool.pop()
fill_locations.remove(spot_to_fill) world.push_item(spot_to_fill, item_to_place, False)
trashcnt += 1 fill_locations.remove(spot_to_fill)
trashcnt += 1
random.shuffle(fill_locations) random.shuffle(fill_locations)
fill_locations.reverse() fill_locations.reverse()
# Make sure the escape small key is placed first in standard keysanity to prevent running out of spots
if world.keysanity and world.mode == 'standard':
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' else 0)
fill_restrictive(world, world.state, fill_locations, progitempool) fill_restrictive(world, world.state, fill_locations, progitempool)
random.shuffle(fill_locations) random.shuffle(fill_locations)
@ -297,3 +315,93 @@ def flood_items(world):
world.push_item(location, item_to_place, True) world.push_item(location, item_to_place, True)
itempool.remove(item_to_place) itempool.remove(item_to_place)
break break
def balance_multiworld_progression(world):
state = CollectionState(world)
checked_locations = []
unchecked_locations = world.get_locations().copy()
random.shuffle(unchecked_locations)
reachable_locations_count = {}
for player in range(1, world.players + 1):
reachable_locations_count[player] = 0
def get_sphere_locations(sphere_state, locations):
if not world.keysanity:
sphere_state.sweep_for_events(key_only=True, locations=locations)
return [loc for loc in locations if sphere_state.can_reach(loc)]
while True:
sphere_locations = get_sphere_locations(state, unchecked_locations)
for location in sphere_locations:
unchecked_locations.remove(location)
reachable_locations_count[location.player] += 1
if checked_locations:
average_reachable_locations = sum(reachable_locations_count.values()) / world.players
threshold = ((average_reachable_locations + max(reachable_locations_count.values())) / 2) * 0.8 #todo: probably needs some tweaking
balancing_players = [player for player, reachables in reachable_locations_count.items() if reachables < threshold]
if balancing_players:
balancing_state = state.copy()
balancing_unchecked_locations = unchecked_locations.copy()
balancing_reachables = reachable_locations_count.copy()
balancing_sphere = sphere_locations.copy()
candidate_items = []
while True:
for location in balancing_sphere:
if location.event:
balancing_state.collect(location.item, True, location)
if location.item.player in balancing_players:
candidate_items.append(location)
balancing_sphere = get_sphere_locations(balancing_state, balancing_unchecked_locations)
for location in balancing_sphere:
balancing_unchecked_locations.remove(location)
balancing_reachables[location.player] += 1
if world.has_beaten_game(balancing_state) or all([reachables >= threshold for reachables in balancing_reachables.values()]):
break
unlocked_locations = [l for l in unchecked_locations if l not in balancing_unchecked_locations]
items_to_replace = []
for player in balancing_players:
locations_to_test = [l for l in unlocked_locations if l.player == player]
items_to_test = [l for l in candidate_items if l.item.player == player and l.player != player]
while items_to_test:
testing = items_to_test.pop()
reducing_state = state.copy()
for location in [*[l for l in items_to_replace if l.item.player == player], *items_to_test]:
reducing_state.collect(location.item, True, location)
reducing_state.sweep_for_events(locations=locations_to_test)
if world.has_beaten_game(balancing_state):
if not world.has_beaten_game(reducing_state):
items_to_replace.append(testing)
else:
reduced_sphere = get_sphere_locations(reducing_state, locations_to_test)
if reachable_locations_count[player] + len(reduced_sphere) < threshold:
items_to_replace.append(testing)
replaced_items = False
locations_for_replacing = [l for l in checked_locations if not l.event]
while locations_for_replacing and items_to_replace:
new_location = locations_for_replacing.pop()
old_location = items_to_replace.pop()
new_location.item, old_location.item = old_location.item, new_location.item
new_location.event = True
old_location.event = False
state.collect(new_location.item, True, new_location)
replaced_items = True
if replaced_items:
for location in get_sphere_locations(state, [l for l in unlocked_locations if l.player in balancing_players]):
unchecked_locations.remove(location)
reachable_locations_count[location.player] += 1
sphere_locations.append(location)
for location in sphere_locations:
if location.event:
state.collect(location.item, True, location)
checked_locations.extend(sphere_locations)
if world.has_beaten_game(state):
break

16
Gui.py
View File

@ -244,15 +244,20 @@ def guiMain(args=None):
bottomFrame = Frame(randomizerWindow) bottomFrame = Frame(randomizerWindow)
worldLabel = Label(bottomFrame, text='Worlds')
worldVar = StringVar()
worldSpinbox = Spinbox(bottomFrame, from_=1, to=100, width=5, textvariable=worldVar)
seedLabel = Label(bottomFrame, text='Seed #') seedLabel = Label(bottomFrame, text='Seed #')
seedVar = StringVar() seedVar = StringVar()
seedEntry = Entry(bottomFrame, textvariable=seedVar) seedEntry = Entry(bottomFrame, width=15, textvariable=seedVar)
countLabel = Label(bottomFrame, text='Count') countLabel = Label(bottomFrame, text='Count')
countVar = StringVar() countVar = StringVar()
countSpinbox = Spinbox(bottomFrame, from_=1, to=100, textvariable=countVar) countSpinbox = Spinbox(bottomFrame, from_=1, to=100, width=5, textvariable=countVar)
def generateRom(): def generateRom():
guiargs = Namespace guiargs = Namespace
guiargs.multi = int(worldVar.get())
guiargs.seed = int(seedVar.get()) if seedVar.get() else None guiargs.seed = int(seedVar.get()) if seedVar.get() else None
guiargs.count = int(countVar.get()) if countVar.get() != '1' else None guiargs.count = int(countVar.get()) if countVar.get() != '1' else None
guiargs.mode = modeVar.get() guiargs.mode = modeVar.get()
@ -290,6 +295,8 @@ def guiMain(args=None):
guiargs.rom = romVar.get() guiargs.rom = romVar.get()
guiargs.jsonout = None guiargs.jsonout = None
guiargs.sprite = sprite guiargs.sprite = sprite
guiargs.skip_playthrough = False
guiargs.outputpath = None
try: try:
if guiargs.count is not None: if guiargs.count is not None:
seed = guiargs.seed seed = guiargs.seed
@ -305,7 +312,9 @@ def guiMain(args=None):
generateButton = Button(bottomFrame, text='Generate Patched Rom', command=generateRom) generateButton = Button(bottomFrame, text='Generate Patched Rom', command=generateRom)
seedLabel.pack(side=LEFT) worldLabel.pack(side=LEFT)
worldSpinbox.pack(side=LEFT)
seedLabel.pack(side=LEFT, padx=(5, 0))
seedEntry.pack(side=LEFT) seedEntry.pack(side=LEFT)
countLabel.pack(side=LEFT, padx=(5, 0)) countLabel.pack(side=LEFT, padx=(5, 0))
countSpinbox.pack(side=LEFT) countSpinbox.pack(side=LEFT)
@ -384,6 +393,7 @@ def guiMain(args=None):
fastMenuLabel2 = Label(fastMenuFrame2, text='Menu speed') fastMenuLabel2 = Label(fastMenuFrame2, text='Menu speed')
fastMenuLabel2.pack(side=LEFT) fastMenuLabel2.pack(side=LEFT)
heartbeepFrame2.pack(expand=True, anchor=E) heartbeepFrame2.pack(expand=True, anchor=E)
heartcolorFrame2.pack(expand=True, anchor=E) heartcolorFrame2.pack(expand=True, anchor=E)
fastMenuFrame2.pack(expand=True, anchor=E) fastMenuFrame2.pack(expand=True, anchor=E)

View File

@ -207,7 +207,7 @@ difficulties = {
), ),
} }
def generate_itempool(world): def generate_itempool(world, player):
if (world.difficulty not in ['easy', 'normal', 'hard', 'expert', 'insane'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'] if (world.difficulty not in ['easy', 'normal', 'hard', 'expert', 'insane'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals']
or world.mode not in ['open', 'standard', 'swordless'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']): or world.mode not in ['open', 'standard', 'swordless'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
raise NotImplementedError('Not supported yet') raise NotImplementedError('Not supported yet')
@ -215,20 +215,20 @@ def generate_itempool(world):
if world.timer in ['ohko', 'timed-ohko']: if world.timer in ['ohko', 'timed-ohko']:
world.can_take_damage = False world.can_take_damage = False
world.push_item('Ganon', ItemFactory('Triforce'), False) world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False)
world.get_location('Ganon').event = True world.get_location('Ganon', player).event = True
world.push_item('Agahnim 1', ItemFactory('Beat Agahnim 1'), False) world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False)
world.get_location('Agahnim 1').event = True world.get_location('Agahnim 1', player).event = True
world.push_item('Agahnim 2', ItemFactory('Beat Agahnim 2'), False) world.push_item(world.get_location('Agahnim 2', player), ItemFactory('Beat Agahnim 2', player), False)
world.get_location('Agahnim 2').event = True world.get_location('Agahnim 2', player).event = True
world.push_item('Dark Blacksmith Ruins', ItemFactory('Pick Up Purple Chest'), False) world.push_item(world.get_location('Dark Blacksmith Ruins', player), ItemFactory('Pick Up Purple Chest', player), False)
world.get_location('Dark Blacksmith Ruins').event = True world.get_location('Dark Blacksmith Ruins', player).event = True
world.push_item('Frog', ItemFactory('Get Frog'), False) world.push_item(world.get_location('Frog', player), ItemFactory('Get Frog', player), False)
world.get_location('Frog').event = True world.get_location('Frog', player).event = True
world.push_item('Missing Smith', ItemFactory('Return Smith'), False) world.push_item(world.get_location('Missing Smith', player), ItemFactory('Return Smith', player), False)
world.get_location('Missing Smith').event = True world.get_location('Missing Smith', player).event = True
world.push_item('Floodgate', ItemFactory('Open Floodgate'), False) world.push_item(world.get_location('Floodgate', player), ItemFactory('Open Floodgate', player), False)
world.get_location('Floodgate').event = True world.get_location('Floodgate', player).event = True
# set up item pool # set up item pool
if world.custom: if world.custom:
@ -236,10 +236,10 @@ def generate_itempool(world):
world.rupoor_cost = min(world.customitemarray[67], 9999) world.rupoor_cost = min(world.customitemarray[67], 9999)
else: else:
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro) (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro)
world.itempool = ItemFactory(pool) world.itempool += ItemFactory(pool, player)
for (location, item) in placed_items: for (location, item) in placed_items:
world.push_item(location, ItemFactory(item), False) world.push_item(world.get_location(location, player), ItemFactory(item, player), False)
world.get_location(location).event = True world.get_location(location, player).event = True
world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms
if clock_mode is not None: if clock_mode is not None:
world.clock_mode = clock_mode world.clock_mode = clock_mode
@ -249,30 +249,30 @@ def generate_itempool(world):
world.treasure_hunt_icon = treasure_hunt_icon world.treasure_hunt_icon = treasure_hunt_icon
if world.keysanity: if world.keysanity:
world.itempool.extend(get_dungeon_item_pool(world)) world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player])
# logic has some branches where having 4 hearts is one possible requirement (of several alternatives) # logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
# rather than making all hearts/heart pieces progression items (which slows down generation considerably) # rather than making all hearts/heart pieces progression items (which slows down generation considerably)
# We mark one random heart container as an advancement item (or 4 heart pieces in expert mode) # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
if world.difficulty in ['easy', 'normal', 'hard'] and not (world.custom and world.customitemarray[30] == 0): if world.difficulty in ['easy', 'normal', 'hard'] and not (world.custom and world.customitemarray[30] == 0):
[item for item in world.itempool if item.name == 'Boss Heart Container'][0].advancement = True [item for item in world.itempool if item.name == 'Boss Heart Container' and item.player == player][0].advancement = True
elif world.difficulty in ['expert'] and not (world.custom and world.customitemarray[29] < 4): elif world.difficulty in ['expert'] and not (world.custom and world.customitemarray[29] < 4):
adv_heart_pieces = [item for item in world.itempool if item.name == 'Piece of Heart'][0:4] adv_heart_pieces = [item for item in world.itempool if item.name == 'Piece of Heart' and item.player == player][0:4]
for hp in adv_heart_pieces: for hp in adv_heart_pieces:
hp.advancement = True hp.advancement = True
# shuffle medallions # shuffle medallions
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
world.required_medallions = (mm_medallion, tr_medallion) world.required_medallions[player] = (mm_medallion, tr_medallion)
place_bosses(world) place_bosses(world, player)
set_up_shops(world) set_up_shops(world, player)
if world.retro: if world.retro:
set_up_take_anys(world) set_up_take_anys(world, player)
create_dynamic_shop_locations(world) create_dynamic_shop_locations(world, player)
take_any_locations = [ take_any_locations = [
'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut',
@ -284,39 +284,39 @@ take_any_locations = [
'Palace of Darkness Hint', 'East Dark World Hint', 'Archery Game', 'Dark Lake Hylia Ledge Hint', 'Palace of Darkness Hint', 'East Dark World Hint', 'Archery Game', 'Dark Lake Hylia Ledge Hint',
'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint'] 'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint']
def set_up_take_anys(world): def set_up_take_anys(world, player):
regions = random.sample(take_any_locations, 5) regions = random.sample(take_any_locations, 5)
old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave') old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player)
world.regions.append(old_man_take_any) world.regions.append(old_man_take_any)
world.dynamic_regions.append(old_man_take_any) world.dynamic_regions.append(old_man_take_any)
reg = regions.pop() reg = regions.pop()
entrance = world.get_region(reg).entrances[0] entrance = world.get_region(reg, player).entrances[0]
connect_entrance(world, entrance, old_man_take_any) connect_entrance(world, entrance, old_man_take_any, player)
entrance.target = 0x58 entrance.target = 0x58
old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True) old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True)
world.shops.append(old_man_take_any.shop) world.shops.append(old_man_take_any.shop)
old_man_take_any.shop.active = True old_man_take_any.shop.active = True
swords = [item for item in world.itempool if item.type == 'Sword'] swords = [item for item in world.itempool if item.type == 'Sword' and item.player == player]
if swords: if swords:
sword = random.choice(swords) sword = random.choice(swords)
world.itempool.remove(sword) world.itempool.remove(sword)
world.itempool.append(ItemFactory('Rupees (20)')) world.itempool.append(ItemFactory('Rupees (20)', player))
old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True) old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True)
else: else:
old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0) old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0)
for num in range(4): for num in range(4):
take_any = Region("Take-Any #{}".format(num+1), RegionType.Cave, 'a cave of choice') take_any = Region("Take-Any #{}".format(num+1), RegionType.Cave, 'a cave of choice', player)
world.regions.append(take_any) world.regions.append(take_any)
world.dynamic_regions.append(take_any) world.dynamic_regions.append(take_any)
target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)])
reg = regions.pop() reg = regions.pop()
entrance = world.get_region(reg).entrances[0] entrance = world.get_region(reg, player).entrances[0]
connect_entrance(world, entrance, take_any) connect_entrance(world, entrance, take_any, player)
entrance.target = target entrance.target = target
take_any.shop = Shop(take_any, room_id, ShopType.TakeAny, 0xE3, True) take_any.shop = Shop(take_any, room_id, ShopType.TakeAny, 0xE3, True)
world.shops.append(take_any.shop) world.shops.append(take_any.shop)
@ -326,50 +326,52 @@ def set_up_take_anys(world):
world.intialize_regions() world.intialize_regions()
def create_dynamic_shop_locations(world): def create_dynamic_shop_locations(world, player):
for shop in world.shops: for shop in world.shops:
for i, item in enumerate(shop.inventory): if shop.region.player == player:
if item is None: for i, item in enumerate(shop.inventory):
continue if item is None:
if item['create_location']: continue
loc = Location("{} Item {}".format(shop.region.name, i+1), parent=shop.region) if item['create_location']:
shop.region.locations.append(loc) loc = Location(player, "{} Item {}".format(shop.region.name, i+1), parent=shop.region)
world.dynamic_locations.append(loc) shop.region.locations.append(loc)
world.dynamic_locations.append(loc)
world.clear_location_cache() world.clear_location_cache()
world.push_item(loc, ItemFactory(item['item']), False) world.push_item(loc, ItemFactory(item['item'], player), False)
loc.event = True loc.event = True
def fill_prizes(world, attempts=15): def fill_prizes(world, attempts=15):
crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6']) all_state = world.get_all_state(keys=True)
crystal_locations = [world.get_location('Turtle Rock - Prize'), world.get_location('Eastern Palace - Prize'), world.get_location('Desert Palace - Prize'), world.get_location('Tower of Hera - Prize'), world.get_location('Palace of Darkness - Prize'), for player in range(1, world.players + 1):
world.get_location('Thieves\' Town - Prize'), world.get_location('Skull Woods - Prize'), world.get_location('Swamp Palace - Prize'), world.get_location('Ice Palace - Prize'), crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'], player)
world.get_location('Misery Mire - Prize')] crystal_locations = [world.get_location('Turtle Rock - Prize', player), world.get_location('Eastern Palace - Prize', player), world.get_location('Desert Palace - Prize', player), world.get_location('Tower of Hera - Prize', player), world.get_location('Palace of Darkness - Prize', player),
placed_prizes = [loc.item.name for loc in crystal_locations if loc.item is not None] world.get_location('Thieves\' Town - Prize', player), world.get_location('Skull Woods - Prize', player), world.get_location('Swamp Palace - Prize', player), world.get_location('Ice Palace - Prize', player),
unplaced_prizes = [crystal for crystal in crystals if crystal.name not in placed_prizes] world.get_location('Misery Mire - Prize', player)]
empty_crystal_locations = [loc for loc in crystal_locations if loc.item is None] placed_prizes = [loc.item.name for loc in crystal_locations if loc.item is not None]
unplaced_prizes = [crystal for crystal in crystals if crystal.name not in placed_prizes]
empty_crystal_locations = [loc for loc in crystal_locations if loc.item is None]
while attempts: for attempt in range(attempts):
attempts -= 1 try:
try: prizepool = list(unplaced_prizes)
prizepool = list(unplaced_prizes) prize_locs = list(empty_crystal_locations)
prize_locs = list(empty_crystal_locations) random.shuffle(prizepool)
random.shuffle(prizepool) random.shuffle(prize_locs)
random.shuffle(prize_locs) fill_restrictive(world, all_state, prize_locs, prizepool)
fill_restrictive(world, world.get_all_state(keys=True), prize_locs, prizepool) except FillError as e:
except FillError: logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times" % (e, attempts))
logging.getLogger('').info("Failed to place dungeon prizes. Will retry %s more times", attempts) for location in empty_crystal_locations:
for location in empty_crystal_locations: location.item = None
location.item = None continue
continue break
break else:
else: raise FillError('Unable to place dungeon prizes')
raise FillError('Unable to place dungeon prizes')
def set_up_shops(world): def set_up_shops(world, player):
# Changes to basic Shops # Changes to basic Shops
# TODO: move hard+ mode changes for sheilds here, utilizing the new shops # TODO: move hard+ mode changes for sheilds here, utilizing the new shops
@ -377,13 +379,13 @@ def set_up_shops(world):
shop.active = True shop.active = True
if world.retro: if world.retro:
rss = world.get_region('Red Shield Shop').shop rss = world.get_region('Red Shield Shop', player).shop
rss.active = True rss.active = True
rss.add_inventory(2, 'Single Arrow', 80) rss.add_inventory(2, 'Single Arrow', 80)
# Randomized changes to Shops # Randomized changes to Shops
if world.retro: if world.retro:
for shop in random.sample([s for s in world.shops if s.replaceable], 5): for shop in random.sample([s for s in world.shops if s.replaceable and s.region.player == player], 5):
shop.active = True shop.active = True
shop.add_inventory(0, 'Single Arrow', 80) shop.add_inventory(0, 'Single Arrow', 80)
shop.add_inventory(1, 'Small Key (Universal)', 100) shop.add_inventory(1, 'Small Key (Universal)', 100)

View File

@ -3,7 +3,7 @@ import logging
from BaseClasses import Item from BaseClasses import Item
def ItemFactory(items): def ItemFactory(items, player):
ret = [] ret = []
singleton = False singleton = False
if isinstance(items, str): if isinstance(items, str):
@ -12,7 +12,7 @@ def ItemFactory(items):
for item in items: for item in items:
if item in item_table: if item in item_table:
advancement, priority, type, code, pedestal_hint, pedestal_credit, sickkid_credit, zora_credit, witch_credit, fluteboy_credit, hint_text = item_table[item] advancement, priority, type, code, pedestal_hint, pedestal_credit, sickkid_credit, zora_credit, witch_credit, fluteboy_credit, hint_text = item_table[item]
ret.append(Item(item, advancement, priority, type, code, pedestal_hint, pedestal_credit, sickkid_credit, zora_credit, witch_credit, fluteboy_credit, hint_text)) ret.append(Item(item, advancement, priority, type, code, pedestal_hint, pedestal_credit, sickkid_credit, zora_credit, witch_credit, fluteboy_credit, hint_text, player))
else: else:
logging.getLogger('').warning('Unknown Item: %s', item) logging.getLogger('').warning('Unknown Item: %s', item)
return None return None

125
Main.py
View File

@ -12,7 +12,7 @@ from EntranceShuffle import link_entrances
from Rom import patch_rom, Sprite, LocalRom, JsonRom from Rom import patch_rom, Sprite, LocalRom, JsonRom
from Rules import set_rules from Rules import set_rules
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression
from ItemList import generate_itempool, difficulties, fill_prizes from ItemList import generate_itempool, difficulties, fill_prizes
from Utils import output_path from Utils import output_path
@ -40,7 +40,7 @@ def main(args, seed=None):
start = time.clock() start = time.clock()
# initialize the world # initialize the world
world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints) world = World(args.multi, args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints)
logger = logging.getLogger('') logger = logging.getLogger('')
if seed is None: if seed is None:
random.seed(None) random.seed(None)
@ -49,26 +49,32 @@ def main(args, seed=None):
world.seed = int(seed) world.seed = int(seed)
random.seed(world.seed) random.seed(world.seed)
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed) logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed)
world.difficulty_requirements = difficulties[world.difficulty] world.difficulty_requirements = difficulties[world.difficulty]
create_regions(world) for player in range(1, world.players + 1):
create_regions(world, player)
create_dungeons(world) create_dungeons(world, player)
logger.info('Shuffling the World about.') logger.info('Shuffling the World about.')
link_entrances(world) for player in range(1, world.players + 1):
link_entrances(world, player)
mark_light_world_regions(world) mark_light_world_regions(world)
logger.info('Generating Item Pool.') logger.info('Generating Item Pool.')
generate_itempool(world) for player in range(1, world.players + 1):
generate_itempool(world, player)
logger.info('Calculating Access Rules.') logger.info('Calculating Access Rules.')
set_rules(world) for player in range(1, world.players + 1):
set_rules(world, player)
logger.info('Placing Dungeon Prizes.') logger.info('Placing Dungeon Prizes.')
@ -102,9 +108,9 @@ def main(args, seed=None):
elif args.algorithm == 'balanced': elif args.algorithm == 'balanced':
distribute_items_restrictive(world, gt_filler(world)) distribute_items_restrictive(world, gt_filler(world))
logger.info('Calculating playthrough.') if world.players > 1:
logger.info('Balancing multiworld progression.')
create_playthrough(world) balance_multiworld_progression(world)
logger.info('Patching ROM.') logger.info('Patching ROM.')
@ -118,20 +124,38 @@ def main(args, seed=None):
outfilebase = 'ER_%s_%s-%s-%s%s_%s-%s%s%s%s%s_%s' % (world.logic, world.difficulty, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-nohints" if not world.hints else "", world.seed) outfilebase = 'ER_%s_%s-%s-%s%s_%s-%s%s%s%s%s_%s' % (world.logic, world.difficulty, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-nohints" if not world.hints else "", world.seed)
jsonout = {}
if not args.suppress_rom: if not args.suppress_rom:
if args.jsonout: if world.players > 1:
rom = JsonRom() raise NotImplementedError("Multiworld rom writes have not been implemented")
else: else:
rom = LocalRom(args.rom) player = 1
patch_rom(world, rom, bytearray(logic_hash), args.heartbeep, args.heartcolor, sprite)
if args.jsonout: if args.jsonout:
print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()})) rom = JsonRom()
else: else:
rom.write_to_file(args.jsonout or output_path('%s.sfc' % outfilebase)) rom = LocalRom(args.rom)
patch_rom(world, player, rom, bytearray(logic_hash), args.heartbeep, args.heartcolor, sprite, player_names)
if args.jsonout:
jsonout['patch'] = rom.patches
else:
apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite, player_names)
rom.write_to_file(output_path('%s.sfc' % outfilebase))
if args.create_spoiler and not args.jsonout: if args.create_spoiler and not args.jsonout:
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
if not args.skip_playthrough:
logger.info('Calculating playthrough.')
create_playthrough(world)
if args.jsonout:
print(json.dumps({**jsonout, 'spoiler': world.spoiler.to_json()}))
elif args.create_spoiler and not args.skip_playthrough:
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
logger.info('Done. Enjoy.') logger.info('Done. Enjoy.')
logger.debug('Total Time: %s', time.clock() - start) logger.debug('Total Time: %s', time.clock() - start)
@ -144,10 +168,12 @@ def gt_filler(world):
def copy_world(world): def copy_world(world):
# ToDo: Not good yet # ToDo: Not good yet
ret = World(world.shuffle, world.logic, world.mode, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.retro, world.custom, world.customitemarray, world.boss_shuffle, world.hints) ret = World(world.players, world.shuffle, world.logic, world.mode, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.retro, world.custom, world.customitemarray, world.boss_shuffle, world.hints)
ret.required_medallions = list(world.required_medallions) ret.required_medallions = world.required_medallions.copy()
ret.swamp_patch_required = world.swamp_patch_required ret.swamp_patch_required = world.swamp_patch_required.copy()
ret.ganon_at_pyramid = world.ganon_at_pyramid ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()
ret.powder_patch_required = world.powder_patch_required.copy()
ret.ganonstower_vanilla = world.ganonstower_vanilla.copy()
ret.treasure_hunt_count = world.treasure_hunt_count ret.treasure_hunt_count = world.treasure_hunt_count
ret.treasure_hunt_icon = world.treasure_hunt_icon ret.treasure_hunt_icon = world.treasure_hunt_icon
ret.sewer_light_cone = world.sewer_light_cone ret.sewer_light_cone = world.sewer_light_cone
@ -162,53 +188,56 @@ def copy_world(world):
ret.difficulty_requirements = world.difficulty_requirements ret.difficulty_requirements = world.difficulty_requirements
ret.fix_fake_world = world.fix_fake_world ret.fix_fake_world = world.fix_fake_world
ret.lamps_needed_for_dark_rooms = world.lamps_needed_for_dark_rooms ret.lamps_needed_for_dark_rooms = world.lamps_needed_for_dark_rooms
create_regions(ret)
create_dungeons(ret) for player in range(1, world.players + 1):
create_regions(ret, player)
create_dungeons(ret, player)
copy_dynamic_regions_and_locations(world, ret) copy_dynamic_regions_and_locations(world, ret)
# copy bosses # copy bosses
for dungeon in world.dungeons: for dungeon in world.dungeons:
for level, boss in dungeon.bosses.items(): for level, boss in dungeon.bosses.items():
ret.get_dungeon(dungeon.name).bosses[level] = boss ret.get_dungeon(dungeon.name, dungeon.player).bosses[level] = boss
for shop in world.shops: for shop in world.shops:
copied_shop = ret.get_region(shop.region.name).shop copied_shop = ret.get_region(shop.region.name, shop.region.player).shop
copied_shop.active = shop.active copied_shop.active = shop.active
copied_shop.inventory = copy.copy(shop.inventory) copied_shop.inventory = copy.copy(shop.inventory)
# connect copied world # connect copied world
for region in world.regions: for region in world.regions:
copied_region = ret.get_region(region.name) copied_region = ret.get_region(region.name, region.player)
copied_region.is_light_world = region.is_light_world copied_region.is_light_world = region.is_light_world
copied_region.is_dark_world = region.is_dark_world copied_region.is_dark_world = region.is_dark_world
for entrance in region.entrances: for entrance in region.entrances:
ret.get_entrance(entrance.name).connect(copied_region) ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
# fill locations # fill locations
for location in world.get_locations(): for location in world.get_locations():
if location.item is not None: if location.item is not None:
item = Item(location.item.name, location.item.advancement, location.item.priority, location.item.type) item = Item(location.item.name, location.item.advancement, location.item.priority, location.item.type, player = location.item.player)
ret.get_location(location.name).item = item ret.get_location(location.name, location.player).item = item
item.location = ret.get_location(location.name) item.location = ret.get_location(location.name, location.player)
if location.event: if location.event:
ret.get_location(location.name).event = True ret.get_location(location.name, location.player).event = True
# copy remaining itempool. No item in itempool should have an assigned location # copy remaining itempool. No item in itempool should have an assigned location
for item in world.itempool: for item in world.itempool:
ret.itempool.append(Item(item.name, item.advancement, item.priority, item.type)) ret.itempool.append(Item(item.name, item.advancement, item.priority, item.type, player = item.player))
# copy progress items in state # copy progress items in state
ret.state.prog_items = list(world.state.prog_items) ret.state.prog_items = list(world.state.prog_items)
ret.state.stale = True ret.state.stale = True
set_rules(ret) for player in range(1, world.players + 1):
set_rules(ret, player)
return ret return ret
def copy_dynamic_regions_and_locations(world, ret): def copy_dynamic_regions_and_locations(world, ret):
for region in world.dynamic_regions: for region in world.dynamic_regions:
new_reg = Region(region.name, region.type, region.hint_text) new_reg = Region(region.name, region.type, region.hint_text, region.player)
ret.regions.append(new_reg) ret.regions.append(new_reg)
ret.dynamic_regions.append(new_reg) ret.dynamic_regions.append(new_reg)
@ -219,8 +248,8 @@ def copy_dynamic_regions_and_locations(world, ret):
ret.shops.append(new_reg.shop) ret.shops.append(new_reg.shop)
for location in world.dynamic_locations: for location in world.dynamic_locations:
new_loc = Location(location.name, location.address, location.crystal, location.hint_text, location.parent_region) new_loc = Location(location.player, location.name, location.address, location.crystal, location.hint_text, location.parent_region,)
new_reg = ret.get_region(location.parent_region.name) new_reg = ret.get_region(location.parent_region.name, location.parent_region.player)
new_reg.locations.append(new_loc) new_reg.locations.append(new_loc)
@ -231,7 +260,8 @@ def create_playthrough(world):
# in treasure hunt and pedestal goals, ganon is invincible # in treasure hunt and pedestal goals, ganon is invincible
if world.goal in ['pedestal', 'triforcehunt']: if world.goal in ['pedestal', 'triforcehunt']:
world.get_location('Ganon').item = None for player in range(1, world.players + 1):
world.get_location('Ganon', player).item = None
# if we only check for beatable, we can do this sanity check first before writing down spheres # if we only check for beatable, we can do this sanity check first before writing down spheres
if world.check_beatable_only and not world.can_beat_game(): if world.check_beatable_only and not world.can_beat_game():
@ -264,7 +294,7 @@ def create_playthrough(world):
logging.getLogger('').debug('Calculated sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(prog_locations)) logging.getLogger('').debug('Calculated sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(prog_locations))
if not sphere: if not sphere:
logging.getLogger('').debug('The following items could not be reached: %s', ['%s at %s' % (location.item.name, location.name) for location in sphere_candidates]) logging.getLogger('').debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates])
if not world.check_beatable_only: if not world.check_beatable_only:
raise RuntimeError('Not all progression items reachable. Something went terribly wrong here.') raise RuntimeError('Not all progression items reachable. Something went terribly wrong here.')
else: else:
@ -275,11 +305,11 @@ def create_playthrough(world):
to_delete = [] to_delete = []
for location in sphere: for location in sphere:
# we remove the item at location and check if game is still beatable # we remove the item at location and check if game is still beatable
logging.getLogger('').debug('Checking if %s is required to beat the game.', location.item.name) logging.getLogger('').debug('Checking if %s (Player %d) is required to beat the game.', location.item.name, location.item.player)
old_item = location.item old_item = location.item
location.item = None location.item = None
state.remove(old_item) state.remove(old_item)
##if world.can_beat_game(state_cache[num]): ##if world.can_beat_game(state_cache[num]):
if world.can_beat_game(): if world.can_beat_game():
to_delete.append(location) to_delete.append(location)
else: else:
@ -316,7 +346,7 @@ def create_playthrough(world):
raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') raise RuntimeError('Not all required items reachable. Something went terribly wrong here.')
# store the required locations for statistical analysis # store the required locations for statistical analysis
old_world.required_locations = [location.name for sphere in collection_spheres for location in sphere] old_world.required_locations = [(location.name, location.player) for sphere in collection_spheres for location in sphere]
def flist_to_iter(node): def flist_to_iter(node):
while node: while node:
@ -331,9 +361,12 @@ def create_playthrough(world):
pathpairs = zip_longest(pathsiter, pathsiter) pathpairs = zip_longest(pathsiter, pathsiter)
return list(pathpairs) return list(pathpairs)
old_world.spoiler.paths = {location.name : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere} old_world.spoiler.paths = dict()
if any(exit == 'Pyramid Fairy' for path in old_world.spoiler.paths.values() for (_, exit) in path): for player in range(1, world.players + 1):
old_world.spoiler.paths['Big Bomb Shop'] = get_path(state, world.get_region('Big Bomb Shop')) old_world.spoiler.paths.update({ str(location) : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player})
for _, path in dict(old_world.spoiler.paths).items():
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
old_world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = get_path(state, world.get_region('Big Bomb Shop', player))
# we can finally output our playthrough # we can finally output our playthrough
old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)]) old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)])

View File

@ -33,7 +33,7 @@ def main(args):
start_time = time.clock() start_time = time.clock()
# initialize the world # initialize the world
world = World('vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, False, None) world = World(1, 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, False, None, 'none', False)
logger = logging.getLogger('') logger = logging.getLogger('')
hasher = hashlib.md5() hasher = hashlib.md5()
@ -48,14 +48,14 @@ def main(args):
world.difficulty_requirements = difficulties[world.difficulty] world.difficulty_requirements = difficulties[world.difficulty]
create_regions(world) create_regions(world, 1)
create_dungeons(world) create_dungeons(world, 1)
link_entrances(world) link_entrances(world, 1)
logger.info('Calculating Access Rules.') logger.info('Calculating Access Rules.')
set_rules(world) set_rules(world, 1)
logger.info('Fill the world.') logger.info('Fill the world.')
@ -63,8 +63,8 @@ def main(args):
fill_world(world, args.plando, text_patches) fill_world(world, args.plando, text_patches)
if world.get_entrance('Dam').connected_region.name != 'Dam' or world.get_entrance('Swamp Palace').connected_region.name != 'Swamp Palace (Entrance)': if world.get_entrance('Dam', 1).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', 1).connected_region.name != 'Swamp Palace (Entrance)':
world.swamp_patch_required = True world.swamp_patch_required[1] = True
logger.info('Calculating playthrough.') logger.info('Calculating playthrough.')
@ -84,7 +84,7 @@ def main(args):
sprite = None sprite = None
rom = LocalRom(args.rom) rom = LocalRom(args.rom)
patch_rom(world, rom, logic_hash, args.heartbeep, args.heartcolor, sprite) patch_rom(world, 1, rom, logic_hash, args.heartbeep, args.heartcolor, sprite)
for textname, texttype, text in text_patches: for textname, texttype, text in text_patches:
if texttype == 'text': if texttype == 'text':
@ -174,33 +174,33 @@ def fill_world(world, plando, text_patches):
continue continue
locationstr, itemstr = line.split(':', 1) locationstr, itemstr = line.split(':', 1)
location = world.get_location(locationstr.strip()) location = world.get_location(locationstr.strip(), 1)
if location is None: if location is None:
logger.warning('Unknown location: %s', locationstr) logger.warning('Unknown location: %s', locationstr)
continue continue
else: else:
item = ItemFactory(itemstr.strip()) item = ItemFactory(itemstr.strip(), 1)
if item is not None: if item is not None:
world.push_item(location, item) world.push_item(location, item)
if item.key: if item.key:
location.event = True location.event = True
elif '<=>' in line: elif '<=>' in line:
entrance, exit = line.split('<=>', 1) entrance, exit = line.split('<=>', 1)
connect_two_way(world, entrance.strip(), exit.strip()) connect_two_way(world, entrance.strip(), exit.strip(), 1)
elif '=>' in line: elif '=>' in line:
entrance, exit = line.split('=>', 1) entrance, exit = line.split('=>', 1)
connect_entrance(world, entrance.strip(), exit.strip()) connect_entrance(world, entrance.strip(), exit.strip(), 1)
elif '<=' in line: elif '<=' in line:
entrance, exit = line.split('<=', 1) entrance, exit = line.split('<=', 1)
connect_exit(world, exit.strip(), entrance.strip()) connect_exit(world, exit.strip(), entrance.strip(), 1)
world.required_medallions = (mm_medallion, tr_medallion) world.required_medallions[1] = (mm_medallion, tr_medallion)
# set up Agahnim Events # set up Agahnim Events
world.get_location('Agahnim 1').event = True world.get_location('Agahnim 1', 1).event = True
world.get_location('Agahnim 1').item = ItemFactory('Beat Agahnim 1') world.get_location('Agahnim 1', 1).item = ItemFactory('Beat Agahnim 1', 1)
world.get_location('Agahnim 2').event = True world.get_location('Agahnim 2', 1).event = True
world.get_location('Agahnim 2').item = ItemFactory('Beat Agahnim 2') world.get_location('Agahnim 2', 1).item = ItemFactory('Beat Agahnim 2', 1)
def start(): def start():

View File

@ -2,10 +2,10 @@ import collections
from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType
def create_regions(world): def create_regions(world, player):
world.regions = [ world.regions += [
create_lw_region('Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'], create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'],
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam', ["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam',
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave', 'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump',
@ -15,118 +15,118 @@ def create_regions(world):
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)', 'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', 'Waterfall of Wishing', 'Hyrule Castle Main Gate', 'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', 'Waterfall of Wishing', 'Hyrule Castle Main Gate',
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']), 'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']),
create_lw_region('Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']), create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']),
create_lw_region('Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']), create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']),
create_cave_region('Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top", create_cave_region(player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
"Blind\'s Hideout - Left", "Blind\'s Hideout - Left",
"Blind\'s Hideout - Right", "Blind\'s Hideout - Right",
"Blind\'s Hideout - Far Left", "Blind\'s Hideout - Far Left",
"Blind\'s Hideout - Far Right"]), "Blind\'s Hideout - Far Right"]),
create_cave_region('Hyrule Castle Secret Entrance', 'a drop\'s exit', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']), create_cave_region(player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
create_lw_region('Zoras River', ['King Zora', 'Zora\'s Ledge']), create_lw_region(player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
create_cave_region('Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']), create_cave_region(player, 'Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
create_lw_region('Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']), create_lw_region(player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
create_cave_region('Kings Grave', 'a cave with a chest', ['King\'s Tomb']), create_cave_region(player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
create_cave_region('North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']), create_cave_region(player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
create_cave_region('Dam', 'the dam', ['Floodgate', 'Floodgate Chest']), create_cave_region(player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
create_cave_region('Links House', 'your house', ['Link\'s House'], ['Links House Exit']), create_cave_region(player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']),
create_cave_region('Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']), create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
create_cave_region('Tavern', 'the tavern', ['Kakariko Tavern']), create_cave_region(player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
create_cave_region('Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']), create_cave_region(player, 'Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']),
create_cave_region('Snitch Lady (East)', 'a boring house'), create_cave_region(player, 'Snitch Lady (East)', 'a boring house'),
create_cave_region('Snitch Lady (West)', 'a boring house'), create_cave_region(player, 'Snitch Lady (West)', 'a boring house'),
create_cave_region('Bush Covered House', 'the grass man'), create_cave_region(player, 'Bush Covered House', 'the grass man'),
create_cave_region('Tavern (Front)', 'the tavern'), create_cave_region(player, 'Tavern (Front)', 'the tavern'),
create_cave_region('Light World Bomb Hut', 'a restock room'), create_cave_region(player, 'Light World Bomb Hut', 'a restock room'),
create_cave_region('Kakariko Shop', 'a common shop'), create_cave_region(player, 'Kakariko Shop', 'a common shop'),
create_cave_region('Fortune Teller (Light)', 'a fortune teller'), create_cave_region(player, 'Fortune Teller (Light)', 'a fortune teller'),
create_cave_region('Lake Hylia Fortune Teller', 'a fortune teller'), create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
create_cave_region('Lumberjack House', 'a boring house'), create_cave_region(player, 'Lumberjack House', 'a boring house'),
create_cave_region('Bonk Fairy (Light)', 'a fairy fountain'), create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'),
create_cave_region('Bonk Fairy (Dark)', 'a fairy fountain'), create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
create_cave_region('Lake Hylia Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region('Swamp Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'Swamp Healer Fairy', 'a fairy fountain'),
create_cave_region('Desert Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'),
create_cave_region('Dark Lake Hylia Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region('Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
create_cave_region('Dark Desert Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
create_cave_region('Dark Death Mountain Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
create_cave_region('Chicken House', 'a house with a chest', ['Chicken House']), create_cave_region(player, 'Chicken House', 'a house with a chest', ['Chicken House']),
create_cave_region('Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']), create_cave_region(player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
create_cave_region('Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']), create_cave_region(player, 'Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']),
create_cave_region('Kakariko Well (top)', 'a drop\'s exit', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle', create_cave_region(player, 'Kakariko Well (top)', 'a drop\'s exit', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']), 'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
create_cave_region('Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']), create_cave_region(player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
create_cave_region('Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']), create_cave_region(player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
create_lw_region('Bat Cave Drop Ledge', None, ['Bat Cave Drop']), create_lw_region(player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
create_cave_region('Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']), create_cave_region(player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
create_cave_region('Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']), create_cave_region(player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
create_cave_region('Sick Kids House', 'the sick kid', ['Sick Kid']), create_cave_region(player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
create_lw_region('Hobo Bridge', ['Hobo']), create_lw_region(player, 'Hobo Bridge', ['Hobo']),
create_cave_region('Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']), create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']),
create_cave_region('Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']), create_cave_region(player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']),
create_cave_region('Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']), create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']),
create_cave_region('Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']), create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
create_lw_region('Cave 45 Ledge', None, ['Cave 45']), create_lw_region(player, 'Cave 45 Ledge', None, ['Cave 45']),
create_cave_region('Cave 45', 'a cave with an item', ['Cave 45']), create_cave_region(player, 'Cave 45', 'a cave with an item', ['Cave 45']),
create_lw_region('Graveyard Ledge', None, ['Graveyard Cave']), create_lw_region(player, 'Graveyard Ledge', None, ['Graveyard Cave']),
create_cave_region('Graveyard Cave', 'a cave with an item', ['Graveyard Cave']), create_cave_region(player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
create_cave_region('Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']), create_cave_region(player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
create_cave_region('Long Fairy Cave', 'a fairy fountain'), create_cave_region(player, 'Long Fairy Cave', 'a fairy fountain'),
create_cave_region('Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right', create_cave_region(player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']), 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
create_cave_region('Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']), create_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
create_cave_region('Good Bee Cave', 'a cold bee'), create_cave_region(player, 'Good Bee Cave', 'a cold bee'),
create_cave_region('20 Rupee Cave', 'a cave with some cash'), create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'),
create_cave_region('Cave Shop (Lake Hylia)', 'a common shop'), create_cave_region(player, 'Cave Shop (Lake Hylia)', 'a common shop'),
create_cave_region('Cave Shop (Dark Death Mountain)', 'a common shop'), create_cave_region(player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
create_cave_region('Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']), create_cave_region(player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
create_cave_region('Library', 'the library', ['Library']), create_cave_region(player, 'Library', 'the library', ['Library']),
create_cave_region('Kakariko Gamble Game', 'a game of chance'), create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'),
create_cave_region('Potion Shop', 'the potion shop', ['Potion Shop']), create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
create_lw_region('Lake Hylia Island', ['Lake Hylia Island']), create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']),
create_cave_region('Capacity Upgrade', 'the queen of fairies'), create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies'),
create_cave_region('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('Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']), create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']),
create_cave_region('50 Rupee Cave', 'a cave with some cash'), create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'),
create_lw_region('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)']),
create_lw_region('Desert Ledge (Northeast)', None, ['Checkerboard Cave']), create_lw_region(player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']),
create_lw_region('Desert Palace Stairs', None, ['Desert Palace Entrance (South)']), create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']),
create_lw_region('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)']),
create_lw_region('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']),
create_dungeon_region('Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'], create_dungeon_region(player, 'Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']), ['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']),
create_dungeon_region('Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']), create_dungeon_region(player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
create_dungeon_region('Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']), create_dungeon_region(player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
create_dungeon_region('Desert Palace North', 'Desert Palace', ['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']), create_dungeon_region(player, 'Desert Palace North', 'Desert Palace', ['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
create_dungeon_region('Eastern Palace', 'Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest', create_dungeon_region(player, 'Eastern Palace', 'Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest',
'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize'], ['Eastern Palace Exit']), 'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize'], ['Eastern Palace Exit']),
create_lw_region('Master Sword Meadow', ['Master Sword Pedestal']), create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']),
create_cave_region('Lost Woods Gamble', 'a game of chance'), create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
create_lw_region('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('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']),
create_dungeon_region('Hyrule Castle', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest'], create_dungeon_region(player, 'Hyrule Castle', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest'],
['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']), ['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']),
create_dungeon_region('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_dungeon_region('Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'], ['Sewers Door']), create_dungeon_region(player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'], ['Sewers Door']),
create_dungeon_region('Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', create_dungeon_region(player, 'Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']), 'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
create_dungeon_region('Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']), create_dungeon_region(player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
create_dungeon_region('Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze'], ['Agahnim 1', 'Agahnims Tower Exit']), create_dungeon_region(player, 'Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze'], ['Agahnim 1', 'Agahnims Tower Exit']),
create_dungeon_region('Agahnim 1', 'Castle Tower', ['Agahnim 1'], None), create_dungeon_region(player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
create_cave_region('Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']), create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
create_cave_region('Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']), create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
create_cave_region('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('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('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('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)']),
create_cave_region('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('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('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)']),
create_lw_region('East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']), create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
create_cave_region('Hookshot Fairy', 'fairies deep in a cave'), create_cave_region(player, 'Hookshot Fairy', 'fairies deep in a cave'),
create_cave_region('Paradox Cave Front', 'a connector', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)', 'Light World Death Mountain Shop']), create_cave_region(player, 'Paradox Cave Front', 'a connector', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)', 'Light World Death Mountain Shop']),
create_cave_region('Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left', create_cave_region(player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
'Paradox Cave Lower - Left', 'Paradox Cave Lower - Left',
'Paradox Cave Lower - Right', 'Paradox Cave Lower - Right',
'Paradox Cave Lower - Far Right', 'Paradox Cave Lower - Far Right',
@ -134,174 +134,174 @@ def create_regions(world):
'Paradox Cave Upper - Left', 'Paradox Cave Upper - Left',
'Paradox Cave Upper - Right'], 'Paradox Cave Upper - Right'],
['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']), ['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']),
create_cave_region('Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']), create_cave_region(player, 'Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
create_cave_region('Light World Death Mountain Shop', 'a common shop'), create_cave_region(player, 'Light World Death Mountain Shop', 'a common shop'),
create_lw_region('East Death Mountain (Top)', None, ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'Turtle Rock Teleporter', 'Fairy Ascension Ledge']), create_lw_region(player, 'East Death Mountain (Top)', None, ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'Turtle Rock Teleporter', 'Fairy Ascension Ledge']),
create_lw_region('Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']), create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']),
create_cave_region('Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']), create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
create_cave_region('Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']), create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
create_lw_region('Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']), create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
create_cave_region('Fairy Ascension Cave (Bottom)', 'a connector', None, ['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']), create_cave_region(player, 'Fairy Ascension Cave (Bottom)', 'a connector', None, ['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']),
create_cave_region('Fairy Ascension Cave (Drop)', 'a connector', None, ['Fairy Ascension Cave Pots']), create_cave_region(player, 'Fairy Ascension Cave (Drop)', 'a connector', None, ['Fairy Ascension Cave Pots']),
create_cave_region('Fairy Ascension Cave (Top)', 'a connector', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']), create_cave_region(player, 'Fairy Ascension Cave (Top)', 'a connector', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
create_lw_region('Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']), create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']),
create_lw_region('Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']), create_lw_region(player, 'Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']),
create_lw_region('Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']), create_lw_region(player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']),
create_dungeon_region('Tower of Hera (Bottom)', 'Tower of Hera', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']), create_dungeon_region(player, 'Tower of Hera (Bottom)', 'Tower of Hera', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
create_dungeon_region('Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']), create_dungeon_region(player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']),
create_dungeon_region('Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']), create_dungeon_region(player, 'Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']),
create_dw_region('East Dark World', ['Pyramid'], ['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)', 'Dark Lake Hylia Teleporter', create_dw_region(player, 'East Dark World', ['Pyramid'], ['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)', 'Dark Lake Hylia Teleporter',
'Hyrule Castle Ledge Mirror Spot', 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Pyramid Hole', 'Northeast Dark World Broken Bridge Pass']), 'Hyrule Castle Ledge Mirror Spot', 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Pyramid Hole', 'Northeast Dark World Broken Bridge Pass']),
create_dw_region('Northeast Dark World', ['Catfish'], ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass']), create_dw_region(player, 'Northeast Dark World', ['Catfish'], ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass']),
create_cave_region('Palace of Darkness Hint', 'a storyteller'), create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region('East Dark World Hint', 'a storyteller'), create_cave_region(player, 'East Dark World Hint', 'a storyteller'),
create_dw_region('South Dark World', ['Stumpy', 'Digging Game', 'Bombos Tablet'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', 'Maze Race Mirror Spot', create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game', 'Bombos Tablet'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', 'Maze Race Mirror Spot',
'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop']), 'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop']),
create_cave_region('Big Bomb Shop', 'the bomb shop'), create_cave_region(player, 'Big Bomb Shop', 'the bomb shop'),
create_cave_region('Archery Game', 'a game of skill'), create_cave_region(player, 'Archery Game', 'a game of skill'),
create_dw_region('Dark Lake Hylia', None, ['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']), create_dw_region(player, 'Dark Lake Hylia', None, ['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']),
create_dw_region('Dark Lake Hylia Central Island', None, ['Ice Palace', 'Lake Hylia Central Island Mirror Spot']), create_dw_region(player, 'Dark Lake Hylia Central Island', None, ['Ice Palace', 'Lake Hylia Central Island Mirror Spot']),
create_dw_region('Dark Lake Hylia Ledge', None, ['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave']), create_dw_region(player, 'Dark Lake Hylia Ledge', None, ['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave']),
create_cave_region('Dark Lake Hylia Ledge Hint', 'a storyteller'), create_cave_region(player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
create_cave_region('Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'), create_cave_region(player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
create_cave_region('Hype Cave', 'a bounty of five items', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left', create_cave_region(player, 'Hype Cave', 'a bounty of five items', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']), 'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
create_dw_region('West Dark World', ['Frog'], ['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House', 'Chest Game', 'Thieves Town', 'Graveyard Ledge Mirror Spot', 'Kings Grave Mirror Spot', 'Bumper Cave Entrance Rock', create_dw_region(player, 'West Dark World', ['Frog'], ['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House', 'Chest Game', 'Thieves Town', 'Graveyard Ledge Mirror Spot', 'Kings Grave Mirror Spot', 'Bumper Cave Entrance Rock',
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Dark World Lumberjack Shop']), 'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Dark World Lumberjack Shop']),
create_dw_region('Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Dark World Shop']), create_dw_region(player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Dark World Shop']),
create_dw_region('Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Peg Area Rocks']), create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Peg Area Rocks']),
create_dw_region('Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']), create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']),
create_cave_region('Fortune Teller (Dark)', 'a fortune teller'), create_cave_region(player, 'Fortune Teller (Dark)', 'a fortune teller'),
create_cave_region('Village of Outcasts Shop', 'a common shop'), create_cave_region(player, 'Village of Outcasts Shop', 'a common shop'),
create_cave_region('Dark Lake Hylia Shop', 'a common shop'), create_cave_region(player, 'Dark Lake Hylia Shop', 'a common shop'),
create_cave_region('Dark World Lumberjack Shop', 'a common shop'), create_cave_region(player, 'Dark World Lumberjack Shop', 'a common shop'),
create_cave_region('Dark World Potion Shop', 'a common shop'), create_cave_region(player, 'Dark World Potion Shop', 'a common shop'),
create_cave_region('Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']), create_cave_region(player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
create_cave_region('Pyramid Fairy', 'a cave with two chests', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']), create_cave_region(player, 'Pyramid Fairy', 'a cave with two chests', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
create_cave_region('Brewery', 'a house with a chest', ['Brewery']), create_cave_region(player, 'Brewery', 'a house with a chest', ['Brewery']),
create_cave_region('C-Shaped House', 'a house with a chest', ['C-Shaped House']), create_cave_region(player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
create_cave_region('Chest Game', 'a game of 16 chests', ['Chest Game']), create_cave_region(player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
create_cave_region('Red Shield Shop', 'the rare shop'), create_cave_region(player, 'Red Shield Shop', 'the rare shop'),
create_cave_region('Dark Sanctuary Hint', 'a storyteller'), create_cave_region(player, 'Dark Sanctuary Hint', 'a storyteller'),
create_cave_region('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('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']),
create_dw_region('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('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']),
create_dw_region('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('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']),
create_cave_region('Dark Desert Hint', 'a storyteller'), create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
create_dw_region('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('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('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']),
create_dw_region('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']),
create_dw_region('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('Superbunny Cave', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], create_cave_region(player, 'Superbunny Cave', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'],
['Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)']), ['Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)']),
create_cave_region('Spike Cave', 'Spike Cave', ['Spike Cave']), create_cave_region(player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
create_cave_region('Hookshot Cave', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'], create_cave_region(player, 'Hookshot Cave', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'],
['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']), ['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']),
create_dw_region('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']),
create_lw_region('Death Mountain Floating Island (Light World)', ['Floating Island']), create_lw_region(player, 'Death Mountain Floating Island (Light World)', ['Floating Island']),
create_dw_region('Turtle Rock (Top)', None, ['Turtle Rock Drop']), create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']),
create_lw_region('Mimic Cave Ledge', None, ['Mimic Cave']), create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
create_cave_region('Mimic Cave', 'Mimic Cave', ['Mimic Cave']), create_cave_region(player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
create_dungeon_region('Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']), create_dungeon_region(player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
create_dungeon_region('Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']), create_dungeon_region(player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
create_dungeon_region('Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']), create_dungeon_region(player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']),
create_dungeon_region('Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest', create_dungeon_region(player, 'Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest',
'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']), 'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']),
create_dungeon_region('Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right', create_dungeon_region(player, 'Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
'Swamp Palace - Waterfall Room', 'Swamp Palace - Boss', 'Swamp Palace - Prize']), 'Swamp Palace - Waterfall Room', 'Swamp Palace - Boss', 'Swamp Palace - Prize']),
create_dungeon_region('Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest', create_dungeon_region(player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest',
'Thieves\' Town - Map Chest', 'Thieves\' Town - Map Chest',
'Thieves\' Town - Compass Chest', 'Thieves\' Town - Compass Chest',
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']), 'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
create_dungeon_region('Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic', create_dungeon_region(player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
'Thieves\' Town - Big Chest', 'Thieves\' Town - Big Chest',
'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']), 'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']),
create_dungeon_region('Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']), create_dungeon_region(player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
create_dungeon_region('Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']), create_dungeon_region(player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
create_dungeon_region('Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']), create_dungeon_region(player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
create_dungeon_region('Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']), create_dungeon_region(player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
create_dungeon_region('Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']), create_dungeon_region(player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
create_dungeon_region('Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']), create_dungeon_region(player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
create_dungeon_region('Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']), create_dungeon_region(player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
create_dungeon_region('Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']), create_dungeon_region(player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
create_dungeon_region('Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']), create_dungeon_region(player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
create_dungeon_region('Ice Palace (Entrance)', 'Ice Palace', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']), create_dungeon_region(player, 'Ice Palace (Entrance)', 'Ice Palace', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']),
create_dungeon_region('Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest', create_dungeon_region(player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest',
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']), 'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
create_dungeon_region('Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']), create_dungeon_region(player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
create_dungeon_region('Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']), create_dungeon_region(player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']),
create_dungeon_region('Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']), create_dungeon_region(player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
create_dungeon_region('Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']), create_dungeon_region(player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
create_dungeon_region('Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby', create_dungeon_region(player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']), 'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']),
create_dungeon_region('Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']), create_dungeon_region(player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
create_dungeon_region('Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']), create_dungeon_region(player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
create_dungeon_region('Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']), create_dungeon_region(player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
create_dungeon_region('Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']), create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
create_dungeon_region('Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left', create_dungeon_region(player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
'Turtle Rock - Roller Room - Right'], ['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']), 'Turtle Rock - Roller Room - Right'], ['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
create_dungeon_region('Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']), create_dungeon_region(player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
create_dungeon_region('Turtle Rock (Second Section)', 'Turtle Rock', ['Turtle Rock - Big Key Chest'], ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door']), create_dungeon_region(player, 'Turtle Rock (Second Section)', 'Turtle Rock', ['Turtle Rock - Big Key Chest'], ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door']),
create_dungeon_region('Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']), create_dungeon_region(player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
create_dungeon_region('Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']), create_dungeon_region(player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
create_dungeon_region('Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']), create_dungeon_region(player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
create_dungeon_region('Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', create_dungeon_region(player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'], 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']), ['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']),
create_dungeon_region('Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']), create_dungeon_region(player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
create_dungeon_region('Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']), create_dungeon_region(player, 'Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
create_dungeon_region('Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'], create_dungeon_region(player, 'Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']), ['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']),
create_dungeon_region('Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']), create_dungeon_region(player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']),
create_dungeon_region('Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']), create_dungeon_region(player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
create_dungeon_region('Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'], create_dungeon_region(player, 'Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']), ['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']),
create_dungeon_region('Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']), create_dungeon_region(player, 'Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
create_dungeon_region('Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']), create_dungeon_region(player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']),
create_dungeon_region('Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']), create_dungeon_region(player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
create_dungeon_region('Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'], create_dungeon_region(player, 'Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'],
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']), ['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']),
create_dungeon_region('Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']), create_dungeon_region(player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
create_dungeon_region('Ganons Tower (Compass Room)', 'Ganon\'s Tower', ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', create_dungeon_region(player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower', ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right',
'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'], 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'],
['Ganons Tower (Bottom) (East)']), ['Ganons Tower (Bottom) (East)']),
create_dungeon_region('Ganons Tower (Hookshot Room)', 'Ganon\'s Tower', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right', create_dungeon_region(player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'], 'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'],
['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']), ['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']),
create_dungeon_region('Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']), create_dungeon_region(player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
create_dungeon_region('Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']), create_dungeon_region(player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
create_dungeon_region('Ganons Tower (Teleport Room)', 'Ganon\'s Tower', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', create_dungeon_region(player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right',
'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'], 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'],
['Ganons Tower (Bottom) (West)']), ['Ganons Tower (Bottom) (West)']),
create_dungeon_region('Ganons Tower (Bottom)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left', create_dungeon_region(player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left',
'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']), 'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']),
create_dungeon_region('Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']), create_dungeon_region(player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']),
create_dungeon_region('Ganons Tower (Before Moldorm)', 'Ganon\'s Tower', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right', create_dungeon_region(player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right',
'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']), 'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']),
create_dungeon_region('Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']), create_dungeon_region(player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']),
create_dungeon_region('Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None), create_dungeon_region(player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_cave_region('Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']), create_cave_region(player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
create_cave_region('Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']), create_cave_region(player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
create_dw_region('Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']) create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop'])
] ]
for region_name, (room_id, shopkeeper, replaceable) in shop_table.items(): for region_name, (room_id, shopkeeper, replaceable) in shop_table.items():
region = world.get_region(region_name) region = world.get_region(region_name, player)
shop = Shop(region, room_id, ShopType.Shop, shopkeeper, replaceable) shop = Shop(region, room_id, ShopType.Shop, shopkeeper, replaceable)
region.shop = shop region.shop = shop
world.shops.append(shop) world.shops.append(shop)
for index, (item, price) in enumerate(default_shop_contents[region_name]): for index, (item, price) in enumerate(default_shop_contents[region_name]):
shop.add_inventory(index, item, price) shop.add_inventory(index, item, price)
region = world.get_region('Capacity Upgrade') region = world.get_region('Capacity Upgrade', player)
shop = Shop(region, 0x0115, ShopType.UpgradeShop, 0x04, True) shop = Shop(region, 0x0115, ShopType.UpgradeShop, 0x04, True)
region.shop = shop region.shop = shop
world.shops.append(shop) world.shops.append(shop)
@ -309,30 +309,30 @@ def create_regions(world):
shop.add_inventory(1, 'Arrow Upgrade (+5)', 100, 7) shop.add_inventory(1, 'Arrow Upgrade (+5)', 100, 7)
world.intialize_regions() world.intialize_regions()
def create_lw_region(name, locations=None, exits=None): def create_lw_region(player, name, locations=None, exits=None):
return _create_region(name, RegionType.LightWorld, 'Light World', locations, exits) return _create_region(player, name, RegionType.LightWorld, 'Light World', locations, exits)
def create_dw_region(name, locations=None, exits=None): def create_dw_region(player, name, locations=None, exits=None):
return _create_region(name, RegionType.DarkWorld, 'Dark World', locations, exits) return _create_region(player, name, RegionType.DarkWorld, 'Dark World', locations, exits)
def create_cave_region(name, hint='Hyrule', locations=None, exits=None): def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None):
return _create_region(name, RegionType.Cave, hint, locations, exits) return _create_region(player, name, RegionType.Cave, hint, locations, exits)
def create_dungeon_region(name, hint='Hyrule', locations=None, exits=None): def create_dungeon_region(player, name, hint='Hyrule', locations=None, exits=None):
return _create_region(name, RegionType.Dungeon, hint, locations, exits) return _create_region(player, name, RegionType.Dungeon, hint, locations, exits)
def _create_region(name, type, hint='Hyrule', locations=None, exits=None): def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None):
ret = Region(name, type, hint) ret = Region(name, type, hint, player)
if locations is None: if locations is None:
locations = [] locations = []
if exits is None: if exits is None:
exits = [] exits = []
for exit in exits: for exit in exits:
ret.exits.append(Entrance(exit, ret)) ret.exits.append(Entrance(player, exit, ret))
for location in locations: for location in locations:
address, crystal, hint_text = location_table[location] address, crystal, hint_text = location_table[location]
ret.locations.append(Location(location, address, crystal, hint_text, ret)) ret.locations.append(Location(player, location, address, crystal, hint_text, ret))
return ret return ret
def mark_light_world_regions(world): def mark_light_world_regions(world):
@ -398,221 +398,221 @@ default_shop_contents = {
} }
location_table = {'Mushroom': (0x180013, False, 'in the woods'), location_table = {'Mushroom': (0x180013, False, 'in the woods'),
'Bottle Merchant': (0x2EB18, False, 'with a merchant'), 'Bottle Merchant': (0x2eb18, False, 'with a merchant'),
'Flute Spot': (0x18014A, False, 'underground'), 'Flute Spot': (0x18014a, False, 'underground'),
'Sunken Treasure': (0x180145, False, 'underwater'), 'Sunken Treasure': (0x180145, False, 'underwater'),
'Purple Chest': (0x33D68, False, 'from a box'), 'Purple Chest': (0x33d68, False, 'from a box'),
'Blind\'s Hideout - Top': (0xEB0F, False, 'in a basement'), "Blind's Hideout - Top": (0xeb0f, False, 'in a basement'),
'Blind\'s Hideout - Left': (0xEB12, False, 'in a basement'), "Blind's Hideout - Left": (0xeb12, False, 'in a basement'),
'Blind\'s Hideout - Right': (0xEB15, False, 'in a basement'), "Blind's Hideout - Right": (0xeb15, False, 'in a basement'),
'Blind\'s Hideout - Far Left': (0xEB18, False, 'in a basement'), "Blind's Hideout - Far Left": (0xeb18, False, 'in a basement'),
'Blind\'s Hideout - Far Right': (0xEB1B, False, 'in a basement'), "Blind's Hideout - Far Right": (0xeb1b, False, 'in a basement'),
'Link\'s Uncle': (0x2DF45, False, 'with your uncle'), "Link's Uncle": (0x2df45, False, 'with your uncle'),
'Secret Passage': (0xE971, False, 'near your uncle'), 'Secret Passage': (0xe971, False, 'near your uncle'),
'King Zora': (0xEE1C3, False, 'at a high price'), 'King Zora': (0xee1c3, False, 'at a high price'),
'Zora\'s Ledge': (0x180149, False, 'near Zora'), "Zora's Ledge": (0x180149, False, 'near Zora'),
'Waterfall Fairy - Left': (0xE9B0, False, 'near a fairy'), 'Waterfall Fairy - Left': (0xe9b0, False, 'near a fairy'),
'Waterfall Fairy - Right': (0xE9D1, False, 'near a fairy'), 'Waterfall Fairy - Right': (0xe9d1, False, 'near a fairy'),
'King\'s Tomb': (0xE97A, False, 'alone in a cave'), "King's Tomb": (0xe97a, False, 'alone in a cave'),
'Floodgate Chest': (0xE98C, False, 'in the dam'), 'Floodgate Chest': (0xe98c, False, 'in the dam'),
'Link\'s House': (0xE9BC, False, 'in your home'), "Link's House": (0xe9bc, False, 'in your home'),
'Kakariko Tavern': (0xE9CE, False, 'in the bar'), 'Kakariko Tavern': (0xe9ce, False, 'in the bar'),
'Chicken House': (0xE9E9, False, 'near poultry'), 'Chicken House': (0xe9e9, False, 'near poultry'),
'Aginah\'s Cave': (0xE9F2, False, 'with Aginah'), "Aginah's Cave": (0xe9f2, False, 'with Aginah'),
'Sahasrahla\'s Hut - Left': (0xEA82, False, 'near the elder'), "Sahasrahla's Hut - Left": (0xea82, False, 'near the elder'),
'Sahasrahla\'s Hut - Middle': (0xEA85, False, 'near the elder'), "Sahasrahla's Hut - Middle": (0xea85, False, 'near the elder'),
'Sahasrahla\'s Hut - Right': (0xEA88, False, 'near the elder'), "Sahasrahla's Hut - Right": (0xea88, False, 'near the elder'),
'Sahasrahla': (0x2F1FC, False, 'with the elder'), 'Sahasrahla': (0x2f1fc, False, 'with the elder'),
'Kakariko Well - Top': (0xEA8E, False, 'in a well'), 'Kakariko Well - Top': (0xea8e, False, 'in a well'),
'Kakariko Well - Left': (0xEA91, False, 'in a well'), 'Kakariko Well - Left': (0xea91, False, 'in a well'),
'Kakariko Well - Middle': (0xEA94, False, 'in a well'), 'Kakariko Well - Middle': (0xea94, False, 'in a well'),
'Kakariko Well - Right': (0xEA97, False, 'in a well'), 'Kakariko Well - Right': (0xea97, False, 'in a well'),
'Kakariko Well - Bottom': (0xEA9A, False, 'in a well'), 'Kakariko Well - Bottom': (0xea9a, False, 'in a well'),
'Blacksmith': (0x18002A, False, 'with the smith'), 'Blacksmith': (0x18002a, False, 'with the smith'),
'Magic Bat': (0x180015, False, 'with the bat'), 'Magic Bat': (0x180015, False, 'with the bat'),
'Sick Kid': (0x339CF, False, 'with the sick'), 'Sick Kid': (0x339cf, False, 'with the sick'),
'Hobo': (0x33E7D, False, 'with the hobo'), 'Hobo': (0x33e7d, False, 'with the hobo'),
'Lost Woods Hideout': (0x180000, False, 'near a thief'), 'Lost Woods Hideout': (0x180000, False, 'near a thief'),
'Lumberjack Tree': (0x180001, False, 'in a hole'), 'Lumberjack Tree': (0x180001, False, 'in a hole'),
'Cave 45': (0x180003, False, 'alone in a cave'), 'Cave 45': (0x180003, False, 'alone in a cave'),
'Graveyard Cave': (0x180004, False, 'alone in a cave'), 'Graveyard Cave': (0x180004, False, 'alone in a cave'),
'Checkerboard Cave': (0x180005, False, 'alone in a cave'), 'Checkerboard Cave': (0x180005, False, 'alone in a cave'),
'Mini Moldorm Cave - Far Left': (0xEB42, False, 'near Moldorms'), 'Mini Moldorm Cave - Far Left': (0xeb42, False, 'near Moldorms'),
'Mini Moldorm Cave - Left': (0xEB45, False, 'near Moldorms'), 'Mini Moldorm Cave - Left': (0xeb45, False, 'near Moldorms'),
'Mini Moldorm Cave - Right': (0xEB48, False, 'near Moldorms'), 'Mini Moldorm Cave - Right': (0xeb48, False, 'near Moldorms'),
'Mini Moldorm Cave - Far Right': (0xEB4B, False, 'near Moldorms'), 'Mini Moldorm Cave - Far Right': (0xeb4b, False, 'near Moldorms'),
'Mini Moldorm Cave - Generous Guy': (0x180010, False, 'near Moldorms'), 'Mini Moldorm Cave - Generous Guy': (0x180010, False, 'near Moldorms'),
'Ice Rod Cave': (0xEB4E, False, 'in a frozen cave'), 'Ice Rod Cave': (0xeb4e, False, 'in a frozen cave'),
'Bonk Rock Cave': (0xEB3F, False, 'alone in a cave'), 'Bonk Rock Cave': (0xeb3f, False, 'alone in a cave'),
'Library': (0x180012, False, 'near books'), 'Library': (0x180012, False, 'near books'),
'Potion Shop': (0x180014, False, 'near potions'), 'Potion Shop': (0x180014, False, 'near potions'),
'Lake Hylia Island': (0x180144, False, 'on an island'), 'Lake Hylia Island': (0x180144, False, 'on an island'),
'Maze Race': (0x180142, False, 'at the race'), 'Maze Race': (0x180142, False, 'at the race'),
'Desert Ledge': (0x180143, False, 'in the desert'), 'Desert Ledge': (0x180143, False, 'in the desert'),
'Desert Palace - Big Chest': (0xE98F, False, 'in Desert Palace'), 'Desert Palace - Big Chest': (0xe98f, False, 'in Desert Palace'),
'Desert Palace - Torch': (0x180160, False, 'in Desert Palace'), 'Desert Palace - Torch': (0x180160, False, 'in Desert Palace'),
'Desert Palace - Map Chest': (0xE9B6, False, 'in Desert Palace'), 'Desert Palace - Map Chest': (0xe9b6, False, 'in Desert Palace'),
'Desert Palace - Compass Chest': (0xE9CB, False, 'in Desert Palace'), 'Desert Palace - Compass Chest': (0xe9cb, False, 'in Desert Palace'),
'Desert Palace - Big Key Chest': (0xE9C2, False, 'in Desert Palace'), 'Desert Palace - Big Key Chest': (0xe9c2, False, 'in Desert Palace'),
'Desert Palace - Boss': (0x180151, False, 'with Lanmolas'), 'Desert Palace - Boss': (0x180151, False, 'with Lanmolas'),
'Eastern Palace - Compass Chest': (0xE977, False, 'in Eastern Palace'), 'Eastern Palace - Compass Chest': (0xe977, False, 'in Eastern Palace'),
'Eastern Palace - Big Chest': (0xE97D, False, 'in Eastern Palace'), 'Eastern Palace - Big Chest': (0xe97d, False, 'in Eastern Palace'),
'Eastern Palace - Cannonball Chest': (0xE9B3, False, 'in Eastern Palace'), 'Eastern Palace - Cannonball Chest': (0xe9b3, False, 'in Eastern Palace'),
'Eastern Palace - Big Key Chest': (0xE9B9, False, 'in Eastern Palace'), 'Eastern Palace - Big Key Chest': (0xe9b9, False, 'in Eastern Palace'),
'Eastern Palace - Map Chest': (0xE9F5, False, 'in Eastern Palace'), 'Eastern Palace - Map Chest': (0xe9f5, False, 'in Eastern Palace'),
'Eastern Palace - Boss': (0x180150, False, 'with the Armos'), 'Eastern Palace - Boss': (0x180150, False, 'with the Armos'),
'Master Sword Pedestal': (0x289B0, False, 'at the pedestal'), 'Master Sword Pedestal': (0x289b0, False, 'at the pedestal'),
'Hyrule Castle - Boomerang Chest': (0xE974, False, 'in Hyrule Castle'), 'Hyrule Castle - Boomerang Chest': (0xe974, False, 'in Hyrule Castle'),
'Hyrule Castle - Map Chest': (0xEB0C, False, 'in Hyrule Castle'), 'Hyrule Castle - Map Chest': (0xeb0c, False, 'in Hyrule Castle'),
'Hyrule Castle - Zelda\'s Chest': (0xEB09, False, 'in Hyrule Castle'), "Hyrule Castle - Zelda's Chest": (0xeb09, False, 'in Hyrule Castle'),
'Sewers - Dark Cross': (0xE96E, False, 'in the sewers'), 'Sewers - Dark Cross': (0xe96e, False, 'in the sewers'),
'Sewers - Secret Room - Left': (0xEB5D, False, 'in the sewers'), 'Sewers - Secret Room - Left': (0xeb5d, False, 'in the sewers'),
'Sewers - Secret Room - Middle': (0xEB60, False, 'in the sewers'), 'Sewers - Secret Room - Middle': (0xeb60, False, 'in the sewers'),
'Sewers - Secret Room - Right': (0xEB63, False, 'in the sewers'), 'Sewers - Secret Room - Right': (0xeb63, False, 'in the sewers'),
'Sanctuary': (0xEA79, False, 'in Sanctuary'), 'Sanctuary': (0xea79, False, 'in Sanctuary'),
'Castle Tower - Room 03': (0xEAB5, False, 'in Castle Tower'), 'Castle Tower - Room 03': (0xeab5, False, 'in Castle Tower'),
'Castle Tower - Dark Maze': (0xEAB2, False, 'in Castle Tower'), 'Castle Tower - Dark Maze': (0xeab2, False, 'in Castle Tower'),
'Old Man': (0xF69FA, False, 'with the old man'), 'Old Man': (0xf69fa, False, 'with the old man'),
'Spectacle Rock Cave': (0x180002, False, 'alone in a cave'), 'Spectacle Rock Cave': (0x180002, False, 'alone in a cave'),
'Paradox Cave Lower - Far Left': (0xEB2A, False, 'in a cave with seven chests'), 'Paradox Cave Lower - Far Left': (0xeb2a, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Left': (0xEB2D, False, 'in a cave with seven chests'), 'Paradox Cave Lower - Left': (0xeb2d, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Right': (0xEB30, False, 'in a cave with seven chests'), 'Paradox Cave Lower - Right': (0xeb30, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Far Right': (0xEB33, False, 'in a cave with seven chests'), 'Paradox Cave Lower - Far Right': (0xeb33, False, 'in a cave with seven chests'),
'Paradox Cave Lower - Middle': (0xEB36, False, 'in a cave with seven chests'), 'Paradox Cave Lower - Middle': (0xeb36, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Left': (0xEB39, False, 'in a cave with seven chests'), 'Paradox Cave Upper - Left': (0xeb39, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Right': (0xEB3C, False, 'in a cave with seven chests'), 'Paradox Cave Upper - Right': (0xeb3c, False, 'in a cave with seven chests'),
'Spiral Cave': (0xE9BF, False, 'in spiral cave'), 'Spiral Cave': (0xe9bf, False, 'in spiral cave'),
'Ether Tablet': (0x180016, False, 'at a monolith'), 'Ether Tablet': (0x180016, False, 'at a monolith'),
'Spectacle Rock': (0x180140, False, 'atop a rock'), 'Spectacle Rock': (0x180140, False, 'atop a rock'),
'Tower of Hera - Basement Cage': (0x180162, False, 'in Tower of Hera'), 'Tower of Hera - Basement Cage': (0x180162, False, 'in Tower of Hera'),
'Tower of Hera - Map Chest': (0xE9AD, False, 'in Tower of Hera'), 'Tower of Hera - Map Chest': (0xe9ad, False, 'in Tower of Hera'),
'Tower of Hera - Big Key Chest': (0xE9E6, False, 'in Tower of Hera'), 'Tower of Hera - Big Key Chest': (0xe9e6, False, 'in Tower of Hera'),
'Tower of Hera - Compass Chest': (0xE9FB, False, 'in Tower of Hera'), 'Tower of Hera - Compass Chest': (0xe9fb, False, 'in Tower of Hera'),
'Tower of Hera - Big Chest': (0xE9F8, False, 'in Tower of Hera'), 'Tower of Hera - Big Chest': (0xe9f8, False, 'in Tower of Hera'),
'Tower of Hera - Boss': (0x180152, False, 'with Moldorm'), 'Tower of Hera - Boss': (0x180152, False, 'with Moldorm'),
'Pyramid': (0x180147, False, 'on the pyramid'), 'Pyramid': (0x180147, False, 'on the pyramid'),
'Catfish': (0xEE185, False, 'with a catfish'), 'Catfish': (0xee185, False, 'with a catfish'),
'Stumpy': (0x330C7, False, 'with tree boy'), 'Stumpy': (0x330c7, False, 'with tree boy'),
'Digging Game': (0x180148, False, 'underground'), 'Digging Game': (0x180148, False, 'underground'),
'Bombos Tablet': (0x180017, False, 'at a monolith'), 'Bombos Tablet': (0x180017, False, 'at a monolith'),
'Hype Cave - Top': (0xEB1E, False, 'near a bat-like man'), 'Hype Cave - Top': (0xeb1e, False, 'near a bat-like man'),
'Hype Cave - Middle Right': (0xEB21, False, 'near a bat-like man'), 'Hype Cave - Middle Right': (0xeb21, False, 'near a bat-like man'),
'Hype Cave - Middle Left': (0xEB24, False, 'near a bat-like man'), 'Hype Cave - Middle Left': (0xeb24, False, 'near a bat-like man'),
'Hype Cave - Bottom': (0xEB27, False, 'near a bat-like man'), 'Hype Cave - Bottom': (0xeb27, False, 'near a bat-like man'),
'Hype Cave - Generous Guy': (0x180011, False, 'with a bat-like man'), 'Hype Cave - Generous Guy': (0x180011, False, 'with a bat-like man'),
'Peg Cave': (0x180006, False, 'alone in a cave'), 'Peg Cave': (0x180006, False, 'alone in a cave'),
'Pyramid Fairy - Left': (0xE980, False, 'near a fairy'), 'Pyramid Fairy - Left': (0xe980, False, 'near a fairy'),
'Pyramid Fairy - Right': (0xE983, False, 'near a fairy'), 'Pyramid Fairy - Right': (0xe983, False, 'near a fairy'),
'Brewery': (0xE9EC, False, 'alone in a home'), 'Brewery': (0xe9ec, False, 'alone in a home'),
'C-Shaped House': (0xE9EF, False, 'alone in a home'), 'C-Shaped House': (0xe9ef, False, 'alone in a home'),
'Chest Game': (0xEDA8, False, 'as a prize'), 'Chest Game': (0xeda8, False, 'as a prize'),
'Bumper Cave Ledge': (0x180146, False, 'on a ledge'), 'Bumper Cave Ledge': (0x180146, False, 'on a ledge'),
'Mire Shed - Left': (0xEA73, False, 'near sparks'), 'Mire Shed - Left': (0xea73, False, 'near sparks'),
'Mire Shed - Right': (0xEA76, False, 'near sparks'), 'Mire Shed - Right': (0xea76, False, 'near sparks'),
'Superbunny Cave - Top': (0xEA7C, False, 'in a connection'), 'Superbunny Cave - Top': (0xea7c, False, 'in a connection'),
'Superbunny Cave - Bottom': (0xEA7F, False, 'in a connection'), 'Superbunny Cave - Bottom': (0xea7f, False, 'in a connection'),
'Spike Cave': (0xEA8B, False, 'beyond spikes'), 'Spike Cave': (0xea8b, False, 'beyond spikes'),
'Hookshot Cave - Top Right': (0xEB51, False, 'across pits'), 'Hookshot Cave - Top Right': (0xeb51, False, 'across pits'),
'Hookshot Cave - Top Left': (0xEB54, False, 'across pits'), 'Hookshot Cave - Top Left': (0xeb54, False, 'across pits'),
'Hookshot Cave - Bottom Right': (0xEB5A, False, 'across pits'), 'Hookshot Cave - Bottom Right': (0xeb5a, False, 'across pits'),
'Hookshot Cave - Bottom Left': (0xEB57, False, 'across pits'), 'Hookshot Cave - Bottom Left': (0xeb57, False, 'across pits'),
'Floating Island': (0x180141, False, 'on an island'), 'Floating Island': (0x180141, False, 'on an island'),
'Mimic Cave': (0xE9C5, False, 'in a cave of mimicry'), 'Mimic Cave': (0xe9c5, False, 'in a cave of mimicry'),
'Swamp Palace - Entrance': (0xEA9D, False, 'in Swamp Palace'), 'Swamp Palace - Entrance': (0xea9d, False, 'in Swamp Palace'),
'Swamp Palace - Map Chest': (0xE986, False, 'in Swamp Palace'), 'Swamp Palace - Map Chest': (0xe986, False, 'in Swamp Palace'),
'Swamp Palace - Big Chest': (0xE989, False, 'in Swamp Palace'), 'Swamp Palace - Big Chest': (0xe989, False, 'in Swamp Palace'),
'Swamp Palace - Compass Chest': (0xEAA0, False, 'in Swamp Palace'), 'Swamp Palace - Compass Chest': (0xeaa0, False, 'in Swamp Palace'),
'Swamp Palace - Big Key Chest': (0xEAA6, False, 'in Swamp Palace'), 'Swamp Palace - Big Key Chest': (0xeaa6, False, 'in Swamp Palace'),
'Swamp Palace - West Chest': (0xEAA3, False, 'in Swamp Palace'), 'Swamp Palace - West Chest': (0xeaa3, False, 'in Swamp Palace'),
'Swamp Palace - Flooded Room - Left': (0xEAA9, False, 'in Swamp Palace'), 'Swamp Palace - Flooded Room - Left': (0xeaa9, False, 'in Swamp Palace'),
'Swamp Palace - Flooded Room - Right': (0xEAAC, False, 'in Swamp Palace'), 'Swamp Palace - Flooded Room - Right': (0xeaac, False, 'in Swamp Palace'),
'Swamp Palace - Waterfall Room': (0xEAAF, False, 'in Swamp Palace'), 'Swamp Palace - Waterfall Room': (0xeaaf, False, 'in Swamp Palace'),
'Swamp Palace - Boss': (0x180154, False, 'with Arrghus'), 'Swamp Palace - Boss': (0x180154, False, 'with Arrghus'),
'Thieves\' Town - Big Key Chest': (0xEA04, False, 'in Thieves\' Town'), "Thieves' Town - Big Key Chest": (0xea04, False, "in Thieves' Town"),
'Thieves\' Town - Map Chest': (0xEA01, False, 'in Thieves\' Town'), "Thieves' Town - Map Chest": (0xea01, False, "in Thieves' Town"),
'Thieves\' Town - Compass Chest': (0xEA07, False, 'in Thieves\' Town'), "Thieves' Town - Compass Chest": (0xea07, False, "in Thieves' Town"),
'Thieves\' Town - Ambush Chest': (0xEA0A, False, 'in Thieves\' Town'), "Thieves' Town - Ambush Chest": (0xea0a, False, "in Thieves' Town"),
'Thieves\' Town - Attic': (0xEA0D, False, 'in Thieves\' Town'), "Thieves' Town - Attic": (0xea0d, False, "in Thieves' Town"),
'Thieves\' Town - Big Chest': (0xEA10, False, 'in Thieves\' Town'), "Thieves' Town - Big Chest": (0xea10, False, "in Thieves' Town"),
'Thieves\' Town - Blind\'s Cell': (0xEA13, False, 'in Thieves\' Town'), "Thieves' Town - Blind's Cell": (0xea13, False, "in Thieves' Town"),
'Thieves\' Town - Boss': (0x180156, False, 'with Blind'), "Thieves' Town - Boss": (0x180156, False, 'with Blind'),
'Skull Woods - Compass Chest': (0xE992, False, 'in Skull Woods'), 'Skull Woods - Compass Chest': (0xe992, False, 'in Skull Woods'),
'Skull Woods - Map Chest': (0xE99B, False, 'in Skull Woods'), 'Skull Woods - Map Chest': (0xe99b, False, 'in Skull Woods'),
'Skull Woods - Big Chest': (0xE998, False, 'in Skull Woods'), 'Skull Woods - Big Chest': (0xe998, False, 'in Skull Woods'),
'Skull Woods - Pot Prison': (0xE9A1, False, 'in Skull Woods'), 'Skull Woods - Pot Prison': (0xe9a1, False, 'in Skull Woods'),
'Skull Woods - Pinball Room': (0xE9C8, False, 'in Skull Woods'), 'Skull Woods - Pinball Room': (0xe9c8, False, 'in Skull Woods'),
'Skull Woods - Big Key Chest': (0xE99E, False, 'in Skull Woods'), 'Skull Woods - Big Key Chest': (0xe99e, False, 'in Skull Woods'),
'Skull Woods - Bridge Room': (0xE9FE, False, 'near Mothula'), 'Skull Woods - Bridge Room': (0xe9fe, False, 'near Mothula'),
'Skull Woods - Boss': (0x180155, False, 'with Mothula'), 'Skull Woods - Boss': (0x180155, False, 'with Mothula'),
'Ice Palace - Compass Chest': (0xE9D4, False, 'in Ice Palace'), 'Ice Palace - Compass Chest': (0xe9d4, False, 'in Ice Palace'),
'Ice Palace - Freezor Chest': (0xE995, False, 'in Ice Palace'), 'Ice Palace - Freezor Chest': (0xe995, False, 'in Ice Palace'),
'Ice Palace - Big Chest': (0xE9AA, False, 'in Ice Palace'), 'Ice Palace - Big Chest': (0xe9aa, False, 'in Ice Palace'),
'Ice Palace - Iced T Room': (0xE9E3, False, 'in Ice Palace'), 'Ice Palace - Iced T Room': (0xe9e3, False, 'in Ice Palace'),
'Ice Palace - Spike Room': (0xE9E0, False, 'in Ice Palace'), 'Ice Palace - Spike Room': (0xe9e0, False, 'in Ice Palace'),
'Ice Palace - Big Key Chest': (0xE9A4, False, 'in Ice Palace'), 'Ice Palace - Big Key Chest': (0xe9a4, False, 'in Ice Palace'),
'Ice Palace - Map Chest': (0xE9DD, False, 'in Ice Palace'), 'Ice Palace - Map Chest': (0xe9dd, False, 'in Ice Palace'),
'Ice Palace - Boss': (0x180157, False, 'with Kholdstare'), 'Ice Palace - Boss': (0x180157, False, 'with Kholdstare'),
'Misery Mire - Big Chest': (0xEA67, False, 'in Misery Mire'), 'Misery Mire - Big Chest': (0xea67, False, 'in Misery Mire'),
'Misery Mire - Map Chest': (0xEA6A, False, 'in Misery Mire'), 'Misery Mire - Map Chest': (0xea6a, False, 'in Misery Mire'),
'Misery Mire - Main Lobby': (0xEA5E, False, 'in Misery Mire'), 'Misery Mire - Main Lobby': (0xea5e, False, 'in Misery Mire'),
'Misery Mire - Bridge Chest': (0xEA61, False, 'in Misery Mire'), 'Misery Mire - Bridge Chest': (0xea61, False, 'in Misery Mire'),
'Misery Mire - Spike Chest': (0xE9DA, False, 'in Misery Mire'), 'Misery Mire - Spike Chest': (0xe9da, False, 'in Misery Mire'),
'Misery Mire - Compass Chest': (0xEA64, False, 'in Misery Mire'), 'Misery Mire - Compass Chest': (0xea64, False, 'in Misery Mire'),
'Misery Mire - Big Key Chest': (0xEA6D, False, 'in Misery Mire'), 'Misery Mire - Big Key Chest': (0xea6d, False, 'in Misery Mire'),
'Misery Mire - Boss': (0x180158, False, 'with Vitreous'), 'Misery Mire - Boss': (0x180158, False, 'with Vitreous'),
'Turtle Rock - Compass Chest': (0xEA22, False, 'in Turtle Rock'), 'Turtle Rock - Compass Chest': (0xea22, False, 'in Turtle Rock'),
'Turtle Rock - Roller Room - Left': (0xEA1C, False, 'in Turtle Rock'), 'Turtle Rock - Roller Room - Left': (0xea1c, False, 'in Turtle Rock'),
'Turtle Rock - Roller Room - Right': (0xEA1F, False, 'in Turtle Rock'), 'Turtle Rock - Roller Room - Right': (0xea1f, False, 'in Turtle Rock'),
'Turtle Rock - Chain Chomps': (0xEA16, False, 'in Turtle Rock'), 'Turtle Rock - Chain Chomps': (0xea16, False, 'in Turtle Rock'),
'Turtle Rock - Big Key Chest': (0xEA25, False, 'in Turtle Rock'), 'Turtle Rock - Big Key Chest': (0xea25, False, 'in Turtle Rock'),
'Turtle Rock - Big Chest': (0xEA19, False, 'in Turtle Rock'), 'Turtle Rock - Big Chest': (0xea19, False, 'in Turtle Rock'),
'Turtle Rock - Crystaroller Room': (0xEA34, False, 'in Turtle Rock'), 'Turtle Rock - Crystaroller Room': (0xea34, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Bottom Left': (0xEA31, False, 'in Turtle Rock'), 'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Bottom Right': (0xEA2E, False, 'in Turtle Rock'), 'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Top Left': (0xEA2B, False, 'in Turtle Rock'), 'Turtle Rock - Eye Bridge - Top Left': (0xea2b, False, 'in Turtle Rock'),
'Turtle Rock - Eye Bridge - Top Right': (0xEA28, False, 'in Turtle Rock'), 'Turtle Rock - Eye Bridge - Top Right': (0xea28, False, 'in Turtle Rock'),
'Turtle Rock - Boss': (0x180159, False, 'with Trinexx'), 'Turtle Rock - Boss': (0x180159, False, 'with Trinexx'),
'Palace of Darkness - Shooter Room': (0xEA5B, False, 'in Palace of Darkness'), 'Palace of Darkness - Shooter Room': (0xea5b, False, 'in Palace of Darkness'),
'Palace of Darkness - The Arena - Bridge': (0xEA3D, False, 'in Palace of Darkness'), 'Palace of Darkness - The Arena - Bridge': (0xea3d, False, 'in Palace of Darkness'),
'Palace of Darkness - Stalfos Basement': (0xEA49, False, 'in Palace of Darkness'), 'Palace of Darkness - Stalfos Basement': (0xea49, False, 'in Palace of Darkness'),
'Palace of Darkness - Big Key Chest': (0xEA37, False, 'in Palace of Darkness'), 'Palace of Darkness - Big Key Chest': (0xea37, False, 'in Palace of Darkness'),
'Palace of Darkness - The Arena - Ledge': (0xEA3A, False, 'in Palace of Darkness'), 'Palace of Darkness - The Arena - Ledge': (0xea3a, False, 'in Palace of Darkness'),
'Palace of Darkness - Map Chest': (0xEA52, False, 'in Palace of Darkness'), 'Palace of Darkness - Map Chest': (0xea52, False, 'in Palace of Darkness'),
'Palace of Darkness - Compass Chest': (0xEA43, False, 'in Palace of Darkness'), 'Palace of Darkness - Compass Chest': (0xea43, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Basement - Left': (0xEA4C, False, 'in Palace of Darkness'), 'Palace of Darkness - Dark Basement - Left': (0xea4c, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Basement - Right': (0xEA4F, False, 'in Palace of Darkness'), 'Palace of Darkness - Dark Basement - Right': (0xea4f, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Maze - Top': (0xEA55, False, 'in Palace of Darkness'), 'Palace of Darkness - Dark Maze - Top': (0xea55, False, 'in Palace of Darkness'),
'Palace of Darkness - Dark Maze - Bottom': (0xEA58, False, 'in Palace of Darkness'), 'Palace of Darkness - Dark Maze - Bottom': (0xea58, False, 'in Palace of Darkness'),
'Palace of Darkness - Big Chest': (0xEA40, False, 'in Palace of Darkness'), 'Palace of Darkness - Big Chest': (0xea40, False, 'in Palace of Darkness'),
'Palace of Darkness - Harmless Hellway': (0xEA46, False, 'in Palace of Darkness'), 'Palace of Darkness - Harmless Hellway': (0xea46, False, 'in Palace of Darkness'),
'Palace of Darkness - Boss': (0x180153, False, 'with Helmasaur King'), 'Palace of Darkness - Boss': (0x180153, False, 'with Helmasaur King'),
'Ganons Tower - Bob\'s Torch': (0x180161, False, 'in Ganon\'s Tower'), "Ganons Tower - Bob's Torch": (0x180161, False, "in Ganon's Tower"),
'Ganons Tower - Hope Room - Left': (0xEAD9, False, 'in Ganon\'s Tower'), 'Ganons Tower - Hope Room - Left': (0xead9, False, "in Ganon's Tower"),
'Ganons Tower - Hope Room - Right': (0xEADC, False, 'in Ganon\'s Tower'), 'Ganons Tower - Hope Room - Right': (0xeadc, False, "in Ganon's Tower"),
'Ganons Tower - Tile Room': (0xEAE2, False, 'in Ganon\'s Tower'), 'Ganons Tower - Tile Room': (0xeae2, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Top Left': (0xEAE5, False, 'in Ganon\'s Tower'), 'Ganons Tower - Compass Room - Top Left': (0xeae5, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Top Right': (0xEAE8, False, 'in Ganon\'s Tower'), 'Ganons Tower - Compass Room - Top Right': (0xeae8, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Bottom Left': (0xEAEB, False, 'in Ganon\'s Tower'), 'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, False, "in Ganon's Tower"),
'Ganons Tower - Compass Room - Bottom Right': (0xEAEE, False, 'in Ganon\'s Tower'), 'Ganons Tower - Compass Room - Bottom Right': (0xeaee, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Top Left': (0xEAB8, False, 'in Ganon\'s Tower'), 'Ganons Tower - DMs Room - Top Left': (0xeab8, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Top Right': (0xEABB, False, 'in Ganon\'s Tower'), 'Ganons Tower - DMs Room - Top Right': (0xeabb, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Bottom Left': (0xEABE, False, 'in Ganon\'s Tower'), 'Ganons Tower - DMs Room - Bottom Left': (0xeabe, False, "in Ganon's Tower"),
'Ganons Tower - DMs Room - Bottom Right': (0xEAC1, False, 'in Ganon\'s Tower'), 'Ganons Tower - DMs Room - Bottom Right': (0xeac1, False, "in Ganon's Tower"),
'Ganons Tower - Map Chest': (0xEAD3, False, 'in Ganon\'s Tower'), 'Ganons Tower - Map Chest': (0xead3, False, "in Ganon's Tower"),
'Ganons Tower - Firesnake Room': (0xEAD0, False, 'in Ganon\'s Tower'), 'Ganons Tower - Firesnake Room': (0xead0, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Top Left': (0xEAC4, False, 'in Ganon\'s Tower'), 'Ganons Tower - Randomizer Room - Top Left': (0xeac4, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Top Right': (0xEAC7, False, 'in Ganon\'s Tower'), 'Ganons Tower - Randomizer Room - Top Right': (0xeac7, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Bottom Left': (0xEACA, False, 'in Ganon\'s Tower'), 'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, False, "in Ganon's Tower"),
'Ganons Tower - Randomizer Room - Bottom Right': (0xEACD, False, 'in Ganon\'s Tower'), 'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, False, "in Ganon's Tower"),
'Ganons Tower - Bob\'s Chest': (0xEADF, False, 'in Ganon\'s Tower'), "Ganons Tower - Bob's Chest": (0xeadf, False, "in Ganon's Tower"),
'Ganons Tower - Big Chest': (0xEAD6, False, 'in Ganon\'s Tower'), 'Ganons Tower - Big Chest': (0xead6, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Room - Left': (0xEAF4, False, 'in Ganon\'s Tower'), 'Ganons Tower - Big Key Room - Left': (0xeaf4, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Room - Right': (0xEAF7, False, 'in Ganon\'s Tower'), 'Ganons Tower - Big Key Room - Right': (0xeaf7, False, "in Ganon's Tower"),
'Ganons Tower - Big Key Chest': (0xEAF1, False, 'in Ganon\'s Tower'), 'Ganons Tower - Big Key Chest': (0xeaf1, False, "in Ganon's Tower"),
'Ganons Tower - Mini Helmasaur Room - Left': (0xEAFD, False, 'atop Ganon\'s Tower'), 'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, False, "atop Ganon's Tower"),
'Ganons Tower - Mini Helmasaur Room - Right': (0xEB00, False, 'atop Ganon\'s Tower'), 'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, False, "atop Ganon's Tower"),
'Ganons Tower - Pre-Moldorm Chest': (0xEB03, False, 'atop Ganon\'s Tower'), 'Ganons Tower - Pre-Moldorm Chest': (0xeb03, False, "atop Ganon's Tower"),
'Ganons Tower - Validation Chest': (0xEB06, False, 'atop Ganon\'s Tower'), 'Ganons Tower - Validation Chest': (0xeb06, False, "atop Ganon's Tower"),
'Ganon': (None, False, 'from me'), 'Ganon': (None, False, 'from me'),
'Agahnim 1': (None, False, 'from Ganon\'s wizardry form'), 'Agahnim 1': (None, False, 'from Ganon\'s wizardry form'),
'Agahnim 2': (None, False, 'from Ganon\'s wizardry form'), 'Agahnim 2': (None, False, 'from Ganon\'s wizardry form'),

175
Rom.py
View File

@ -6,13 +6,13 @@ import os
import struct import struct
import random import random
from BaseClasses import ShopType from BaseClasses import ShopType, Region, Location, Item
from Dungeons import dungeon_music_addresses from Dungeons import dungeon_music_addresses
from Text import MultiByteTextMapper, text_addresses, Credits, TextTable from Text import MultiByteTextMapper, text_addresses, Credits, TextTable
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
from Utils import local_path, int16_as_bytes, int32_as_bytes from Utils import local_path, int16_as_bytes, int32_as_bytes
from Items import ItemFactory from Items import ItemFactory, item_table
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
@ -22,6 +22,7 @@ RANDOMIZERBASEHASH = 'cb560220b7b1b8202e92381aee19cd36'
class JsonRom(object): class JsonRom(object):
def __init__(self): def __init__(self):
self.name = None
self.patches = {} self.patches = {}
def write_byte(self, address, value): def write_byte(self, address, value):
@ -52,6 +53,7 @@ class JsonRom(object):
class LocalRom(object): class LocalRom(object):
def __init__(self, file, patch=True): def __init__(self, file, patch=True):
self.name = None
with open(file, 'rb') as stream: with open(file, 'rb') as stream:
self.buffer = read_rom(stream) self.buffer = read_rom(stream)
if patch: if patch:
@ -273,15 +275,18 @@ class Sprite(object):
# split into palettes of 15 colors # split into palettes of 15 colors
return array_chunk(palette_as_colors, 15) return array_chunk(palette_as_colors, 15)
def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): def patch_rom(world, player, rom, hashtable, beep='normal', color='red', sprite=None):
random.seed(world.rom_seeds[player])
# patch items # patch items
for location in world.get_locations(): for location in world.get_locations():
itemid = location.item.code if location.item is not None else 0x5A if location.player != player:
continue
if itemid is None or location.address is None:
itemid = location.item.code if location.item is not None else 0x5A
if location.address is None:
continue continue
locationaddress = location.address
if not location.crystal: if not location.crystal:
# Keys in their native dungeon should use the orignal item code for keys # Keys in their native dungeon should use the orignal item code for keys
if location.parent_region.dungeon: if location.parent_region.dungeon:
@ -291,10 +296,10 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
itemid = 0x32 itemid = 0x32
if location.item.type == "SmallKey": if location.item.type == "SmallKey":
itemid = 0x24 itemid = 0x24
rom.write_byte(locationaddress, itemid) rom.write_byte(location.address, itemid)
else: else:
# crystals # crystals
for address, value in zip(locationaddress, itemid): for address, value in zip(location.address, itemid):
rom.write_byte(address, value) rom.write_byte(address, value)
# patch music # patch music
@ -312,7 +317,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
# patch entrance/exits/holes # patch entrance/exits/holes
for region in world.regions: for region in world.regions:
for exit in region.exits: for exit in region.exits:
if exit.target is not None: if exit.target is not None and exit.player == player:
if isinstance(exit.addresses, tuple): if isinstance(exit.addresses, tuple):
offset = exit.target offset = exit.target
room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = exit.addresses room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = exit.addresses
@ -360,25 +365,25 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
# patch door table # patch door table
rom.write_byte(0xDBB73 + exit.addresses, exit.target) rom.write_byte(0xDBB73 + exit.addresses, exit.target)
write_custom_shops(rom, world) write_custom_shops(rom, world, player)
# patch medallion requirements # patch medallion requirements
if world.required_medallions[0] == 'Bombos': if world.required_medallions[player][0] == 'Bombos':
rom.write_byte(0x180022, 0x00) # requirement rom.write_byte(0x180022, 0x00) # requirement
rom.write_byte(0x4FF2, 0x31) # sprite rom.write_byte(0x4FF2, 0x31) # sprite
rom.write_byte(0x50D1, 0x80) rom.write_byte(0x50D1, 0x80)
rom.write_byte(0x51B0, 0x00) rom.write_byte(0x51B0, 0x00)
elif world.required_medallions[0] == 'Quake': elif world.required_medallions[player][0] == 'Quake':
rom.write_byte(0x180022, 0x02) # requirement rom.write_byte(0x180022, 0x02) # requirement
rom.write_byte(0x4FF2, 0x31) # sprite rom.write_byte(0x4FF2, 0x31) # sprite
rom.write_byte(0x50D1, 0x88) rom.write_byte(0x50D1, 0x88)
rom.write_byte(0x51B0, 0x00) rom.write_byte(0x51B0, 0x00)
if world.required_medallions[1] == 'Bombos': if world.required_medallions[player][1] == 'Bombos':
rom.write_byte(0x180023, 0x00) # requirement rom.write_byte(0x180023, 0x00) # requirement
rom.write_byte(0x5020, 0x31) # sprite rom.write_byte(0x5020, 0x31) # sprite
rom.write_byte(0x50FF, 0x90) rom.write_byte(0x50FF, 0x90)
rom.write_byte(0x51DE, 0x00) rom.write_byte(0x51DE, 0x00)
elif world.required_medallions[1] == 'Ether': elif world.required_medallions[player][1] == 'Ether':
rom.write_byte(0x180023, 0x01) # requirement rom.write_byte(0x180023, 0x01) # requirement
rom.write_byte(0x5020, 0x31) # sprite rom.write_byte(0x5020, 0x31) # sprite
rom.write_byte(0x50FF, 0x98) rom.write_byte(0x50FF, 0x98)
@ -408,8 +413,8 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00) rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00)
GREEN_TWENTY_RUPEES = 0x47 GREEN_TWENTY_RUPEES = 0x47
TRIFORCE_PIECE = ItemFactory('Triforce Piece').code TRIFORCE_PIECE = ItemFactory('Triforce Piece', player).code
GREEN_CLOCK = ItemFactory('Green Clock').code GREEN_CLOCK = ItemFactory('Green Clock', player).code
rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on
# handle difficulty # handle difficulty
@ -713,7 +718,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
# assorted fixes # assorted fixes
rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark word dungion before killing aga1 rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark word dungion before killing aga1
rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death
rom.write_byte(0x180173, 0x01) # Bob is enabled rom.write_byte(0x180173, 0x01) # Bob is enabled
rom.write_byte(0x180168, 0x08) # Spike Cave Damage rom.write_byte(0x180168, 0x08) # Spike Cave Damage
rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) #Set spike cave and MM spike room Cape usage rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) #Set spike cave and MM spike room Cape usage
@ -801,18 +806,19 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved # patch swamp: Need to enable permanent drain of water as dam or swamp were moved
rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required else 0x00) rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required[player] else 0x00)
# powder patch: remove the need to leave the scrren after powder, since it causes problems for potion shop at race game # powder patch: remove the need to leave the screen after powder, since it causes problems for potion shop at race game
# temporarally we are just nopping out this check we will conver this to a rom fix soon. # temporarally we are just nopping out this check we will conver this to a rom fix soon.
rom.write_bytes(0x02F539, [0xEA, 0xEA, 0xEA, 0xEA, 0xEA] if world.powder_patch_required else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F]) rom.write_bytes(0x02F539, [0xEA, 0xEA, 0xEA, 0xEA, 0xEA] if world.powder_patch_required[player] else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F])
# allow smith into multi-entrance caves in appropriate shuffles # allow smith into multi-entrance caves in appropriate shuffles
if world.shuffle in ['restricted', 'full', 'crossed', 'insanity']: if world.shuffle in ['restricted', 'full', 'crossed', 'insanity']:
rom.write_byte(0x18004C, 0x01) rom.write_byte(0x18004C, 0x01)
# set correct flag for hera basement item # set correct flag for hera basement item
if world.get_location('Tower of Hera - Basement Cage').item is not None and world.get_location('Tower of Hera - Basement Cage').item.name == 'Small Key (Tower of Hera)': hera_basement = world.get_location('Tower of Hera - Basement Cage', player)
if hera_basement.item is not None and hera_basement.item.name == 'Small Key (Tower of Hera)' and hera_basement.item.player == player:
rom.write_byte(0x4E3BB, 0xE4) rom.write_byte(0x4E3BB, 0xE4)
else: else:
rom.write_byte(0x4E3BB, 0xEB) rom.write_byte(0x4E3BB, 0xEB)
@ -827,11 +833,13 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0xFED31, 0x2A) # preopen bombable exit rom.write_byte(0xFED31, 0x2A) # preopen bombable exit
rom.write_byte(0xFEE41, 0x2A) # preopen bombable exit rom.write_byte(0xFEE41, 0x2A) # preopen bombable exit
write_strings(rom, world) write_strings(rom, world, player)
# set rom name # set rom name
# 21 bytes # 21 bytes
rom.write_bytes(0x7FC0, bytearray('ER_062_%09d\0' % world.seed, 'utf8') + world.option_identifier.to_bytes(4, 'big')) rom.name = bytearray('ER062%09d' % world.seed, 'utf8') + world.option_identifier(7, player).to_bytes(7, 'big')
assert(len(rom.name) == 21)
rom.write_bytes(0x7FC0, rom.name)
# Write title screen Code # Write title screen Code
hashint = int(rom.get_hash(), 16) hashint = int(rom.get_hash(), 16)
@ -848,8 +856,8 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
return rom return rom
def write_custom_shops(rom, world): def write_custom_shops(rom, world, player):
shops = [shop for shop in world.shops if shop.replaceable and shop.active] shops = [shop for shop in world.shops if shop.replaceable and shop.active and shop.region.player == player]
shop_data = bytearray() shop_data = bytearray()
items_data = bytearray() items_data = bytearray()
@ -870,7 +878,7 @@ def write_custom_shops(rom, world):
for item in shop.inventory: for item in shop.inventory:
if item is None: if item is None:
break break
item_data = [shop_id, ItemFactory(item['item']).code] + int16_as_bytes(item['price']) + [item['max'], ItemFactory(item['replacement']).code if item['replacement'] else 0xFF] + int16_as_bytes(item['replacement_price']) item_data = [shop_id, ItemFactory(item['item'], player).code] + int16_as_bytes(item['price']) + [item['max'], ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF] + int16_as_bytes(item['replacement_price'])
items_data.extend(item_data) items_data.extend(item_data)
rom.write_bytes(0x184800, shop_data) rom.write_bytes(0x184800, shop_data)
@ -1013,7 +1021,7 @@ def write_string_to_rom(rom, target, string):
rom.write_bytes(address, MultiByteTextMapper.convert(string, maxbytes)) rom.write_bytes(address, MultiByteTextMapper.convert(string, maxbytes))
def write_strings(rom, world): def write_strings(rom, world, player):
tt = TextTable() tt = TextTable()
tt.removeUnwantedText() tt.removeUnwantedText()
@ -1022,6 +1030,17 @@ def write_strings(rom, world):
tt['kakariko_flophouse_man_no_flippers'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.' tt['kakariko_flophouse_man_no_flippers'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
tt['kakariko_flophouse_man'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.' tt['kakariko_flophouse_man'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
def hint_text(dest, ped_hint=False):
hint = dest.hint_text if not ped_hint else dest.pedestal_hint_text
if dest.player != player:
if ped_hint:
hint += " for p%d!" % dest.player
elif type(dest) in [Region, Location]:
hint += " in p%d's world" % dest.player
elif type(dest) is Item:
hint += " for p%d" % dest.player
return hint
# For hints, first we write hints about entrances, some from the inconvenient list others from all reasonable entrances. # For hints, first we write hints about entrances, some from the inconvenient list others from all reasonable entrances.
if world.hints: if world.hints:
tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles can have hints!' tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles can have hints!'
@ -1031,16 +1050,17 @@ def write_strings(rom, world):
entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'}) entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'})
hint_locations = HintLocations.copy() hint_locations = HintLocations.copy()
random.shuffle(hint_locations) random.shuffle(hint_locations)
all_entrances = world.get_entrances() all_entrances = [entrance for entrance in world.get_entrances() if entrance.player == player]
random.shuffle(all_entrances) random.shuffle(all_entrances)
hint_count = 4 hint_count = 4 if world.shuffle != 'vanilla' else 0
for entrance in all_entrances: for entrance in all_entrances:
if entrance.name in entrances_to_hint: if entrance.name in entrances_to_hint:
this_hint = entrances_to_hint[entrance.name] + ' leads to ' + entrance.connected_region.hint_text + '.' if hint_count > 0:
tt[hint_locations.pop(0)] = this_hint this_hint = entrances_to_hint[entrance.name] + ' leads to ' + hint_text(entrance.connected_region) + '.'
entrances_to_hint.pop(entrance.name) tt[hint_locations.pop(0)] = this_hint
hint_count -= 1 entrances_to_hint.pop(entrance.name)
if hint_count < 1: hint_count -= 1
else:
break break
entrances_to_hint.update(OtherEntrances) entrances_to_hint.update(OtherEntrances)
@ -1048,57 +1068,58 @@ def write_strings(rom, world):
entrances_to_hint.update(InsanityEntrances) entrances_to_hint.update(InsanityEntrances)
if world.shuffle_ganon: if world.shuffle_ganon:
entrances_to_hint.update({'Pyramid Ledge': 'The pyramid ledge'}) entrances_to_hint.update({'Pyramid Ledge': 'The pyramid ledge'})
hint_count = 4 hint_count = 4 if world.shuffle != 'vanilla' else 0
for entrance in all_entrances: for entrance in all_entrances:
if entrance.name in entrances_to_hint: if entrance.name in entrances_to_hint:
this_hint = entrances_to_hint[entrance.name] + ' leads to ' + entrance.connected_region.hint_text + '.' if hint_count > 0:
tt[hint_locations.pop(0)] = this_hint this_hint = entrances_to_hint[entrance.name] + ' leads to ' + hint_text(entrance.connected_region) + '.'
entrances_to_hint.pop(entrance.name) tt[hint_locations.pop(0)] = this_hint
hint_count -= 1 entrances_to_hint.pop(entrance.name)
if hint_count < 1: hint_count -= 1
else:
break break
# Next we write a few hints for specific inconvenient locations. We don't make many because in entrance this is highly unpredictable. # Next we write a few hints for specific inconvenient locations. We don't make many because in entrance this is highly unpredictable.
locations_to_hint = InconvenientLocations.copy() locations_to_hint = InconvenientLocations.copy()
random.shuffle(locations_to_hint) random.shuffle(locations_to_hint)
hint_count = 3 hint_count = 3 if world.shuffle != 'vanilla' else 4
del locations_to_hint[hint_count:] del locations_to_hint[hint_count:]
for location in locations_to_hint: for location in locations_to_hint:
if location == 'Swamp Left': if location == 'Swamp Left':
if random.randint(0, 1) == 0: if random.randint(0, 1) == 0:
first_item = world.get_location('Swamp Palace - West Chest').item.hint_text first_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item)
second_item = world.get_location('Swamp Palace - Big Key Chest').item.hint_text second_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item)
else: else:
second_item = world.get_location('Swamp Palace - West Chest').item.hint_text second_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item)
first_item = world.get_location('Swamp Palace - Big Key Chest').item.hint_text first_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item)
this_hint = ('The westmost chests in Swamp Palace contain ' + first_item + ' and ' + second_item + '.') this_hint = ('The westmost chests in Swamp Palace contain ' + first_item + ' and ' + second_item + '.')
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
elif location == 'Mire Left': elif location == 'Mire Left':
if random.randint(0, 1) == 0: if random.randint(0, 1) == 0:
first_item = world.get_location('Misery Mire - Compass Chest').item.hint_text first_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item)
second_item = world.get_location('Misery Mire - Big Key Chest').item.hint_text second_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item)
else: else:
second_item = world.get_location('Misery Mire - Compass Chest').item.hint_text second_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item)
first_item = world.get_location('Misery Mire - Big Key Chest').item.hint_text first_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item)
this_hint = ('The westmost chests in Misery Mire contain ' + first_item + ' and ' + second_item + '.') this_hint = ('The westmost chests in Misery Mire contain ' + first_item + ' and ' + second_item + '.')
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
elif location == 'Tower of Hera - Big Key Chest': elif location == 'Tower of Hera - Big Key Chest':
this_hint = 'Waiting in the Tower of Hera basement leads to ' + world.get_location(location).item.hint_text + '.' this_hint = 'Waiting in the Tower of Hera basement leads to ' + hint_text(world.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
elif location == 'Ganons Tower - Big Chest': elif location == 'Ganons Tower - Big Chest':
this_hint = 'The big chest in Ganon\'s Tower contains ' + world.get_location(location).item.hint_text + '.' this_hint = 'The big chest in Ganon\'s Tower contains ' + hint_text(world.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
elif location == 'Thieves\' Town - Big Chest': elif location == 'Thieves\' Town - Big Chest':
this_hint = 'The big chest in Thieves\' Town contains ' + world.get_location(location).item.hint_text + '.' this_hint = 'The big chest in Thieves\' Town contains ' + hint_text(world.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
elif location == 'Ice Palace - Big Chest': elif location == 'Ice Palace - Big Chest':
this_hint = 'The big chest in Ice Palace contains ' + world.get_location(location).item.hint_text + '.' this_hint = 'The big chest in Ice Palace contains ' + hint_text(world.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
elif location == 'Eastern Palace - Big Key Chest': elif location == 'Eastern Palace - Big Key Chest':
this_hint = 'The antifairy guarded chest in Eastern Palace contains ' + world.get_location(location).item.hint_text + '.' this_hint = 'The antifairy guarded chest in Eastern Palace contains ' + hint_text(world.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
else: else:
this_hint = location + ' leads to ' + world.get_location(location).item.hint_text + '.' this_hint = location + ' leads to ' + hint_text(world.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
# Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well. # Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well.
@ -1106,17 +1127,15 @@ def write_strings(rom, world):
if world.keysanity: if world.keysanity:
items_to_hint.extend(KeysanityItems) items_to_hint.extend(KeysanityItems)
random.shuffle(items_to_hint) random.shuffle(items_to_hint)
hint_count = 5 hint_count = 5 if world.shuffle != 'vanilla' else 7
while(hint_count > 0): while hint_count > 0:
this_item = items_to_hint.pop(0) this_item = items_to_hint.pop(0)
this_location = world.find_items(this_item) this_location = world.find_items(this_item, player)
random.shuffle(this_location) random.shuffle(this_location)
if this_location: if this_location:
this_hint = this_location[0].item.hint_text + ' can be found ' + this_location[0].hint_text + '.' this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.'
tt[hint_locations.pop(0)] = this_hint tt[hint_locations.pop(0)] = this_hint
hint_count -= 1 hint_count -= 1
else:
continue
# All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint isn't selected twice. # All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint isn't selected twice.
junk_hints = junk_texts.copy() junk_hints = junk_texts.copy()
@ -1125,16 +1144,16 @@ def write_strings(rom, world):
tt[location] = junk_hints.pop(0) tt[location] = junk_hints.pop(0)
# We still need the older hints of course. Those are done here. # We still need the older hints of course. Those are done here.
silverarrows = world.find_items('Silver Arrows') silverarrows = world.find_items('Silver Arrows', player)
random.shuffle(silverarrows) random.shuffle(silverarrows)
silverarrow_hint = (' %s?' % silverarrows[0].hint_text.replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!'
tt['ganon_phase_3'] = 'Did you find the silver arrows%s' % silverarrow_hint tt['ganon_phase_3'] = 'Did you find the silver arrows%s' % silverarrow_hint
crystal5 = world.find_items('Crystal 5')[0] crystal5 = world.find_items('Crystal 5', player)[0]
crystal6 = world.find_items('Crystal 6')[0] crystal6 = world.find_items('Crystal 6', player)[0]
tt['bomb_shop'] = 'Big Bomb?\nMy supply is blocked until you clear %s and %s.' % (crystal5.hint_text, crystal6.hint_text) tt['bomb_shop'] = 'Big Bomb?\nMy supply is blocked until you clear %s and %s.' % (crystal5.hint_text, crystal6.hint_text)
greenpendant = world.find_items('Green Pendant')[0] greenpendant = world.find_items('Green Pendant', player)[0]
tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant.hint_text tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant.hint_text
tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)] tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)]
@ -1154,32 +1173,32 @@ def write_strings(rom, world):
tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!' tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!'
tt['kakariko_tavern_fisherman'] = TavernMan_texts[random.randint(0, len(TavernMan_texts) - 1)] tt['kakariko_tavern_fisherman'] = TavernMan_texts[random.randint(0, len(TavernMan_texts) - 1)]
pedestalitem = world.get_location('Master Sword Pedestal').item pedestalitem = world.get_location('Master Sword Pedestal', player).item
pedestal_text = 'Some Hot Air' if pedestalitem is None else pedestalitem.pedestal_hint_text if pedestalitem.pedestal_hint_text is not None else 'Unknown Item' pedestal_text = 'Some Hot Air' if pedestalitem is None else hint_text(pedestalitem, True) if pedestalitem.pedestal_hint_text is not None else 'Unknown Item'
tt['mastersword_pedestal_translated'] = pedestal_text tt['mastersword_pedestal_translated'] = pedestal_text
pedestal_credit_text = 'and the Hot Air' if pedestalitem is None else pedestalitem.pedestal_credit_text if pedestalitem.pedestal_credit_text is not None else 'and the Unknown Item' pedestal_credit_text = 'and the Hot Air' if pedestalitem is None else pedestalitem.pedestal_credit_text if pedestalitem.pedestal_credit_text is not None else 'and the Unknown Item'
etheritem = world.get_location('Ether Tablet').item etheritem = world.get_location('Ether Tablet', player).item
ether_text = 'Some Hot Air' if etheritem is None else etheritem.pedestal_hint_text if etheritem.pedestal_hint_text is not None else 'Unknown Item' ether_text = 'Some Hot Air' if etheritem is None else hint_text(etheritem, True) if etheritem.pedestal_hint_text is not None else 'Unknown Item'
tt['tablet_ether_book'] = ether_text tt['tablet_ether_book'] = ether_text
bombositem = world.get_location('Bombos Tablet').item bombositem = world.get_location('Bombos Tablet', player).item
bombos_text = 'Some Hot Air' if bombositem is None else bombositem.pedestal_hint_text if bombositem.pedestal_hint_text is not None else 'Unknown Item' bombos_text = 'Some Hot Air' if bombositem is None else hint_text(bombositem, True) if bombositem.pedestal_hint_text is not None else 'Unknown Item'
tt['tablet_bombos_book'] = bombos_text tt['tablet_bombos_book'] = bombos_text
rom.write_bytes(0xE0000, tt.getBytes()) rom.write_bytes(0xE0000, tt.getBytes())
credits = Credits() credits = Credits()
sickkiditem = world.get_location('Sick Kid').item sickkiditem = world.get_location('Sick Kid', player).item
sickkiditem_text = random.choice(SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text sickkiditem_text = random.choice(SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text
zoraitem = world.get_location('King Zora').item zoraitem = world.get_location('King Zora', player).item
zoraitem_text = random.choice(Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text zoraitem_text = random.choice(Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text
magicshopitem = world.get_location('Potion Shop').item magicshopitem = world.get_location('Potion Shop', player).item
magicshopitem_text = random.choice(MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text magicshopitem_text = random.choice(MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text
fluteboyitem = world.get_location('Flute Spot').item fluteboyitem = world.get_location('Flute Spot', player).item
fluteboyitem_text = random.choice(FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text fluteboyitem_text = random.choice(FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text
credits.update_credits_line('castle', 0, random.choice(KingsReturn_texts)) credits.update_credits_line('castle', 0, random.choice(KingsReturn_texts))

775
Rules.py

File diff suppressed because it is too large Load Diff