From 54c53ea07e4591a53b1cfd2a0ffce3cc41878981 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Tue, 9 Jul 2019 20:26:45 -0400 Subject: [PATCH 01/52] Fix breakage from PyInstaller 3.5's release --- bundle/EntranceRandomizer.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundle/EntranceRandomizer.spec b/bundle/EntranceRandomizer.spec index c01d61556c..308a3ba912 100644 --- a/bundle/EntranceRandomizer.spec +++ b/bundle/EntranceRandomizer.spec @@ -24,7 +24,7 @@ exe = EXE(pyz, debug=False, strip=False, upx=False, - icon='data/ER.ico', + icon='../data/ER.ico', console=is_win ) coll = COLLECT(exe, a.binaries, @@ -35,5 +35,5 @@ coll = COLLECT(exe, name='EntranceRandomizer') app = BUNDLE(coll, name ='EntranceRandomizer.app', - icon = 'data/ER.icns', + icon = '../data/ER.icns', bundle_identifier = None) From d44d194de79cb3832183c97038b52e36e79751dc Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Mon, 8 Jul 2019 22:48:16 -0400 Subject: [PATCH 02/52] Switch to simpler caching system This should speed up generating the seeds the currently take the longest. Seems to have no impact on the average case. --- BaseClasses.py | 80 ++++++++++++++++++-------------------------------- Dungeons.py | 4 --- Main.py | 1 + 3 files changed, 29 insertions(+), 56 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 2395d32e4e..bacf336883 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -172,7 +172,6 @@ class World(object): '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): soft_collect(item) ret.sweep_for_events() - ret.clear_cached_unreachable() return ret def get_items(self): @@ -233,7 +232,6 @@ class World(object): def unlocks_new_location(self, item): temp_state = self.state.copy() - temp_state.clear_cached_unreachable() temp_state.collect(item, True) for location in self.get_unfilled_locations(): @@ -320,75 +318,50 @@ class CollectionState(object): def __init__(self, parent): self.prog_items = [] self.world = parent - self.region_cache = {} - self.location_cache = {} - self.entrance_cache = {} - self.recursion_count = 0 + self.reachable_regions = set() self.events = [] self.path = {} self.locations_checked = set() + self.stale = True + def update_reachable_regions(self): + self.stale=False - def clear_cached_unreachable(self): - # we only need to invalidate results which were False, places we could reach before we can still reach after adding more items - self.region_cache = {k: v for k, v in self.region_cache.items() if v} - self.location_cache = {k: v for k, v in self.location_cache.items() if v} - self.entrance_cache = {k: v for k, v in self.entrance_cache.items() if v} + new_regions = True + reachable_regions_count = len(self.reachable_regions) + while new_regions: + possible = [region for region in self.world.regions if region not in self.reachable_regions] + for candidate in possible: + if candidate.can_reach_private(self): + self.reachable_regions.add(candidate) + new_regions = len(self.reachable_regions) > reachable_regions_count + reachable_regions_count = len(self.reachable_regions) def copy(self): ret = CollectionState(self.world) ret.prog_items = copy.copy(self.prog_items) - ret.region_cache = copy.copy(self.region_cache) - ret.location_cache = copy.copy(self.location_cache) - ret.entrance_cache = copy.copy(self.entrance_cache) + ret.reachable_regions = copy.copy(self.reachable_regions) ret.events = copy.copy(self.events) ret.path = copy.copy(self.path) ret.locations_checked = copy.copy(self.locations_checked) + ret.stale = True return ret def can_reach(self, spot, resolution_hint=None): try: spot_type = spot.spot_type - if spot_type == 'Location': - correct_cache = self.location_cache - elif spot_type == 'Region': - correct_cache = self.region_cache - elif spot_type == 'Entrance': - correct_cache = self.entrance_cache - else: - raise AttributeError except AttributeError: # try to resolve a name if resolution_hint == 'Location': spot = self.world.get_location(spot) - correct_cache = self.location_cache elif resolution_hint == 'Entrance': spot = self.world.get_entrance(spot) - correct_cache = self.entrance_cache else: # default to Region spot = self.world.get_region(spot) - correct_cache = self.region_cache + - if spot.recursion_count > 0: - return False - - if spot not in correct_cache: - # for the purpose of evaluating results, recursion is resolved by always denying recursive access (as that ia what we are trying to figure out right now in the first place - spot.recursion_count += 1 - self.recursion_count += 1 - can_reach = spot.can_reach(self) - spot.recursion_count -= 1 - self.recursion_count -= 1 - - # we only store qualified false results (i.e. ones not inside a hypothetical) - if not can_reach: - if self.recursion_count == 0: - correct_cache[spot] = can_reach - else: - correct_cache[spot] = can_reach - return can_reach - return correct_cache[spot] + return spot.can_reach(self) def sweep_for_events(self, key_only=False): # this may need improvement @@ -566,12 +539,12 @@ class CollectionState(object): elif event or item.advancement: self.prog_items.append(item.name) changed = True + + self.stale = True if changed: - self.clear_cached_unreachable() if not event: self.sweep_for_events() - self.clear_cached_unreachable() def remove(self, item): if item.advancement: @@ -603,10 +576,8 @@ class CollectionState(object): return # invalidate caches, nothing can be trusted anymore now - self.region_cache = {} - self.location_cache = {} - self.entrance_cache = {} - self.recursion_count = 0 + self.reachable_regions = set() + self.stale = True def __getattr__(self, item): if item.startswith('can_reach_'): @@ -647,6 +618,11 @@ class Region(object): self.recursion_count = 0 def can_reach(self, state): + if state.stale: + state.update_reachable_regions() + return self in state.reachable_regions + + def can_reach_private(self, state): for entrance in self.entrances: if state.can_reach(entrance): if not self in state.path: @@ -683,7 +659,7 @@ class Entrance(object): self.access_rule = lambda state: True def can_reach(self, state): - if self.access_rule(state) and state.can_reach(self.parent_region): + if state.can_reach(self.parent_region) and self.access_rule(state): if not self in state.path: state.path[self] = (self.name, state.path.get(self.parent_region, (self.parent_region.name, None))) return True @@ -768,7 +744,7 @@ class Location(object): 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))) def can_reach(self, state): - if self.access_rule(state) and state.can_reach(self.parent_region): + if state.can_reach(self.parent_region) and self.access_rule(state): return True return False diff --git a/Dungeons.py b/Dungeons.py index 1ef2c17db9..a53e0b4037 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -71,7 +71,6 @@ def fill_dungeons(world): world.push_item(bk_location, big_key, False) bk_location.event = True dungeon_locations.remove(bk_location) - all_state.clear_cached_unreachable() big_key = None # next place small keys @@ -97,7 +96,6 @@ def fill_dungeons(world): world.push_item(sk_location, small_key, False) sk_location.event = True dungeon_locations.remove(sk_location) - all_state.clear_cached_unreachable() if small_keys: # key placement not finished, loop again @@ -109,7 +107,6 @@ def fill_dungeons(world): di_location = dungeon_locations.pop() world.push_item(di_location, dungeon_item, False) - world.state.clear_cached_unreachable() def get_dungeon_item_pool(world): return [item for dungeon in world.dungeons for item in dungeon.all_items if item.key or world.place_dungeon_items] @@ -142,7 +139,6 @@ def fill_dungeons_restrictive(world, shuffled_locations): fill_restrictive(world, all_state_base, shuffled_locations, dungeon_items) - world.state.clear_cached_unreachable() dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A], diff --git a/Main.py b/Main.py index c118299d09..d8de72ba02 100644 --- a/Main.py +++ b/Main.py @@ -200,6 +200,7 @@ def copy_world(world): # copy progress items in state ret.state.prog_items = list(world.state.prog_items) + ret.state.stale = True set_rules(ret) From 1a62b1da282de6fbbcfe2553681b2b6ebc768a4e Mon Sep 17 00:00:00 2001 From: Bonta-kun <40473493+Bonta0@users.noreply.github.com> Date: Thu, 18 Apr 2019 11:23:24 +0200 Subject: [PATCH 03/52] 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 --- .gitignore | 3 + AdjusterMain.py | 2 +- BaseClasses.py | 461 ++++++++++++----------- Bosses.py | 104 +++--- Dungeons.py | 70 ++-- EntranceRandomizer.py | 9 +- EntranceShuffle.py | 498 ++++++++++++------------- Fill.py | 162 ++++++-- Gui.py | 16 +- ItemList.py | 146 ++++---- Items.py | 4 +- Main.py | 125 ++++--- Plando.py | 36 +- Regions.py | 846 +++++++++++++++++++++--------------------- Rom.py | 175 +++++---- Rules.py | 775 +++++++++++++++++++------------------- 16 files changed, 1821 insertions(+), 1611 deletions(-) diff --git a/.gitignore b/.gitignore index af03169dd8..4ac9fd67e7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ build bundle/components.wxs dist README.html +.vs/ +*multidata +*multisave diff --git a/AdjusterMain.py b/AdjusterMain.py index 346e9301c0..634661428b 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -21,7 +21,7 @@ def adjust(args): 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) else: raise RuntimeError('Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.') diff --git a/BaseClasses.py b/BaseClasses.py index bacf336883..9361236462 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -7,7 +7,8 @@ from Utils import int16_as_bytes 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.logic = logic self.mode = mode @@ -22,7 +23,7 @@ class World(object): self.itempool = [] self.seed = None 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_locations = None self._entrance_cache = {} @@ -32,10 +33,10 @@ class World(object): self.required_locations = [] self.place_dungeon_items = place_dungeon_items # configurable in future self.shuffle_bonk_prizes = False - self.swamp_patch_required = False - self.powder_patch_required = False - self.ganon_at_pyramid = True - self.ganonstower_vanilla = True + self.swamp_patch_required = {player: False for player in range(1, players + 1)} + self.powder_patch_required = {player: False for player in range(1, players + 1)} + self.ganon_at_pyramid = {player: True for player in range(1, players + 1)} + self.ganonstower_vanilla = {player: True for player in range(1, players + 1)} self.sewer_light_cone = mode == 'standard' self.light_world_light_cone = False self.dark_world_light_cone = False @@ -78,52 +79,52 @@ class World(object): for region in self.regions: region.world = self - def get_region(self, regionname): + def get_region(self, regionname, player): if isinstance(regionname, Region): return regionname try: - return self._region_cache[regionname] + return self._region_cache[(regionname, player)] except KeyError: for region in self.regions: - if region.name == regionname: - self._region_cache[regionname] = region + if region.name == regionname and region.player == player: + self._region_cache[(regionname, player)] = 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): return entrance try: - return self._entrance_cache[entrance] + return self._entrance_cache[(entrance, player)] except KeyError: for region in self.regions: for exit in region.exits: - if exit.name == entrance: - self._entrance_cache[entrance] = exit + if exit.name == entrance and exit.player == player: + self._entrance_cache[(entrance, player)] = 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): return location try: - return self._location_cache[location] + return self._location_cache[(location, player)] except KeyError: for region in self.regions: for r_location in region.locations: - if r_location.name == location: - self._location_cache[location] = r_location + if r_location.name == location and r_location.player == player: + self._location_cache[(location, player)] = 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): return dungeonname for dungeon in self.dungeons: - if dungeon.name == dungeonname: + if dungeon.name == dungeonname and dungeon.player == player: 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): ret = CollectionState(self) @@ -131,58 +132,61 @@ class World(object): def soft_collect(item): if item.name.startswith('Progressive '): if 'Sword' in item.name: - if ret.has('Golden Sword'): + if ret.has('Golden Sword', item.player): pass - elif ret.has('Tempered Sword') and self.difficulty_requirements.progressive_sword_limit >= 4: - ret.prog_items.append('Golden Sword') - elif ret.has('Master Sword') and self.difficulty_requirements.progressive_sword_limit >= 3: - ret.prog_items.append('Tempered Sword') - elif ret.has('Fighter Sword') and self.difficulty_requirements.progressive_sword_limit >= 2: - ret.prog_items.append('Master Sword') + elif ret.has('Tempered Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 4: + ret.prog_items.append(('Golden Sword', item.player)) + elif ret.has('Master Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 3: + ret.prog_items.append(('Tempered Sword', item.player)) + elif ret.has('Fighter Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 2: + ret.prog_items.append(('Master Sword', item.player)) 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: - if ret.has('Titans Mitts'): + if ret.has('Titans Mitts', item.player): pass - elif ret.has('Power Glove'): - ret.prog_items.append('Titans Mitts') + elif ret.has('Power Glove', item.player): + ret.prog_items.append(('Titans Mitts', item.player)) else: - ret.prog_items.append('Power Glove') + ret.prog_items.append(('Power Glove', item.player)) elif 'Shield' in item.name: - if ret.has('Mirror Shield'): + if ret.has('Mirror Shield', item.player): pass - elif ret.has('Red Shield') and self.difficulty_requirements.progressive_shield_limit >= 3: - ret.prog_items.append('Mirror Shield') - elif ret.has('Blue Shield') and self.difficulty_requirements.progressive_shield_limit >= 2: - ret.prog_items.append('Red Shield') + elif ret.has('Red Shield', item.player) and self.difficulty_requirements.progressive_shield_limit >= 3: + ret.prog_items.append(('Mirror Shield', item.player)) + elif ret.has('Blue Shield', item.player) and self.difficulty_requirements.progressive_shield_limit >= 2: + ret.prog_items.append(('Red Shield', item.player)) 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'): - if ret.bottle_count() < self.difficulty_requirements.progressive_bottle_limit: - ret.prog_items.append(item.name) + if ret.bottle_count(item.player) < self.difficulty_requirements.progressive_bottle_limit: + ret.prog_items.append((item.name, item.player)) 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: soft_collect(item) + if keys: - from Items import ItemFactory - 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)', - '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)', - '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): - soft_collect(item) + for p in range(1, self.players + 1): + from Items import ItemFactory + 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)', + '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)', + '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() return ret def get_items(self): return [loc.item for loc in self.get_filled_locations()] + self.itempool - def find_items(self, item): - return [location for location in self.get_locations() if location.item is not None and location.item.name == 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 and location.item.player == player] def push_item(self, location, item, collect=True): 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): location.item = item @@ -214,21 +218,21 @@ class World(object): def clear_location_cache(self): self._cached_locations = None - def get_unfilled_locations(self): - return [location for location in self.get_locations() if location.item is None] + def get_unfilled_locations(self, player=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): - return [location for location in self.get_locations() if location.item is not None] + def get_filled_locations(self, player=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: 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: 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): temp_state = self.state.copy() @@ -241,11 +245,10 @@ class World(object): return False 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 - if self.goal in ['triforcehunt']: - if state.item_count('Triforce Piece') + state.item_count('Power Star') > self.treasure_hunt_count: - return True return False 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] - 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: sphere = [] # 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: if state.can_reach(location): 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']: - treasure_pieces_collected += 1 - if self.goal in ['triforcehunt'] and treasure_pieces_collected >= self.treasure_hunt_count: + treasure_pieces_collected[location.item.player] += 1 + 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 sphere.append(location) @@ -283,8 +290,7 @@ class World(object): return False - @property - def option_identifier(self): + def option_identifier(self, maxbytes, player): id_value = 0 id_value_max = 1 @@ -309,10 +315,11 @@ class World(object): markbool(self.shuffle_ganon) markbool(self.keysanity) 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 - class CollectionState(object): def __init__(self, parent): @@ -326,7 +333,6 @@ class CollectionState(object): def update_reachable_regions(self): self.stale=False - new_regions = True reachable_regions_count = len(self.reachable_regions) while new_regions: @@ -347,149 +353,149 @@ class CollectionState(object): ret.stale = True return ret - def can_reach(self, spot, resolution_hint=None): + def can_reach(self, spot, resolution_hint=None, player=None): try: spot_type = spot.spot_type except AttributeError: # try to resolve a name if resolution_hint == 'Location': - spot = self.world.get_location(spot) + spot = self.world.get_location(spot, player) elif resolution_hint == 'Entrance': - spot = self.world.get_entrance(spot) + spot = self.world.get_entrance(spot, player) else: # default to Region - spot = self.world.get_region(spot) + spot = self.world.get_region(spot, player) - 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 new_locations = True checked_locations = 0 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: - if event.name not in self.events: - self.events.append(event.name) + if (event.name, event.player) not in self.events: + self.events.append((event.name, event.player)) self.collect(event.item, True, event) new_locations = len(reachable_events) > checked_locations checked_locations = len(reachable_events) - - def has(self, item, count=1): + def has(self, item, player, count=1): if count == 1: - return item in self.prog_items - return self.item_count(item) >= count + return (item, player) in self.prog_items + 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: - return self.can_buy_unlimited('Small Key (Universal)') + return self.can_buy_unlimited('Small Key (Universal)', player) if count == 1: - return item in self.prog_items - return self.item_count(item) >= count + return (item, player) in self.prog_items + 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: - 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 False - def item_count(self, item): - return len([pritem for pritem in self.prog_items if pritem == item]) + def item_count(self, item, player): + return len([pritem for pritem in self.prog_items if pritem == (item, player)]) - def can_lift_rocks(self): - return self.has('Power Glove') or self.has('Titans Mitts') + def can_lift_rocks(self, player): + return self.has('Power Glove', player) or self.has('Titans Mitts', player) - def has_bottle(self): - return self.bottle_count() > 0 + def has_bottle(self, player): + return self.bottle_count(player) > 0 - def bottle_count(self): - return len([pritem for pritem in self.prog_items if pritem.startswith('Bottle')]) + def bottle_count(self, player): + 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 - 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 return ( - self.item_count('Boss Heart Container') - + self.item_count('Sanctuary Heart Container') - + self.item_count('Piece of Heart') // 4 + self.item_count('Boss Heart Container', player) + + self.item_count('Sanctuary Heart Container', player) + + self.item_count('Piece of Heart', player) // 4 + 3 # starting hearts ) - def can_lift_heavy_rocks(self): - return self.has('Titans Mitts') + def can_lift_heavy_rocks(self, player): + 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 - if self.has('Quarter Magic'): + if self.has('Quarter Magic', player): basemagic = 32 - elif self.has('Half Magic'): + elif self.has('Half Magic', player): 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: - 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: - 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: basemagic = basemagic else: - basemagic = basemagic + basemagic * self.bottle_count() + basemagic = basemagic + basemagic * self.bottle_count(player) return basemagic >= smallmagic - def can_kill_most_things(self, enemies=5): - return (self.has_blunt_weapon() - or self.has('Cane of Somaria') - or (self.has('Cane of Byrna') and (enemies < 6 or self.can_extend_magic())) - or self.can_shoot_arrows() - or self.has('Fire Rod') + def can_kill_most_things(self, player, enemies=5): + return (self.has_blunt_weapon(player) + or self.has('Cane of Somaria', player) + or (self.has('Cane of Byrna', player) and (enemies < 6 or self.can_extend_magic(player))) + or self.can_shoot_arrows(player) + or self.has('Fire Rod', player) ) - def can_shoot_arrows(self): + def can_shoot_arrows(self, player): 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) #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') + return self.has('Bow', player) and (self.has('Silver Arrows', player) or self.can_buy_unlimited('Single Arrow', player)) + return self.has('Bow', player) - def can_get_good_bee(self): - cave = self.world.get_region('Good Bee Cave') + def can_get_good_bee(self, player): + cave = self.world.get_region('Good Bee Cave', player) return ( - self.has_bottle() and - self.has('Bug Catching Net') and - (self.has_Boots() or (self.has_sword() and self.has('Quake'))) and + self.has_bottle(player) and + self.has('Bug Catching Net', player) and + (self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))) 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): - return self.has('Fighter Sword') or self.has('Master Sword') or self.has('Tempered Sword') or self.has('Golden Sword') + def has_sword(self, player): + 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): - return self.has('Master Sword') or self.has('Tempered Sword') or self.has('Golden Sword') + def has_beam_sword(self, player): + return self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', player) - def has_blunt_weapon(self): - return self.has_sword() or self.has('Hammer') + def has_blunt_weapon(self, player): + return self.has_sword(player) or self.has('Hammer', player) - def has_Mirror(self): - return self.has('Magic Mirror') + def has_Mirror(self, player): + return self.has('Magic Mirror', player) - def has_Boots(self): - return self.has('Pegasus Boots') + def has_Boots(self, player): + return self.has('Pegasus Boots', player) - def has_Pearl(self): - return self.has('Moon Pearl') + def has_Pearl(self, player): + return self.has('Moon Pearl', player) - def has_fire_source(self): - return self.has('Fire Rod') or self.has('Lamp') + def has_fire_source(self, player): + return self.has('Fire Rod', player) or self.has('Lamp', player) - def has_misery_mire_medallion(self): - return self.has(self.world.required_medallions[0]) + def has_misery_mire_medallion(self, player): + return self.has(self.world.required_medallions[player][0], player) - def has_turtle_rock_medallion(self): - return self.has(self.world.required_medallions[1]) + def has_turtle_rock_medallion(self, player): + return self.has(self.world.required_medallions[player][1], player) def collect(self, item, event=False, location=None): if location: @@ -497,47 +503,47 @@ class CollectionState(object): changed = False if item.name.startswith('Progressive '): if 'Sword' in item.name: - if self.has('Golden Sword'): + if self.has('Golden Sword', item.player): pass - elif self.has('Tempered Sword') and self.world.difficulty_requirements.progressive_sword_limit >= 4: - self.prog_items.append('Golden Sword') + elif self.has('Tempered Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 4: + self.prog_items.append(('Golden Sword', item.player)) changed = True - elif self.has('Master Sword') and self.world.difficulty_requirements.progressive_sword_limit >= 3: - self.prog_items.append('Tempered Sword') + elif self.has('Master Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 3: + self.prog_items.append(('Tempered Sword', item.player)) changed = True - elif self.has('Fighter Sword') and self.world.difficulty_requirements.progressive_sword_limit >= 2: - self.prog_items.append('Master Sword') + elif self.has('Fighter Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 2: + self.prog_items.append(('Master Sword', item.player)) changed = True 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 elif 'Glove' in item.name: - if self.has('Titans Mitts'): + if self.has('Titans Mitts', item.player): pass - elif self.has('Power Glove'): - self.prog_items.append('Titans Mitts') + elif self.has('Power Glove', item.player): + self.prog_items.append(('Titans Mitts', item.player)) changed = True else: - self.prog_items.append('Power Glove') + self.prog_items.append(('Power Glove', item.player)) changed = True elif 'Shield' in item.name: - if self.has('Mirror Shield'): + if self.has('Mirror Shield', item.player): pass - elif self.has('Red Shield') and self.world.difficulty_requirements.progressive_shield_limit >= 3: - self.prog_items.append('Mirror Shield') + elif self.has('Red Shield', item.player) and self.world.difficulty_requirements.progressive_shield_limit >= 3: + self.prog_items.append(('Mirror Shield', item.player)) changed = True - elif self.has('Blue Shield') and self.world.difficulty_requirements.progressive_shield_limit >= 2: - self.prog_items.append('Red Shield') + elif self.has('Blue Shield', item.player) and self.world.difficulty_requirements.progressive_shield_limit >= 2: + self.prog_items.append(('Red Shield', item.player)) changed = True 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 elif item.name.startswith('Bottle'): - if self.bottle_count() < self.world.difficulty_requirements.progressive_bottle_limit: - self.prog_items.append(item.name) + if self.bottle_count(item.player) < self.world.difficulty_requirements.progressive_bottle_limit: + self.prog_items.append((item.name, item.player)) changed = True elif event or item.advancement: - self.prog_items.append(item.name) + self.prog_items.append((item.name, item.player)) changed = True self.stale = True @@ -551,27 +557,27 @@ class CollectionState(object): to_remove = item.name if to_remove.startswith('Progressive '): if 'Sword' in to_remove: - if self.has('Golden Sword'): + if self.has('Golden Sword', item.player): to_remove = 'Golden Sword' - elif self.has('Tempered Sword'): + elif self.has('Tempered Sword', item.player): to_remove = 'Tempered Sword' - elif self.has('Master Sword'): + elif self.has('Master Sword', item.player): to_remove = 'Master Sword' - elif self.has('Fighter Sword'): + elif self.has('Fighter Sword', item.player): to_remove = 'Fighter Sword' else: to_remove = None elif 'Glove' in item.name: - if self.has('Titans Mitts'): + if self.has('Titans Mitts', item.player): to_remove = 'Titans Mitts' - elif self.has('Power Glove'): + elif self.has('Power Glove', item.player): to_remove = 'Power Glove' else: to_remove = None if to_remove is not None: try: - self.prog_items.remove(to_remove) + self.prog_items.remove((to_remove, item.player)) except ValueError: return @@ -582,8 +588,8 @@ class CollectionState(object): def __getattr__(self, item): if item.startswith('can_reach_'): return self.can_reach(item[10]) - elif item.startswith('has_'): - return self.has(item[4]) + #elif item.startswith('has_'): + # return self.has(item[4]) raise RuntimeError('Cannot parse %s.' % item) @@ -602,7 +608,7 @@ class RegionType(Enum): class Region(object): - def __init__(self, name, type, hint): + def __init__(self, name, type, hint, player): self.name = name self.type = type self.entrances = [] @@ -616,6 +622,7 @@ class Region(object): self.spot_type = 'Region' self.hint_text = hint self.recursion_count = 0 + self.player = player def can_reach(self, state): if state.stale: @@ -634,7 +641,7 @@ class Region(object): is_dungeon_item = item.key or item.map or item.compass sewer_hack = self.world.mode == 'standard' and item.name == 'Small Key (Escape)' 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 @@ -642,12 +649,12 @@ class Region(object): return str(self.__unicode__()) def __unicode__(self): - return '%s' % self.name + return '%s (Player %d)' % (self.name, self.player) class Entrance(object): - def __init__(self, name='', parent=None): + def __init__(self, player, name='', parent=None): self.name = name self.parent_region = parent self.connected_region = None @@ -657,6 +664,7 @@ class Entrance(object): self.recursion_count = 0 self.vanilla = None self.access_rule = lambda state: True + self.player = player def can_reach(self, state): if state.can_reach(self.parent_region) and self.access_rule(state): @@ -677,18 +685,19 @@ class Entrance(object): return str(self.__unicode__()) def __unicode__(self): - return '%s' % self.name + return '%s (Player %d)' % (self.name, self.player) 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.regions = regions self.big_key = big_key self.small_keys = small_keys self.dungeon_items = dungeon_items self.bosses = dict() + self.player = player @property def boss(self): @@ -707,25 +716,26 @@ class Dungeon(object): return self.dungeon_items + self.keys 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): return str(self.__unicode__()) def __unicode__(self): - return '%s' % self.name + return '%s (Player %d)' % (self.name, self.player) class Boss(object): - def __init__(self, name, enemizer_name, defeat_rule): + def __init__(self, name, enemizer_name, defeat_rule, player): self.name = name self.enemizer_name = enemizer_name self.defeat_rule = defeat_rule + self.player = player def can_defeat(self, state): - return self.defeat_rule(state) + return self.defeat_rule(state, self.player) 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.parent_region = parent self.item = None @@ -739,6 +749,7 @@ class Location(object): self.always_allow = lambda item, state: False self.access_rule = lambda state: True self.item_rule = lambda item: True + self.player = player 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))) @@ -752,12 +763,12 @@ class Location(object): return str(self.__unicode__()) def __unicode__(self): - return '%s' % self.name + return '%s (Player %d)' % (self.name, self.player) 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.advancement = advancement self.priority = priority @@ -771,6 +782,7 @@ class Item(object): self.hint_text = hint_text self.code = code self.location = None + self.player = player @property def key(self): @@ -792,7 +804,7 @@ class Item(object): return str(self.__unicode__()) def __unicode__(self): - return '%s' % self.name + return '%s (Player %d)' % (self.name, self.player) # have 6 address that need to be filled @@ -874,11 +886,14 @@ class Spoiler(object): self.shops = [] self.bosses = OrderedDict() - def set_entrance(self, entrance, exit, direction): - self.entrances[(entrance, direction)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)]) + def set_entrance(self, entrance, exit, direction, player): + self.entrances[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)]) 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() listed_locations = set() @@ -897,7 +912,7 @@ class Spoiler(object): 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] - 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) 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]) listed_locations.update(other_locations) + self.shops = [] for shop in self.world.shops: if not shop.active: continue - shopdata = {'location': shop.region.name, + shopdata = {'location': str(shop.region), 'type': 'Take Any' if shop.type == ShopType.TakeAny else 'Shop' } 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'] self.shops.append(shopdata) - self.bosses["Eastern Palace"] = self.world.get_dungeon("Eastern Palace").boss.name - self.bosses["Desert Palace"] = self.world.get_dungeon("Desert Palace").boss.name - self.bosses["Tower Of Hera"] = self.world.get_dungeon("Tower of Hera").boss.name - self.bosses["Hyrule Castle"] = "Agahnim" - self.bosses["Palace Of Darkness"] = self.world.get_dungeon("Palace of Darkness").boss.name - self.bosses["Swamp Palace"] = self.world.get_dungeon("Swamp Palace").boss.name - self.bosses["Skull Woods"] = self.world.get_dungeon("Skull Woods").boss.name - self.bosses["Thieves Town"] = self.world.get_dungeon("Thieves Town").boss.name - self.bosses["Ice Palace"] = self.world.get_dungeon("Ice Palace").boss.name - self.bosses["Misery Mire"] = self.world.get_dungeon("Misery Mire").boss.name - self.bosses["Turtle Rock"] = self.world.get_dungeon("Turtle Rock").boss.name - self.bosses["Ganons Tower Basement"] = self.world.get_dungeon('Ganons Tower').bosses['bottom'].name - self.bosses["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower').bosses['middle'].name - self.bosses["Ganons Tower Top"] = self.world.get_dungeon('Ganons Tower').bosses['top'].name - self.bosses["Ganons Tower"] = "Agahnim 2" - self.bosses["Ganon"] = "Ganon" + for player in range(1, self.world.players + 1): + self.bosses[str(player)] = OrderedDict() + self.bosses[str(player)]["Eastern Palace"] = self.world.get_dungeon("Eastern Palace", player).boss.name + self.bosses[str(player)]["Desert Palace"] = self.world.get_dungeon("Desert Palace", player).boss.name + self.bosses[str(player)]["Tower Of Hera"] = self.world.get_dungeon("Tower of Hera", player).boss.name + self.bosses[str(player)]["Hyrule Castle"] = "Agahnim" + self.bosses[str(player)]["Palace Of Darkness"] = self.world.get_dungeon("Palace of Darkness", player).boss.name + self.bosses[str(player)]["Swamp Palace"] = self.world.get_dungeon("Swamp Palace", player).boss.name + self.bosses[str(player)]["Skull Woods"] = self.world.get_dungeon("Skull Woods", player).boss.name + self.bosses[str(player)]["Thieves Town"] = self.world.get_dungeon("Thieves Town", player).boss.name + self.bosses[str(player)]["Ice Palace"] = self.world.get_dungeon("Ice Palace", player).boss.name + self.bosses[str(player)]["Misery Mire"] = self.world.get_dungeon("Misery Mire", player).boss.name + self.bosses[str(player)]["Turtle Rock"] = self.world.get_dungeon("Turtle Rock", player).boss.name + self.bosses[str(player)]["Ganons Tower Basement"] = self.world.get_dungeon('Ganons Tower', player).bosses['bottom'].name + self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower', player).bosses['middle'].name + 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 @@ -951,7 +969,8 @@ class Spoiler(object): 'quickswap': self.world.quickswap, 'fastmenu': self.world.fastmenu, 'disable_music': self.world.disable_music, - 'keysanity': self.world.keysanity} + 'keysanity': self.world.keysanity, + 'players': self.world.players} def to_json(self): 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('L\\R Quickswap enabled: %s\n' % ('Yes' if self.metadata['quickswap'] else 'No')) 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: 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\nMedallions') - outfile.write('\n\nMisery Mire Medallion: %s' % self.medallions['Misery Mire']) - outfile.write('\nTurtle Rock Medallion: %s' % self.medallions['Turtle Rock']) + 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\n') + for player in range(1, self.world.players + 1): + 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'.join(['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in grouping.items()])) outfile.write('\n\nShops:\n\n') diff --git a/Bosses.py b/Bosses.py index 20c7aabfba..43072966bb 100644 --- a/Bosses.py +++ b/Bosses.py @@ -4,101 +4,101 @@ import random from BaseClasses import Boss from Fill import FillError -def BossFactory(boss): +def BossFactory(boss, player): if boss is None: return None if boss in boss_table: 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) return None -def ArmosKnightsDefeatRule(state): +def ArmosKnightsDefeatRule(state, player): # Magic amounts are probably a bit overkill return ( - state.has_blunt_weapon() or - (state.has('Cane of Somaria') and state.can_extend_magic(10)) or - (state.has('Cane of Byrna') and state.can_extend_magic(16)) or - (state.has('Ice Rod') and state.can_extend_magic(32)) or - (state.has('Fire Rod') and state.can_extend_magic(32)) or - state.has('Blue Boomerang') or - state.has('Red Boomerang')) + state.has_blunt_weapon(player) or + (state.has('Cane of Somaria', player) and state.can_extend_magic(player, 10)) or + (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or + (state.has('Ice Rod', player) and state.can_extend_magic(player, 32)) or + (state.has('Fire Rod', player) and state.can_extend_magic(player, 32)) or + state.has('Blue Boomerang', player) or + state.has('Red Boomerang', player)) -def LanmolasDefeatRule(state): +def LanmolasDefeatRule(state, player): # TODO: Allow the canes here? return ( - state.has_blunt_weapon() or - state.has('Fire Rod') or - state.has('Ice Rod') or - state.can_shoot_arrows()) + state.has_blunt_weapon(player) or + state.has('Fire Rod', player) or + state.has('Ice Rod', player) or + state.can_shoot_arrows(player)) -def MoldormDefeatRule(state): - return state.has_blunt_weapon() +def MoldormDefeatRule(state, player): + return state.has_blunt_weapon(player) -def HelmasaurKingDefeatRule(state): - return state.has_blunt_weapon() or state.can_shoot_arrows() +def HelmasaurKingDefeatRule(state, player): + return state.has_blunt_weapon(player) or state.can_shoot_arrows(player) -def ArrghusDefeatRule(state): - if not state.has('Hookshot'): +def ArrghusDefeatRule(state, player): + if not state.has('Hookshot', player): return False # 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 # makes this complicated - if state.has_blunt_weapon(): + if state.has_blunt_weapon(player): 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 - (state.has('Ice Rod') and (state.can_shoot_arrows() or state.can_extend_magic(16)))) + 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', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16)))) -def MothulaDefeatRule(state): +def MothulaDefeatRule(state, player): return ( - state.has_blunt_weapon() or - (state.has('Fire Rod') and state.can_extend_magic(10)) or + state.has_blunt_weapon(player) 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 # 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 Byrna') and state.can_extend_magic(16)) or - state.can_get_good_bee() + (state.has('Cane of Somaria', player) and state.can_extend_magic(player, 16)) or + (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or + state.can_get_good_bee(player) ) -def BlindDefeatRule(state): - return state.has_blunt_weapon() or state.has('Cane of Somaria') or state.has('Cane of Byrna') +def BlindDefeatRule(state, player): + 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 ( ( - 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 - (state.has_sword() or state.world.mode == 'swordless') + (state.has_sword(player) or state.world.mode == 'swordless') ) ) and ( - state.has_blunt_weapon() or - (state.has('Fire Rod') and state.can_extend_magic(20)) or + state.has_blunt_weapon(player) 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 ( - state.has('Fire Rod') and - state.has('Bombos') and + state.has('Fire Rod', player) and + state.has('Bombos', player) and state.world.mode == 'swordless' and - state.can_extend_magic(16) + state.can_extend_magic(player, 16) ) ) ) -def VitreousDefeatRule(state): - return state.can_shoot_arrows() or state.has_blunt_weapon() +def VitreousDefeatRule(state, player): + return state.can_shoot_arrows(player) or state.has_blunt_weapon(player) -def TrinexxDefeatRule(state): - if not (state.has('Fire Rod') and state.has('Ice Rod')): +def TrinexxDefeatRule(state, player): + if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)): 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): - return state.has_sword() or state.has('Hammer') or state.has('Bug Catching Net') +def AgahnimDefeatRule(state, player): + return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player) boss_table = { 'Armos Knights': ('Armos', ArmosKnightsDefeatRule), @@ -137,7 +137,7 @@ def can_place_boss(world, boss, dungeon_name, level=None): return False return True -def place_bosses(world): +def place_bosses(world, player): if world.boss_shuffle == 'none': return # Most to least restrictive order @@ -162,7 +162,7 @@ def place_bosses(world): if world.boss_shuffle in ["basic", "normal"]: # temporary hack for swordless kholdstare: 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') boss_locations.remove(['Ice Palace', None]) placeable_bosses.remove('Kholdstare') @@ -183,7 +183,7 @@ def place_bosses(world): bosses.remove(boss) 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 for [loc, level] in boss_locations: 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) 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) diff --git a/Dungeons.py b/Dungeons.py index a53e0b4037..bed737302a 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -6,44 +6,45 @@ from Fill import fill_restrictive 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): - dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro else small_keys, dungeon_items) - dungeon.boss = BossFactory(default_boss) + dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro else small_keys, dungeon_items, player) + dungeon.boss = BossFactory(default_boss, player) for region in dungeon.regions: - world.get_region(region).dungeon = dungeon + world.get_region(region, player).dungeon = 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)')]) - EP = make_dungeon('Eastern Palace', 'Armos Knights', ['Eastern Palace'], ItemFactory('Big Key (Eastern Palace)'), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern 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)'), [ItemFactory('Small Key (Desert Palace)')], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'])) - 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)'])) - AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2), []) - 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)'])) - 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)'])) - 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)'])) - 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)'])) - 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)'])) - 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)'])) - 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)'])) - 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)'])) + 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)', 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)', 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)', 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, 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)', 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)', 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)', 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)', 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)', 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)', 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)', 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)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player)) - GT.bosses['bottom'] = BossFactory('Armos Knights') - GT.bosses['middle'] = BossFactory('Lanmolas') - GT.bosses['top'] = BossFactory('Moldorm') + GT.bosses['bottom'] = BossFactory('Armos Knights', player) + GT.bosses['middle'] = BossFactory('Lanmolas', player) + 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): 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() - if world.retro: - world.push_item(world.get_location('Skull Woods - Pinball Room'), ItemFactory('Small Key (Universal)'), False) - else: - world.push_item(world.get_location('Skull Woods - Pinball Room'), ItemFactory('Small Key (Skull Woods)'), False) - world.get_location('Skull Woods - Pinball Room').event = True + for player in range(1, world.players + 1): + if world.retro: + world.push_item(world.get_location('Skull Woods - Pinball Room', player), ItemFactory('Small Key (Universal)', player), False) + else: + 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] @@ -88,7 +89,7 @@ def fill_dungeons(world): small_keys.append(small_key) dungeons.append((dungeon_regions, big_key, small_keys, dungeon_items)) # infinite regression protection - if loopcnt < 30: + if loopcnt < (30 * world.players): break else: 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): all_state_base = world.get_all_state() - skull_woods_big_chest = world.get_location('Skull Woods - Pinball Room') - if world.retro: - world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Universal)'), False) - else: - world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)'), False) - skull_woods_big_chest.event = True - shuffled_locations.remove(skull_woods_big_chest) + for player in range(1, world.players + 1): + skull_woods_big_chest = world.get_location('Skull Woods - Pinball Room', player) + if world.retro: + world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Universal)', player), False) + else: + world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)', player), False) + skull_woods_big_chest.event = True + shuffled_locations.remove(skull_woods_big_chest) if world.keysanity: #in keysanity dungeon items are distributed as part of the normal item pool diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index fed67800a8..55bf1d585b 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -8,7 +8,7 @@ import sys from Gui import guiMain from Main import main -from Utils import is_bundled, close_console +from Utils import is_bundled, close_console, output_path class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): @@ -213,8 +213,15 @@ def start(): Output .json patch to stdout instead of a patched rom. Used 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() + if args.outputpath and os.path.isdir(args.outputpath): + output_path.cached_path = args.outputpath + if is_bundled() and len(sys.argv) == 1: # 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 diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 73709adb5a..0265dae842 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -3,9 +3,9 @@ import random # ToDo: With shuffle_ganon option, prevent gtower from linking to an exit only location through a 2 entrance cave. -def link_entrances(world): - connect_two_way(world, 'Links House', 'Links House Exit') # unshuffled. For now - connect_exit(world, 'Chris Houlihan Room Exit', 'Links House') # should always match link's house, except for plandos +def link_entrances(world, player): + connect_two_way(world, 'Links House', 'Links House Exit', player) # unshuffled. For now + connect_exit(world, 'Chris Houlihan Room Exit', 'Links House', player) # should always match link's house, except for plandos Dungeon_Exits = Dungeon_Exits_Base.copy() Cave_Exits = Cave_Exits_Base.copy() @@ -16,24 +16,24 @@ def link_entrances(world): # setup mandatory connections for exitname, regionname in mandatory_connections: - connect_simple(world, exitname, regionname) + connect_simple(world, exitname, regionname, player) # if we do not shuffle, set default connections if world.shuffle == 'vanilla': for exitname, regionname in default_connections: - connect_simple(world, exitname, regionname) + connect_simple(world, exitname, regionname, player) for exitname, regionname in default_dungeon_connections: - connect_simple(world, exitname, regionname) + connect_simple(world, exitname, regionname, player) elif world.shuffle == 'dungeonssimple': for exitname, regionname in default_connections: - connect_simple(world, exitname, regionname) + connect_simple(world, exitname, regionname, player) simple_shuffle_dungeons(world) elif world.shuffle == 'dungeonsfull': for exitname, regionname in default_connections: - connect_simple(world, exitname, regionname) + connect_simple(world, exitname, regionname, player) - skull_woods_shuffle(world) + skull_woods_shuffle(world, player) dungeon_exits = list(Dungeon_Exits) lw_entrances = list(LW_Dungeon_Entrances) @@ -41,26 +41,26 @@ def link_entrances(world): if world.mode == 'standard': # must connect front of hyrule castle to do escape - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) else: dungeon_exits.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) lw_entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) else: dw_entrances.append('Ganons Tower') dungeon_exits.append('Ganons Tower Exit') if world.mode == 'standard': # rest of hyrule castle must be in light world, so it has to be the one connected to east exit of desert - connect_mandatory_exits(world, lw_entrances, [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], list(LW_Dungeon_Entrances_Must_Exit)) + connect_mandatory_exits(world, lw_entrances, [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], list(LW_Dungeon_Entrances_Must_Exit), player) else: - connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit)) - connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit)) - connect_caves(world, lw_entrances, dw_entrances, dungeon_exits) + connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit), player) + connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit), player) + connect_caves(world, lw_entrances, dw_entrances, dungeon_exits, player) elif world.shuffle == 'simple': - simple_shuffle_dungeons(world) + simple_shuffle_dungeons(world, player) old_man_entrances = list(Old_Man_Entrances) caves = list(Cave_Exits) @@ -79,8 +79,8 @@ def link_entrances(world): while two_door_caves: entrance1, entrance2 = two_door_caves.pop() exit1, exit2 = caves.pop() - connect_two_way(world, entrance1, exit1) - connect_two_way(world, entrance2, exit2) + connect_two_way(world, entrance1, exit1, player) + connect_two_way(world, entrance2, exit2, player) # now the remaining pairs two_door_caves = list(Two_Door_Caves) @@ -88,8 +88,8 @@ def link_entrances(world): while two_door_caves: entrance1, entrance2 = two_door_caves.pop() exit1, exit2 = caves.pop() - connect_two_way(world, entrance1, exit1) - connect_two_way(world, entrance2, exit2) + connect_two_way(world, entrance1, exit1, player) + connect_two_way(world, entrance2, exit2, player) # at this point only Light World death mountain entrances remain # place old man, has limited options @@ -100,38 +100,38 @@ def link_entrances(world): remaining_entrances.extend(old_man_entrances) random.shuffle(remaining_entrances) old_man_entrance = remaining_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)') - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)') + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) # add old man house to ensure it is always somewhere on light death mountain caves.extend(list(Old_Man_House)) caves.extend(list(three_exit_caves)) # connect rest - connect_caves(world, remaining_entrances, [], caves) + connect_caves(world, remaining_entrances, [], caves, player) # scramble holes - scramble_holes(world) + scramble_holes(world, player) # place blacksmith, has limited options random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place bomb shop, has limited options random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) # place remaining doors - connect_doors(world, single_doors, door_targets) + connect_doors(world, single_doors, door_targets, player) elif world.shuffle == 'restricted': - simple_shuffle_dungeons(world) + simple_shuffle_dungeons(world, player) lw_entrances = list(LW_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances) dw_entrances = list(DW_Entrances + DW_Single_Cave_Doors) @@ -144,17 +144,17 @@ def link_entrances(world): door_targets = list(Single_Cave_Targets) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) # in restricted, the only mandatory exits are in dark world - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits) + connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in lw_entrances] random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)') + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) lw_entrances.remove(old_man_exit) # place blacksmith, has limited options @@ -163,7 +163,7 @@ def link_entrances(world): blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) if blacksmith_hut in lw_entrances: lw_entrances.remove(blacksmith_hut) if blacksmith_hut in dw_entrances: @@ -176,7 +176,7 @@ def link_entrances(world): bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) if bomb_shop in lw_entrances: lw_entrances.remove(bomb_shop) if bomb_shop in dw_entrances: @@ -185,24 +185,24 @@ def link_entrances(world): # place the old man cave's entrance somewhere in the light world random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)') + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) # place Old Man House in Light World - connect_caves(world, lw_entrances, [], list(Old_Man_House)) #for multiple seeds + connect_caves(world, lw_entrances, [], list(Old_Man_House), player) #for multiple seeds # now scramble the rest - connect_caves(world, lw_entrances, dw_entrances, caves) + connect_caves(world, lw_entrances, dw_entrances, caves, player) # scramble holes - scramble_holes(world) + scramble_holes(world, player) doors = lw_entrances + dw_entrances # place remaining doors - connect_doors(world, doors, door_targets) + connect_doors(world, doors, door_targets, player) elif world.shuffle == 'restricted_legacy': - simple_shuffle_dungeons(world) + simple_shuffle_dungeons(world, player) lw_entrances = list(LW_Entrances) dw_entrances = list(DW_Entrances) @@ -216,7 +216,7 @@ def link_entrances(world): door_targets = list(Single_Cave_Targets) # only use two exit caves to do mandatory dw connections - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits) + connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) # add three exit doors to pool for remainder caves.extend(three_exit_caves) @@ -227,37 +227,37 @@ def link_entrances(world): lw_entrances.extend(old_man_entrances) random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)') - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)') + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) # place Old Man House in Light World - connect_caves(world, lw_entrances, [], Old_Man_House) + connect_caves(world, lw_entrances, [], Old_Man_House, player) # connect rest. There's 2 dw entrances remaining, so we will not run into parity issue placing caves - connect_caves(world, lw_entrances, dw_entrances, caves) + connect_caves(world, lw_entrances, dw_entrances, caves, player) # scramble holes - scramble_holes(world) + scramble_holes(world, player) # place blacksmith, has limited options random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place dam and pyramid fairy, have limited options random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) # place remaining doors - connect_doors(world, single_doors, door_targets) + connect_doors(world, single_doors, door_targets, player) elif world.shuffle == 'full': - skull_woods_shuffle(world) + skull_woods_shuffle(world, player) lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances) dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances + DW_Single_Cave_Doors) @@ -271,17 +271,17 @@ def link_entrances(world): old_man_house = list(Old_Man_House) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) if world.mode == 'standard': # must connect front of hyrule castle to do escape - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) else: caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) lw_entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) else: dw_entrances.append('Ganons Tower') caves.append('Ganons Tower Exit') @@ -291,34 +291,34 @@ def link_entrances(world): #we also places the Old Man House at this time to make sure he can be connected to the desert one way if random.randint(0, 1) == 0: caves += old_man_house - connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits) + connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) try: caves.remove(old_man_house[0]) except ValueError: pass else: #if the cave wasn't placed we get here - connect_caves(world, lw_entrances, [], old_man_house) - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits) + connect_caves(world, lw_entrances, [], old_man_house, player) + connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) else: - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits) + connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) caves += old_man_house - connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits) + connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) try: caves.remove(old_man_house[0]) except ValueError: pass else: #if the cave wasn't placed we get here - connect_caves(world, lw_entrances, [], old_man_house) + connect_caves(world, lw_entrances, [], old_man_house, player) if world.mode == 'standard': # rest of hyrule castle must be in light world - connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')]) + connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player) # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in lw_entrances] random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)') + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) lw_entrances.remove(old_man_exit) # place blacksmith, has limited options @@ -327,7 +327,7 @@ def link_entrances(world): blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) if blacksmith_hut in lw_entrances: lw_entrances.remove(blacksmith_hut) if blacksmith_hut in dw_entrances: @@ -340,7 +340,7 @@ def link_entrances(world): bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) if bomb_shop in lw_entrances: lw_entrances.remove(bomb_shop) if bomb_shop in dw_entrances: @@ -348,21 +348,21 @@ def link_entrances(world): # place the old man cave's entrance somewhere in the light world old_man_entrance = lw_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)') + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) # now scramble the rest - connect_caves(world, lw_entrances, dw_entrances, caves) + connect_caves(world, lw_entrances, dw_entrances, caves, player) # scramble holes - scramble_holes(world) + scramble_holes(world, player) doors = lw_entrances + dw_entrances # place remaining doors - connect_doors(world, doors, door_targets) + connect_doors(world, doors, door_targets, player) elif world.shuffle == 'crossed': - skull_woods_shuffle(world) + skull_woods_shuffle(world, player) entrances = list(LW_Entrances + LW_Dungeon_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances + DW_Entrances + DW_Dungeon_Entrances + DW_Single_Cave_Doors) must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit) @@ -374,34 +374,34 @@ def link_entrances(world): door_targets = list(Single_Cave_Targets) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) if world.mode == 'standard': # must connect front of hyrule castle to do escape - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) else: caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) else: entrances.append('Ganons Tower') caves.append('Ganons Tower Exit') #place must-exit caves - connect_mandatory_exits(world, entrances, caves, must_exits) + connect_mandatory_exits(world, entrances, caves, must_exits, player) if world.mode == 'standard': # rest of hyrule castle must be dealt with - connect_caves(world, entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')]) + connect_caves(world, entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player) # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in entrances] random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)') + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) entrances.remove(old_man_exit) # place blacksmith, has limited options @@ -409,7 +409,7 @@ def link_entrances(world): blacksmith_doors = [door for door in blacksmith_doors if door in entrances] random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) entrances.remove(blacksmith_hut) bomb_shop_doors.extend(blacksmith_doors) @@ -419,26 +419,26 @@ def link_entrances(world): bomb_shop_doors = [door for door in bomb_shop_doors if door in entrances] random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) entrances.remove(bomb_shop) # place the old man cave's entrance somewhere random.shuffle(entrances) old_man_entrance = entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)') + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) # now scramble the rest - connect_caves(world, entrances, [], caves) + connect_caves(world, entrances, [], caves, player) # scramble holes - scramble_holes(world) + scramble_holes(world, player) # place remaining doors - connect_doors(world, entrances, door_targets) + connect_doors(world, entrances, door_targets, player) elif world.shuffle == 'full_legacy': - skull_woods_shuffle(world) + skull_woods_shuffle(world, player) lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances) dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances) @@ -453,27 +453,27 @@ def link_entrances(world): if world.mode == 'standard': # must connect front of hyrule castle to do escape - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) else: caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) lw_entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) else: dw_entrances.append('Ganons Tower') caves.append('Ganons Tower Exit') # we randomize which world requirements we fulfill first so we get better dungeon distribution if random.randint(0, 1) == 0: - connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits) - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits) + connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) + connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) else: - connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits) - connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits) + connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) + connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) if world.mode == 'standard': # rest of hyrule castle must be in light world - connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')]) + connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player) # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about @@ -484,35 +484,35 @@ def link_entrances(world): random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)') - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)') + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) # place Old Man House in Light World - connect_caves(world, lw_entrances, [], list(Old_Man_House)) #need this to avoid badness with multiple seeds + connect_caves(world, lw_entrances, [], list(Old_Man_House), player) #need this to avoid badness with multiple seeds # now scramble the rest - connect_caves(world, lw_entrances, dw_entrances, caves) + connect_caves(world, lw_entrances, dw_entrances, caves, player) # scramble holes - scramble_holes(world) + scramble_holes(world, player) # place blacksmith, has limited options random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place bomb shop, has limited options random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) # place remaining doors - connect_doors(world, single_doors, door_targets) + connect_doors(world, single_doors, door_targets, player) elif world.shuffle == 'madness_legacy': # here lie dragons, connections are no longer two way lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances) @@ -554,18 +554,18 @@ def link_entrances(world): if world.mode == 'standard': # cannot move uncle cave - connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance') - connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs') - connect_entrance(world, lw_doors.pop(), 'Hyrule Castle Secret Entrance Exit') + connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) + connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) + connect_entrance(world, lw_doors.pop(), 'Hyrule Castle Secret Entrance Exit', player) else: lw_hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) lw_entrances.append('Hyrule Castle Secret Entrance Stairs') if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit') - connect_entrance(world, 'Pyramid Hole', 'Pyramid') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) else: dw_entrances.append('Ganons Tower') caves.append('Ganons Tower Exit') @@ -587,29 +587,29 @@ def link_entrances(world): sw_hole_pool = dw_hole_entrances mandatory_dark_world.append('Skull Woods First Section Exit') for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)']: - connect_entrance(world, sw_hole_pool.pop(), target) + connect_entrance(world, sw_hole_pool.pop(), target, player) # sanctuary has to be in light world - connect_entrance(world, lw_hole_entrances.pop(), 'Sewer Drop') + connect_entrance(world, lw_hole_entrances.pop(), 'Sewer Drop', player) mandatory_light_world.append('Sanctuary Exit') # fill up remaining holes for hole in dw_hole_entrances: exits, target = hole_targets.pop() mandatory_dark_world.append(exits) - connect_entrance(world, hole, target) + connect_entrance(world, hole, target, player) for hole in lw_hole_entrances: exits, target = hole_targets.pop() mandatory_light_world.append(exits) - connect_entrance(world, hole, target) + connect_entrance(world, hole, target, player) # hyrule castle handling if world.mode == 'standard': # must connect front of hyrule castle to do escape - connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') + connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) random.shuffle(lw_entrances) - connect_exit(world, 'Hyrule Castle Exit (South)', lw_entrances.pop()) + connect_exit(world, 'Hyrule Castle Exit (South)', lw_entrances.pop(), player) mandatory_light_world.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) else: lw_doors.append('Hyrule Castle Entrance (South)') @@ -648,8 +648,8 @@ def link_entrances(world): exit = cave[-1] cave = cave[:-1] - connect_exit(world, exit, entrance) - connect_entrance(world, worldoors.pop(), exit) + connect_exit(world, exit, entrance, player) + connect_entrance(world, worldoors.pop(), exit, player) # rest of cave now is forced to be in this world worldspecific.append(cave) @@ -672,8 +672,8 @@ def link_entrances(world): old_man_exit = old_man_entrances.pop() lw_entrances.remove(old_man_exit) - connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit) - connect_entrance(world, lw_doors.pop(), 'Old Man Cave Exit (East)') + connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player) + connect_entrance(world, lw_doors.pop(), 'Old Man Cave Exit (East)', player) mandatory_light_world.append('Old Man Cave Exit (West)') # we connect up the mandatory associations we have found @@ -682,18 +682,18 @@ def link_entrances(world): mandatory = (mandatory,) for exit in mandatory: # point out somewhere - connect_exit(world, exit, lw_entrances.pop()) + connect_exit(world, exit, lw_entrances.pop(), player) # point in from somewhere - connect_entrance(world, lw_doors.pop(), exit) + connect_entrance(world, lw_doors.pop(), exit, player) for mandatory in mandatory_dark_world: if not isinstance(mandatory, tuple): mandatory = (mandatory,) for exit in mandatory: # point out somewhere - connect_exit(world, exit, dw_entrances.pop()) + connect_exit(world, exit, dw_entrances.pop(), player) # point in from somewhere - connect_entrance(world, dw_doors.pop(), exit) + connect_entrance(world, dw_doors.pop(), exit, player) # handle remaining caves while caves: @@ -727,8 +727,8 @@ def link_entrances(world): target_entrances = dw_entrances for exit in cave: - connect_exit(world, exit, target_entrances.pop()) - connect_entrance(world, target_doors.pop(), exit) + connect_exit(world, exit, target_entrances.pop(), player) + connect_entrance(world, target_doors.pop(), exit, player) # handle simple doors @@ -740,20 +740,20 @@ def link_entrances(world): # place blacksmith, has limited options random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place dam and pyramid fairy, have limited options random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) # place remaining doors - connect_doors(world, single_doors, door_targets) + connect_doors(world, single_doors, door_targets, player) elif world.shuffle == 'insanity': # beware ye who enter here @@ -789,13 +789,13 @@ def link_entrances(world): 'Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)'] # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) if world.mode == 'standard': # cannot move uncle cave - connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance') - connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs') - connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit') + connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) + connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) + connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit', player) else: hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_targets.append('Hyrule Castle Secret Entrance') @@ -803,9 +803,9 @@ def link_entrances(world): caves.append('Hyrule Castle Secret Entrance Exit') if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit') - connect_entrance(world, 'Pyramid Hole', 'Pyramid') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) else: entrances.append('Ganons Tower') caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) @@ -820,13 +820,13 @@ def link_entrances(world): # fill up holes for hole in hole_entrances: - connect_entrance(world, hole, hole_targets.pop()) + connect_entrance(world, hole, hole_targets.pop(), player) # hyrule castle handling if world.mode == 'standard': # must connect front of hyrule castle to do escape - connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') - connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop()) + connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop(), player) caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) else: doors.append('Hyrule Castle Entrance (South)') @@ -854,8 +854,8 @@ def link_entrances(world): exit = cave[-1] cave = cave[:-1] - connect_exit(world, exit, entrance) - connect_entrance(world, doors.pop(), exit) + connect_exit(world, exit, entrance, player) + connect_entrance(world, doors.pop(), exit, player) # rest of cave now is forced to be in this world caves.append(cave) @@ -870,22 +870,22 @@ def link_entrances(world): old_man_exit = old_man_entrances.pop() entrances.remove(old_man_exit) - connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit) - connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)') + connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player) + connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)', player) caves.append('Old Man Cave Exit (West)') # place blacksmith, has limited options blacksmith_doors = [door for door in blacksmith_doors if door in doors] random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) doors.remove(blacksmith_hut) # place dam and pyramid fairy, have limited options bomb_shop_doors = [door for door in bomb_shop_doors if door in doors] random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) doors.remove(bomb_shop) # handle remaining caves @@ -894,11 +894,11 @@ def link_entrances(world): cave = (cave,) for exit in cave: - connect_exit(world, exit, entrances.pop()) - connect_entrance(world, doors.pop(), exit) + connect_exit(world, exit, entrances.pop(), player) + connect_entrance(world, doors.pop(), exit, player) # place remaining doors - connect_doors(world, doors, door_targets) + connect_doors(world, doors, door_targets, player) elif world.shuffle == 'insanity_legacy': world.fix_fake_world = False # beware ye who enter here @@ -926,9 +926,9 @@ def link_entrances(world): if world.mode == 'standard': # cannot move uncle cave - connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance') - connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs') - connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit') + connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) + connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) + connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit', player) else: hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_targets.append('Hyrule Castle Secret Entrance') @@ -936,9 +936,9 @@ def link_entrances(world): caves.append('Hyrule Castle Secret Entrance Exit') if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit') - connect_entrance(world, 'Pyramid Hole', 'Pyramid') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) else: entrances.append('Ganons Tower') caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) @@ -953,13 +953,13 @@ def link_entrances(world): # fill up holes for hole in hole_entrances: - connect_entrance(world, hole, hole_targets.pop()) + connect_entrance(world, hole, hole_targets.pop(), player) # hyrule castle handling if world.mode == 'standard': # must connect front of hyrule castle to do escape - connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') - connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop()) + connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop(), player) caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) else: doors.append('Hyrule Castle Entrance (South)') @@ -987,8 +987,8 @@ def link_entrances(world): exit = cave[-1] cave = cave[:-1] - connect_exit(world, exit, entrance) - connect_entrance(world, doors.pop(), exit) + connect_exit(world, exit, entrance, player) + connect_entrance(world, doors.pop(), exit, player) # rest of cave now is forced to be in this world caves.append(cave) @@ -1003,8 +1003,8 @@ def link_entrances(world): old_man_exit = old_man_entrances.pop() entrances.remove(old_man_exit) - connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit) - connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)') + connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player) + connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)', player) caves.append('Old Man Cave Exit (West)') # handle remaining caves @@ -1013,8 +1013,8 @@ def link_entrances(world): cave = (cave,) for exit in cave: - connect_exit(world, exit, entrances.pop()) - connect_entrance(world, doors.pop(), exit) + connect_exit(world, exit, entrances.pop(), player) + connect_entrance(world, doors.pop(), exit, player) # handle simple doors @@ -1026,52 +1026,52 @@ def link_entrances(world): # place blacksmith, has limited options random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() - connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut') + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place dam and pyramid fairy, have limited options random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() - connect_entrance(world, bomb_shop, 'Big Bomb Shop') + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern']) + connect_doors(world, ['Tavern North'], ['Tavern'], player) # place remaining doors - connect_doors(world, single_doors, door_targets) + connect_doors(world, single_doors, door_targets, player) else: raise NotImplementedError('Shuffling not supported yet') # check for swamp palace fix - if world.get_entrance('Dam').connected_region.name != 'Dam' or world.get_entrance('Swamp Palace').connected_region.name != 'Swamp Palace (Entrance)': - world.swamp_patch_required = True + if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Palace (Entrance)': + world.swamp_patch_required[player] = True # check for potion shop location - if world.get_entrance('Potion Shop').connected_region.name != 'Potion Shop': - world.powder_patch_required = True + if world.get_entrance('Potion Shop', player).connected_region.name != 'Potion Shop': + world.powder_patch_required[player] = True # check for ganon location - if world.get_entrance('Pyramid Hole').connected_region.name != 'Pyramid': - world.ganon_at_pyramid = False + if world.get_entrance('Pyramid Hole', player).connected_region.name != 'Pyramid': + world.ganon_at_pyramid[player] = False # check for Ganon's Tower location - if world.get_entrance('Ganons Tower').connected_region.name != 'Ganons Tower (Entrance)': - world.ganonstower_vanilla = False + if world.get_entrance('Ganons Tower', player).connected_region.name != 'Ganons Tower (Entrance)': + world.ganonstower_vanilla[player] = False -def connect_simple(world, exitname, regionname): - world.get_entrance(exitname).connect(world.get_region(regionname)) +def connect_simple(world, exitname, regionname, player): + world.get_entrance(exitname, player).connect(world.get_region(regionname, player)) -def connect_entrance(world, entrancename, exitname): - entrance = world.get_entrance(entrancename) +def connect_entrance(world, entrancename, exitname, player): + entrance = world.get_entrance(entrancename, player) # check if we got an entrance or a region to connect to try: - region = world.get_region(exitname) + region = world.get_region(exitname, player) exit = None except RuntimeError: - exit = world.get_entrance(exitname) + exit = world.get_entrance(exitname, player) region = exit.parent_region # if this was already connected somewhere, remove the backreference @@ -1082,24 +1082,24 @@ def connect_entrance(world, entrancename, exitname): addresses = door_addresses[entrance.name][0] entrance.connect(region, addresses, target) - world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance') + world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance', player) -def connect_exit(world, exitname, entrancename): - entrance = world.get_entrance(entrancename) - exit = world.get_entrance(exitname) +def connect_exit(world, exitname, entrancename, player): + entrance = world.get_entrance(entrancename, player) + exit = world.get_entrance(exitname, player) # if this was already connected somewhere, remove the backreference if exit.connected_region is not None: exit.connected_region.entrances.remove(exit) exit.connect(entrance.parent_region, door_addresses[entrance.name][1], exit_ids[exit.name][1]) - world.spoiler.set_entrance(entrance.name, exit.name, 'exit') + world.spoiler.set_entrance(entrance.name, exit.name, 'exit', player) -def connect_two_way(world, entrancename, exitname): - entrance = world.get_entrance(entrancename) - exit = world.get_entrance(exitname) +def connect_two_way(world, entrancename, exitname, player): + entrance = world.get_entrance(entrancename, player) + exit = world.get_entrance(exitname, player) # if these were already connected somewhere, remove the backreference if entrance.connected_region is not None: @@ -1109,10 +1109,10 @@ def connect_two_way(world, entrancename, exitname): entrance.connect(exit.parent_region, door_addresses[entrance.name][0], exit_ids[exit.name][0]) exit.connect(entrance.parent_region, door_addresses[entrance.name][1], exit_ids[exit.name][1]) - world.spoiler.set_entrance(entrance.name, exit.name, 'both') + world.spoiler.set_entrance(entrance.name, exit.name, 'both', player) -def scramble_holes(world): +def scramble_holes(world, player): hole_entrances = [('Kakariko Well Cave', 'Kakariko Well Drop'), ('Bat Cave Cave', 'Bat Cave Drop'), ('North Fairy Cave', 'North Fairy Cave Drop'), @@ -1127,15 +1127,15 @@ def scramble_holes(world): ('Lumberjack Tree Exit', 'Lumberjack Tree (top)')] if not world.shuffle_ganon: - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit') - connect_entrance(world, 'Pyramid Hole', 'Pyramid') + connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) else: hole_targets.append(('Pyramid Exit', 'Pyramid')) if world.mode == 'standard': # cannot move uncle cave - connect_two_way(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit') - connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance') + connect_two_way(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player) + connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) else: hole_entrances.append(('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Drop')) hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) @@ -1146,30 +1146,30 @@ def scramble_holes(world): if world.shuffle_ganon: random.shuffle(hole_targets) exit, target = hole_targets.pop() - connect_two_way(world, 'Pyramid Entrance', exit) - connect_entrance(world, 'Pyramid Hole', target) + connect_two_way(world, 'Pyramid Entrance', exit, player) + connect_entrance(world, 'Pyramid Hole', target, player) if world.shuffle != 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) random.shuffle(hole_targets) for entrance, drop in hole_entrances: exit, target = hole_targets.pop() - connect_two_way(world, entrance, exit) - connect_entrance(world, drop, target) + connect_two_way(world, entrance, exit, player) + connect_entrance(world, drop, target, player) -def connect_random(world, exitlist, targetlist, two_way=False): +def connect_random(world, exitlist, targetlist, player, two_way=False): targetlist = list(targetlist) random.shuffle(targetlist) for exit, target in zip(exitlist, targetlist): if two_way: - connect_two_way(world, exit, target) + connect_two_way(world, exit, target, player) else: - connect_entrance(world, exit, target) + connect_entrance(world, exit, target, player) -def connect_mandatory_exits(world, entrances, caves, must_be_exits): +def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): """This works inplace""" random.shuffle(entrances) random.shuffle(caves) @@ -1187,7 +1187,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits): raise RuntimeError('No more caves left. Should not happen!') # all caves are sorted so that the last exit is always reachable - connect_two_way(world, exit, cave[-1]) + connect_two_way(world, exit, cave[-1], player) if len(cave) == 2: entrance = entrances.pop() # ToDo Better solution, this is a hot fix. Do not connect both sides of trock ledge only to each other @@ -1195,10 +1195,10 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits): new_entrance = entrances.pop() entrances.append(entrance) entrance = new_entrance - connect_two_way(world, entrance, cave[0]) + connect_two_way(world, entrance, cave[0], player) elif cave[-1] == 'Spectacle Rock Cave Exit': #Spectacle rock only has one exit for exit in cave[:-1]: - connect_two_way(world,entrances.pop(),exit) + connect_two_way(world,entrances.pop(),exit, player) else:#save for later so we can connect to multiple exits caves.append(cave[0:-1]) random.shuffle(caves) @@ -1207,11 +1207,11 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits): for cave in used_caves: if cave in caves: #check if we placed multiple entrances from this 3 or 4 exit for exit in cave: - connect_two_way(world, entrances.pop(), exit) + connect_two_way(world, entrances.pop(), exit, player) caves.remove(cave) -def connect_caves(world, lw_entrances, dw_entrances, caves): +def connect_caves(world, lw_entrances, dw_entrances, caves, player): """This works inplace""" random.shuffle(lw_entrances) random.shuffle(dw_entrances) @@ -1236,40 +1236,40 @@ def connect_caves(world, lw_entrances, dw_entrances, caves): target = lw_entrances if target is dw_entrances else dw_entrances for exit in cave: - connect_two_way(world, target.pop(), exit) + connect_two_way(world, target.pop(), exit, player) -def connect_doors(world, doors, targets): +def connect_doors(world, doors, targets, player): """This works inplace""" random.shuffle(doors) random.shuffle(targets) while doors: door = doors.pop() target = targets.pop() - connect_entrance(world, door, target) + connect_entrance(world, door, target, player) -def skull_woods_shuffle(world): +def skull_woods_shuffle(world, player): connect_random(world, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'], - ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)', 'Skull Woods Second Section (Drop)']) + ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)', 'Skull Woods Second Section (Drop)'], player) connect_random(world, ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'], - ['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'], True) + ['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'], player, True) -def simple_shuffle_dungeons(world): - skull_woods_shuffle(world) +def simple_shuffle_dungeons(world, player): + skull_woods_shuffle(world, player) dungeon_entrances = ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace'] dungeon_exits = ['Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit', 'Skull Woods Final Section Exit', 'Palace of Darkness Exit', 'Ice Palace Exit', 'Misery Mire Exit', 'Swamp Palace Exit'] if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit') + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) else: dungeon_entrances.append('Ganons Tower') dungeon_exits.append('Ganons Tower Exit') # shuffle up single entrance dungeons - connect_random(world, dungeon_entrances, dungeon_exits, True) + connect_random(world, dungeon_entrances, dungeon_exits, player, True) # mix up 4 door dungeons multi_dungeons = ['Desert', 'Turtle Rock'] @@ -1287,52 +1287,52 @@ def simple_shuffle_dungeons(world): # ToDo improve this? if hc_target == 'Hyrule Castle': - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)') - connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)') - connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)') - connect_two_way(world, 'Agahnims Tower', 'Agahnims Tower Exit') + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Agahnims Tower', 'Agahnims Tower Exit', player) elif hc_target == 'Desert': - connect_two_way(world, 'Desert Palace Entrance (South)', 'Hyrule Castle Exit (South)') - connect_two_way(world, 'Desert Palace Entrance (East)', 'Hyrule Castle Exit (East)') - connect_two_way(world, 'Desert Palace Entrance (West)', 'Hyrule Castle Exit (West)') - connect_two_way(world, 'Desert Palace Entrance (North)', 'Agahnims Tower Exit') + connect_two_way(world, 'Desert Palace Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Agahnims Tower Exit', player) elif hc_target == 'Turtle Rock': - connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)') - connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Hyrule Castle Exit (East)') - connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)') - connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Agahnims Tower Exit') + connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Agahnims Tower Exit', player) if dp_target == 'Hyrule Castle': - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Desert Palace Exit (South)') - connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Desert Palace Exit (East)') - connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Desert Palace Exit (West)') - connect_two_way(world, 'Agahnims Tower', 'Desert Palace Exit (North)') + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Agahnims Tower', 'Desert Palace Exit (North)', player) elif dp_target == 'Desert': - connect_two_way(world, 'Desert Palace Entrance (South)', 'Desert Palace Exit (South)') - connect_two_way(world, 'Desert Palace Entrance (East)', 'Desert Palace Exit (East)') - connect_two_way(world, 'Desert Palace Entrance (West)', 'Desert Palace Exit (West)') - connect_two_way(world, 'Desert Palace Entrance (North)', 'Desert Palace Exit (North)') + connect_two_way(world, 'Desert Palace Entrance (South)', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Desert Palace Exit (North)', player) elif dp_target == 'Turtle Rock': - connect_two_way(world, 'Turtle Rock', 'Desert Palace Exit (South)') - connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Desert Palace Exit (East)') - connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Desert Palace Exit (West)') - connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Desert Palace Exit (North)') + connect_two_way(world, 'Turtle Rock', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Desert Palace Exit (North)', player) if tr_target == 'Hyrule Castle': - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Turtle Rock Exit (Front)') - connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Turtle Rock Ledge Exit (East)') - connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Turtle Rock Ledge Exit (West)') - connect_two_way(world, 'Agahnims Tower', 'Turtle Rock Isolated Ledge Exit') + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Turtle Rock Ledge Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Agahnims Tower', 'Turtle Rock Isolated Ledge Exit', player) elif tr_target == 'Desert': - connect_two_way(world, 'Desert Palace Entrance (South)', 'Turtle Rock Exit (Front)') - connect_two_way(world, 'Desert Palace Entrance (North)', 'Turtle Rock Ledge Exit (East)') - connect_two_way(world, 'Desert Palace Entrance (West)', 'Turtle Rock Ledge Exit (West)') - connect_two_way(world, 'Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Exit') + connect_two_way(world, 'Desert Palace Entrance (South)', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Turtle Rock Ledge Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Exit', player) elif tr_target == 'Turtle Rock': - connect_two_way(world, 'Turtle Rock', 'Turtle Rock Exit (Front)') - connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Isolated Ledge Exit') - connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)') - connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)') + connect_two_way(world, 'Turtle Rock', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Isolated Ledge Exit', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)', player) def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits): def shuffle_lists_in_list(ls): diff --git a/Fill.py b/Fill.py index c1becd4480..86e0a5a7f0 100644 --- a/Fill.py +++ b/Fill.py @@ -1,6 +1,9 @@ import random import logging +from BaseClasses import CollectionState + + class FillError(RuntimeError): pass @@ -167,31 +170,41 @@ def fill_restrictive(world, base_state, locations, itempool): return new_state 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() perform_access_check = True if world.check_beatable_only: 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 - for location in locations: - if location.can_fill(maximum_exploration_state, item_to_place, perform_access_check): - spot_to_fill = location - break + if spot_to_fill is None: + # we filled all reachable spots. Maybe the game can be beaten anyway? + if world.can_beat_game(): + if not world.check_beatable_only: + 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: - # we filled all reachable spots. Maybe the game can be beaten anyway? - if world.can_beat_game(): - 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 + 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): @@ -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] # fill in gtower locations with trash first - if world.ganonstower_vanilla: - gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name] - random.shuffle(gtower_locations) - trashcnt = 0 - while gtower_locations and restitempool and trashcnt < gftower_trash_count: - spot_to_fill = gtower_locations.pop() - item_to_place = restitempool.pop() - world.push_item(spot_to_fill, item_to_place, False) - fill_locations.remove(spot_to_fill) - trashcnt += 1 + for player in range(1, world.players + 1): + if world.ganonstower_vanilla[player]: + gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player] + random.shuffle(gtower_locations) + trashcnt = 0 + while gtower_locations and restitempool and trashcnt < gftower_trash_count: + spot_to_fill = gtower_locations.pop() + item_to_place = restitempool.pop() + world.push_item(spot_to_fill, item_to_place, False) + fill_locations.remove(spot_to_fill) + trashcnt += 1 random.shuffle(fill_locations) 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) random.shuffle(fill_locations) @@ -297,3 +315,93 @@ def flood_items(world): world.push_item(location, item_to_place, True) itempool.remove(item_to_place) 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 diff --git a/Gui.py b/Gui.py index 33d83c7fee..50f6075647 100755 --- a/Gui.py +++ b/Gui.py @@ -244,15 +244,20 @@ def guiMain(args=None): 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 #') seedVar = StringVar() - seedEntry = Entry(bottomFrame, textvariable=seedVar) + seedEntry = Entry(bottomFrame, width=15, textvariable=seedVar) countLabel = Label(bottomFrame, text='Count') countVar = StringVar() - countSpinbox = Spinbox(bottomFrame, from_=1, to=100, textvariable=countVar) + countSpinbox = Spinbox(bottomFrame, from_=1, to=100, width=5, textvariable=countVar) def generateRom(): guiargs = Namespace + guiargs.multi = int(worldVar.get()) guiargs.seed = int(seedVar.get()) if seedVar.get() else None guiargs.count = int(countVar.get()) if countVar.get() != '1' else None guiargs.mode = modeVar.get() @@ -290,6 +295,8 @@ def guiMain(args=None): guiargs.rom = romVar.get() guiargs.jsonout = None guiargs.sprite = sprite + guiargs.skip_playthrough = False + guiargs.outputpath = None try: if guiargs.count is not None: seed = guiargs.seed @@ -305,7 +312,9 @@ def guiMain(args=None): 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) countLabel.pack(side=LEFT, padx=(5, 0)) countSpinbox.pack(side=LEFT) @@ -384,6 +393,7 @@ def guiMain(args=None): fastMenuLabel2 = Label(fastMenuFrame2, text='Menu speed') fastMenuLabel2.pack(side=LEFT) + heartbeepFrame2.pack(expand=True, anchor=E) heartcolorFrame2.pack(expand=True, anchor=E) fastMenuFrame2.pack(expand=True, anchor=E) diff --git a/ItemList.py b/ItemList.py index 8c7f3d7177..97ab419827 100644 --- a/ItemList.py +++ b/ItemList.py @@ -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'] 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') @@ -215,20 +215,20 @@ def generate_itempool(world): if world.timer in ['ohko', 'timed-ohko']: world.can_take_damage = False - world.push_item('Ganon', ItemFactory('Triforce'), False) - world.get_location('Ganon').event = True - world.push_item('Agahnim 1', ItemFactory('Beat Agahnim 1'), False) - world.get_location('Agahnim 1').event = True - world.push_item('Agahnim 2', ItemFactory('Beat Agahnim 2'), False) - world.get_location('Agahnim 2').event = True - world.push_item('Dark Blacksmith Ruins', ItemFactory('Pick Up Purple Chest'), False) - world.get_location('Dark Blacksmith Ruins').event = True - world.push_item('Frog', ItemFactory('Get Frog'), False) - world.get_location('Frog').event = True - world.push_item('Missing Smith', ItemFactory('Return Smith'), False) - world.get_location('Missing Smith').event = True - world.push_item('Floodgate', ItemFactory('Open Floodgate'), False) - world.get_location('Floodgate').event = True + world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) + world.get_location('Ganon', player).event = True + world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False) + world.get_location('Agahnim 1', player).event = True + world.push_item(world.get_location('Agahnim 2', player), ItemFactory('Beat Agahnim 2', player), False) + world.get_location('Agahnim 2', player).event = True + world.push_item(world.get_location('Dark Blacksmith Ruins', player), ItemFactory('Pick Up Purple Chest', player), False) + world.get_location('Dark Blacksmith Ruins', player).event = True + world.push_item(world.get_location('Frog', player), ItemFactory('Get Frog', player), False) + world.get_location('Frog', player).event = True + world.push_item(world.get_location('Missing Smith', player), ItemFactory('Return Smith', player), False) + world.get_location('Missing Smith', player).event = True + world.push_item(world.get_location('Floodgate', player), ItemFactory('Open Floodgate', player), False) + world.get_location('Floodgate', player).event = True # set up item pool if world.custom: @@ -236,10 +236,10 @@ def generate_itempool(world): world.rupoor_cost = min(world.customitemarray[67], 9999) 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) - world.itempool = ItemFactory(pool) + world.itempool += ItemFactory(pool, player) for (location, item) in placed_items: - world.push_item(location, ItemFactory(item), False) - world.get_location(location).event = True + world.push_item(world.get_location(location, player), ItemFactory(item, player), False) + world.get_location(location, player).event = True world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms if clock_mode is not None: world.clock_mode = clock_mode @@ -249,30 +249,30 @@ def generate_itempool(world): world.treasure_hunt_icon = treasure_hunt_icon 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) # 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) 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): - 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: hp.advancement = True # shuffle medallions mm_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) - set_up_shops(world) + place_bosses(world, player) + set_up_shops(world, player) 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 = [ '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', '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) - 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.dynamic_regions.append(old_man_take_any) reg = regions.pop() - entrance = world.get_region(reg).entrances[0] - connect_entrance(world, entrance, old_man_take_any) + entrance = world.get_region(reg, player).entrances[0] + connect_entrance(world, entrance, old_man_take_any, player) entrance.target = 0x58 old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True) world.shops.append(old_man_take_any.shop) 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: sword = random.choice(swords) 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) else: old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0) 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.dynamic_regions.append(take_any) target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) reg = regions.pop() - entrance = world.get_region(reg).entrances[0] - connect_entrance(world, entrance, take_any) + entrance = world.get_region(reg, player).entrances[0] + connect_entrance(world, entrance, take_any, player) entrance.target = target take_any.shop = Shop(take_any, room_id, ShopType.TakeAny, 0xE3, True) world.shops.append(take_any.shop) @@ -326,50 +326,52 @@ def set_up_take_anys(world): world.intialize_regions() -def create_dynamic_shop_locations(world): +def create_dynamic_shop_locations(world, player): for shop in world.shops: - for i, item in enumerate(shop.inventory): - if item is None: - continue - if item['create_location']: - loc = Location("{} Item {}".format(shop.region.name, i+1), parent=shop.region) - shop.region.locations.append(loc) - world.dynamic_locations.append(loc) + if shop.region.player == player: + for i, item in enumerate(shop.inventory): + if item is None: + continue + if item['create_location']: + loc = Location(player, "{} Item {}".format(shop.region.name, i+1), parent=shop.region) + 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) - loc.event = True + world.push_item(loc, ItemFactory(item['item'], player), False) + loc.event = True 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']) - 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'), - world.get_location('Thieves\' Town - Prize'), world.get_location('Skull Woods - Prize'), world.get_location('Swamp Palace - Prize'), world.get_location('Ice Palace - Prize'), - world.get_location('Misery Mire - Prize')] - 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] + all_state = world.get_all_state(keys=True) + for player in range(1, world.players + 1): + crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'], player) + 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), + 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), + world.get_location('Misery Mire - Prize', player)] + 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: - attempts -= 1 - try: - prizepool = list(unplaced_prizes) - prize_locs = list(empty_crystal_locations) - random.shuffle(prizepool) - random.shuffle(prize_locs) - fill_restrictive(world, world.get_all_state(keys=True), prize_locs, prizepool) - except FillError: - logging.getLogger('').info("Failed to place dungeon prizes. Will retry %s more times", attempts) - for location in empty_crystal_locations: - location.item = None - continue - break - else: - raise FillError('Unable to place dungeon prizes') + for attempt in range(attempts): + try: + prizepool = list(unplaced_prizes) + prize_locs = list(empty_crystal_locations) + random.shuffle(prizepool) + random.shuffle(prize_locs) + fill_restrictive(world, all_state, prize_locs, prizepool) + except FillError as e: + logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times" % (e, attempts)) + for location in empty_crystal_locations: + location.item = None + continue + break + else: + raise FillError('Unable to place dungeon prizes') -def set_up_shops(world): +def set_up_shops(world, player): # Changes to basic 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 if world.retro: - rss = world.get_region('Red Shield Shop').shop + rss = world.get_region('Red Shield Shop', player).shop rss.active = True rss.add_inventory(2, 'Single Arrow', 80) # Randomized changes to Shops 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.add_inventory(0, 'Single Arrow', 80) shop.add_inventory(1, 'Small Key (Universal)', 100) diff --git a/Items.py b/Items.py index 2e0d85061b..1bc94cc9dc 100644 --- a/Items.py +++ b/Items.py @@ -3,7 +3,7 @@ import logging from BaseClasses import Item -def ItemFactory(items): +def ItemFactory(items, player): ret = [] singleton = False if isinstance(items, str): @@ -12,7 +12,7 @@ def ItemFactory(items): for item in items: 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] - 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: logging.getLogger('').warning('Unknown Item: %s', item) return None diff --git a/Main.py b/Main.py index d8de72ba02..413c1a0e45 100644 --- a/Main.py +++ b/Main.py @@ -12,7 +12,7 @@ from EntranceShuffle import link_entrances from Rom import patch_rom, Sprite, LocalRom, JsonRom from Rules import set_rules 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 Utils import output_path @@ -40,7 +40,7 @@ def main(args, seed=None): start = time.clock() # 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('') if seed is None: random.seed(None) @@ -49,26 +49,32 @@ def main(args, seed=None): world.seed = int(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) world.difficulty_requirements = difficulties[world.difficulty] - create_regions(world) - - create_dungeons(world) + for player in range(1, world.players + 1): + create_regions(world, player) + create_dungeons(world, player) 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) 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.') - set_rules(world) + for player in range(1, world.players + 1): + set_rules(world, player) logger.info('Placing Dungeon Prizes.') @@ -102,9 +108,9 @@ def main(args, seed=None): elif args.algorithm == 'balanced': distribute_items_restrictive(world, gt_filler(world)) - logger.info('Calculating playthrough.') - - create_playthrough(world) + if world.players > 1: + logger.info('Balancing multiworld progression.') + balance_multiworld_progression(world) 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) + jsonout = {} if not args.suppress_rom: - if args.jsonout: - rom = JsonRom() + if world.players > 1: + raise NotImplementedError("Multiworld rom writes have not been implemented") else: - rom = LocalRom(args.rom) - patch_rom(world, rom, bytearray(logic_hash), args.heartbeep, args.heartcolor, sprite) - if args.jsonout: - print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()})) - else: - rom.write_to_file(args.jsonout or output_path('%s.sfc' % outfilebase)) + player = 1 + + if args.jsonout: + rom = JsonRom() + else: + 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: 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.debug('Total Time: %s', time.clock() - start) @@ -144,10 +168,12 @@ def gt_filler(world): def copy_world(world): # 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.required_medallions = list(world.required_medallions) - ret.swamp_patch_required = world.swamp_patch_required - ret.ganon_at_pyramid = world.ganon_at_pyramid + 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 = world.required_medallions.copy() + ret.swamp_patch_required = world.swamp_patch_required.copy() + 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_icon = world.treasure_hunt_icon ret.sewer_light_cone = world.sewer_light_cone @@ -162,53 +188,56 @@ def copy_world(world): ret.difficulty_requirements = world.difficulty_requirements ret.fix_fake_world = world.fix_fake_world 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 bosses for dungeon in world.dungeons: 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: - 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.inventory = copy.copy(shop.inventory) # connect copied world 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_dark_world = region.is_dark_world 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 for location in world.get_locations(): if location.item is not None: - item = Item(location.item.name, location.item.advancement, location.item.priority, location.item.type) - ret.get_location(location.name).item = item - item.location = ret.get_location(location.name) + item = Item(location.item.name, location.item.advancement, location.item.priority, location.item.type, player = location.item.player) + ret.get_location(location.name, location.player).item = item + item.location = ret.get_location(location.name, location.player) 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 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 ret.state.prog_items = list(world.state.prog_items) ret.state.stale = True - set_rules(ret) + for player in range(1, world.players + 1): + set_rules(ret, player) return ret def copy_dynamic_regions_and_locations(world, ret): 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.dynamic_regions.append(new_reg) @@ -219,8 +248,8 @@ def copy_dynamic_regions_and_locations(world, ret): ret.shops.append(new_reg.shop) for location in world.dynamic_locations: - new_loc = Location(location.name, location.address, location.crystal, location.hint_text, location.parent_region) - new_reg = ret.get_region(location.parent_region.name) + 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, location.parent_region.player) new_reg.locations.append(new_loc) @@ -231,7 +260,8 @@ def create_playthrough(world): # in treasure hunt and pedestal goals, ganon is invincible 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 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)) 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: raise RuntimeError('Not all progression items reachable. Something went terribly wrong here.') else: @@ -275,11 +305,11 @@ def create_playthrough(world): to_delete = [] for location in sphere: # 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 location.item = None 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(): to_delete.append(location) else: @@ -316,7 +346,7 @@ def create_playthrough(world): raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') # 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): while node: @@ -331,9 +361,12 @@ def create_playthrough(world): pathpairs = zip_longest(pathsiter, pathsiter) return list(pathpairs) - old_world.spoiler.paths = {location.name : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere} - if any(exit == 'Pyramid Fairy' for path in old_world.spoiler.paths.values() for (_, exit) in path): - old_world.spoiler.paths['Big Bomb Shop'] = get_path(state, world.get_region('Big Bomb Shop')) + old_world.spoiler.paths = dict() + for player in range(1, world.players + 1): + 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 old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)]) diff --git a/Plando.py b/Plando.py index f2f6887366..0bb3dd1ad7 100755 --- a/Plando.py +++ b/Plando.py @@ -33,7 +33,7 @@ def main(args): start_time = time.clock() # 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('') hasher = hashlib.md5() @@ -48,14 +48,14 @@ def main(args): world.difficulty_requirements = difficulties[world.difficulty] - create_regions(world) - create_dungeons(world) + create_regions(world, 1) + create_dungeons(world, 1) - link_entrances(world) + link_entrances(world, 1) logger.info('Calculating Access Rules.') - set_rules(world) + set_rules(world, 1) logger.info('Fill the world.') @@ -63,8 +63,8 @@ def main(args): 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)': - world.swamp_patch_required = True + 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[1] = True logger.info('Calculating playthrough.') @@ -84,7 +84,7 @@ def main(args): sprite = None 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: if texttype == 'text': @@ -174,33 +174,33 @@ def fill_world(world, plando, text_patches): continue locationstr, itemstr = line.split(':', 1) - location = world.get_location(locationstr.strip()) + location = world.get_location(locationstr.strip(), 1) if location is None: logger.warning('Unknown location: %s', locationstr) continue else: - item = ItemFactory(itemstr.strip()) + item = ItemFactory(itemstr.strip(), 1) if item is not None: world.push_item(location, item) if item.key: location.event = True elif '<=>' in line: 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: entrance, exit = line.split('=>', 1) - connect_entrance(world, entrance.strip(), exit.strip()) + connect_entrance(world, entrance.strip(), exit.strip(), 1) elif '<=' in line: 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 - world.get_location('Agahnim 1').event = True - world.get_location('Agahnim 1').item = ItemFactory('Beat Agahnim 1') - world.get_location('Agahnim 2').event = True - world.get_location('Agahnim 2').item = ItemFactory('Beat Agahnim 2') + world.get_location('Agahnim 1', 1).event = True + world.get_location('Agahnim 1', 1).item = ItemFactory('Beat Agahnim 1', 1) + world.get_location('Agahnim 2', 1).event = True + world.get_location('Agahnim 2', 1).item = ItemFactory('Beat Agahnim 2', 1) def start(): diff --git a/Regions.py b/Regions.py index f95216a3d3..70ae3b0b89 100644 --- a/Regions.py +++ b/Regions.py @@ -2,10 +2,10 @@ import collections from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType -def create_regions(world): +def create_regions(world, player): - world.regions = [ - create_lw_region('Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'], + world.regions += [ + 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', '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', @@ -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)', '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']), - create_lw_region('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_cave_region('Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top", + create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']), + create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']), + create_cave_region(player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top", "Blind\'s Hideout - Left", "Blind\'s Hideout - Right", "Blind\'s Hideout - Far Left", "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_lw_region('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_lw_region('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('North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']), - create_cave_region('Dam', 'the dam', ['Floodgate', 'Floodgate Chest']), - create_cave_region('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('Tavern', 'the tavern', ['Kakariko Tavern']), - create_cave_region('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('Snitch Lady (West)', 'a boring house'), - create_cave_region('Bush Covered House', 'the grass man'), - create_cave_region('Tavern (Front)', 'the tavern'), - create_cave_region('Light World Bomb Hut', 'a restock room'), - create_cave_region('Kakariko Shop', 'a common shop'), - create_cave_region('Fortune Teller (Light)', 'a fortune teller'), - create_cave_region('Lake Hylia Fortune Teller', 'a fortune teller'), - create_cave_region('Lumberjack House', 'a boring house'), - create_cave_region('Bonk Fairy (Light)', 'a fairy fountain'), - create_cave_region('Bonk Fairy (Dark)', 'a fairy fountain'), - create_cave_region('Lake Hylia Healer Fairy', 'a fairy fountain'), - create_cave_region('Swamp Healer Fairy', 'a fairy fountain'), - create_cave_region('Desert Healer Fairy', 'a fairy fountain'), - create_cave_region('Dark Lake Hylia Healer Fairy', 'a fairy fountain'), - create_cave_region('Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'), - create_cave_region('Dark Desert Healer Fairy', 'a fairy fountain'), - create_cave_region('Dark Death Mountain Healer Fairy', 'a fairy fountain'), - create_cave_region('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('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, 'Hyrule Castle Secret Entrance', 'a drop\'s exit', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']), + create_lw_region(player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']), + create_cave_region(player, 'Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']), + create_lw_region(player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']), + create_cave_region(player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']), + create_cave_region(player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']), + create_cave_region(player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']), + create_cave_region(player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']), + create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']), + create_cave_region(player, 'Tavern', 'the tavern', ['Kakariko Tavern']), + create_cave_region(player, 'Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']), + create_cave_region(player, 'Snitch Lady (East)', 'a boring house'), + create_cave_region(player, 'Snitch Lady (West)', 'a boring house'), + create_cave_region(player, 'Bush Covered House', 'the grass man'), + create_cave_region(player, 'Tavern (Front)', 'the tavern'), + create_cave_region(player, 'Light World Bomb Hut', 'a restock room'), + create_cave_region(player, 'Kakariko Shop', 'a common shop'), + create_cave_region(player, 'Fortune Teller (Light)', 'a fortune teller'), + create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'), + create_cave_region(player, 'Lumberjack House', 'a boring house'), + create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'), + create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'), + create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Swamp Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Desert Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Chicken House', 'a house with a chest', ['Chicken House']), + create_cave_region(player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']), + create_cave_region(player, 'Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']), + 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)']), - create_cave_region('Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']), - create_cave_region('Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']), - create_lw_region('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('Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']), - create_cave_region('Sick Kids House', 'the sick kid', ['Sick Kid']), - create_lw_region('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('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('Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']), - create_lw_region('Cave 45 Ledge', None, ['Cave 45']), - create_cave_region('Cave 45', 'a cave with an item', ['Cave 45']), - create_lw_region('Graveyard Ledge', None, ['Graveyard Cave']), - create_cave_region('Graveyard Cave', 'a cave with an item', ['Graveyard Cave']), - create_cave_region('Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']), - create_cave_region('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, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']), + create_cave_region(player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']), + create_lw_region(player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']), + create_cave_region(player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']), + create_cave_region(player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']), + create_cave_region(player, 'Sick Kids House', 'the sick kid', ['Sick Kid']), + create_lw_region(player, 'Hobo Bridge', ['Hobo']), + create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']), + create_cave_region(player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']), + create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']), + create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']), + create_lw_region(player, 'Cave 45 Ledge', None, ['Cave 45']), + create_cave_region(player, 'Cave 45', 'a cave with an item', ['Cave 45']), + create_lw_region(player, 'Graveyard Ledge', None, ['Graveyard Cave']), + create_cave_region(player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']), + create_cave_region(player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']), + create_cave_region(player, 'Long Fairy Cave', 'a fairy fountain'), + 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']), - create_cave_region('Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']), - create_cave_region('Good Bee Cave', 'a cold bee'), - create_cave_region('20 Rupee Cave', 'a cave with some cash'), - create_cave_region('Cave Shop (Lake Hylia)', 'a common shop'), - create_cave_region('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('Library', 'the library', ['Library']), - create_cave_region('Kakariko Gamble Game', 'a game of chance'), - create_cave_region('Potion Shop', 'the potion shop', ['Potion Shop']), - create_lw_region('Lake Hylia Island', ['Lake Hylia Island']), - create_cave_region('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_lw_region('Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']), - create_cave_region('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('Desert Ledge (Northeast)', None, ['Checkerboard Cave']), - create_lw_region('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('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_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']), + create_cave_region(player, 'Good Bee Cave', 'a cold bee'), + create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'), + create_cave_region(player, 'Cave Shop (Lake Hylia)', 'a common shop'), + create_cave_region(player, 'Cave Shop (Dark Death Mountain)', 'a common shop'), + create_cave_region(player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']), + create_cave_region(player, 'Library', 'the library', ['Library']), + create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'), + create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop']), + create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), + create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies'), + create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), + create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']), + create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'), + create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']), + create_lw_region(player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']), + create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']), + create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']), + create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']), + 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']), - create_dungeon_region('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('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, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']), + create_dungeon_region(player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']), + create_dungeon_region(player, 'Desert Palace North', 'Desert Palace', ['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']), + 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']), - create_lw_region('Master Sword Meadow', ['Master Sword Pedestal']), - create_cave_region('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('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_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']), + create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'), + create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']), + create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']), + 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']), - create_dungeon_region('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('Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', + create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks + create_dungeon_region(player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'], ['Sewers Door']), + 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']), - create_dungeon_region('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('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('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_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_cave_region('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_cave_region('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('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_cave_region('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('Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left', + create_dungeon_region(player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary 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(player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None), + create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']), + create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']), + create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']), + create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']), + create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']), + create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']), + create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), + create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), + create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), + 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(player, 'Hookshot Fairy', 'fairies deep in a cave'), + 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(player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Right', 'Paradox Cave Lower - Far Right', @@ -134,174 +134,174 @@ def create_regions(world): 'Paradox Cave Upper - Left', 'Paradox Cave Upper - Right'], ['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('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('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('Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']), - create_lw_region('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('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_lw_region('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('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('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_cave_region(player, 'Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']), + create_cave_region(player, 'Light World Death Mountain Shop', 'a common shop'), + 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(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']), + create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']), + create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']), + create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']), + create_cave_region(player, 'Fairy Ascension Cave (Bottom)', 'a connector', None, ['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']), + create_cave_region(player, 'Fairy Ascension Cave (Drop)', 'a connector', None, ['Fairy Ascension Cave Pots']), + create_cave_region(player, 'Fairy Ascension Cave (Top)', 'a connector', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']), + create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']), + create_lw_region(player, 'Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']), + create_lw_region(player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']), + 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(player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']), + 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']), - create_dw_region('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('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, 'Northeast Dark World', ['Catfish'], ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass']), + create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'), + create_cave_region(player, 'East Dark World Hint', 'a storyteller'), + 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']), - create_cave_region('Big Bomb Shop', 'the bomb shop'), - create_cave_region('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('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_cave_region('Dark Lake Hylia Ledge Hint', 'a storyteller'), - create_cave_region('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, 'Big Bomb Shop', 'the bomb shop'), + create_cave_region(player, 'Archery Game', 'a game of skill'), + create_dw_region(player, 'Dark Lake Hylia', None, ['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']), + create_dw_region(player, 'Dark Lake Hylia Central Island', None, ['Ice Palace', 'Lake Hylia Central Island Mirror Spot']), + 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(player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'), + create_cave_region(player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'), + 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']), - 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']), - create_dw_region('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('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('Village of Outcasts Shop', 'a common shop'), - create_cave_region('Dark Lake Hylia Shop', 'a common shop'), - create_cave_region('Dark World Lumberjack Shop', 'a common shop'), - create_cave_region('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('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('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('Red Shield Shop', 'the rare shop'), - create_cave_region('Dark Sanctuary Hint', 'a storyteller'), - create_cave_region('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('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, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Dark World Shop']), + 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(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']), + create_cave_region(player, 'Fortune Teller (Dark)', 'a fortune teller'), + create_cave_region(player, 'Village of Outcasts Shop', 'a common shop'), + create_cave_region(player, 'Dark Lake Hylia Shop', 'a common shop'), + create_cave_region(player, 'Dark World Lumberjack Shop', 'a common shop'), + create_cave_region(player, 'Dark World Potion Shop', 'a common shop'), + create_cave_region(player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']), + create_cave_region(player, 'Pyramid Fairy', 'a cave with two chests', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']), + create_cave_region(player, 'Brewery', 'a house with a chest', ['Brewery']), + create_cave_region(player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']), + create_cave_region(player, 'Chest Game', 'a game of 16 chests', ['Chest Game']), + create_cave_region(player, 'Red Shield Shop', 'the rare shop'), + create_cave_region(player, 'Dark Sanctuary Hint', 'a storyteller'), + create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']), + create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']), + create_dw_region(player, '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)']), - 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('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, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']), + 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']), - create_cave_region('Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']), - create_cave_region('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('Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)', + create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']), + create_cave_region(player, 'Dark Desert Hint', 'a storyteller'), + create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']), + create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)', '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('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_cave_region('Superbunny Cave', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], + create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']), + create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']), + create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']), + create_cave_region(player, 'Superbunny Cave', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)']), - create_cave_region('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, 'Spike Cave', 'Spike Cave', ['Spike Cave']), + 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)']), - create_dw_region('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_dw_region('Turtle Rock (Top)', None, ['Turtle Rock Drop']), - create_lw_region('Mimic Cave Ledge', None, ['Mimic Cave']), - create_cave_region('Mimic Cave', 'Mimic Cave', ['Mimic Cave']), + 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(player, 'Death Mountain Floating Island (Light World)', ['Floating Island']), + create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']), + create_lw_region(player, 'Mimic Cave Ledge', None, ['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('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('Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest', + create_dungeon_region(player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']), + create_dungeon_region(player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']), + create_dungeon_region(player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']), + 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)']), - 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']), - 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 - Compass Chest', '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 - Blind\'s Cell'], ['Blind Fight']), - create_dungeon_region('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('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('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('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('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('Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest', + create_dungeon_region(player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']), + 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(player, '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 (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 (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']), + create_dungeon_region(player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']), + 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(player, '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 (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']), + create_dungeon_region(player, 'Ice Palace (Entrance)', 'Ice Palace', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']), + 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)']), - create_dungeon_region('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('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('Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby', + create_dungeon_region(player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']), + create_dungeon_region(player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']), + create_dungeon_region(player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']), + create_dungeon_region(player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']), + 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']), - create_dungeon_region('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('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('Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left', + create_dungeon_region(player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']), + create_dungeon_region(player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']), + create_dungeon_region(player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']), + create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']), + 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']), - 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('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('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('Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', + 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(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(player, '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 (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 (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']), + 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 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('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, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']), + 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(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']), - create_dungeon_region('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('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 (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']), + 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(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']), - 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('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('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, '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 (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']), + create_dungeon_region(player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']), + 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']), - create_dungeon_region('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 (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']), + 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 (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 (Map Room)', 'Ganons Tower (Double Switch Room)']), - create_dungeon_region('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('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 (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']), + create_dungeon_region(player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']), + 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 (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']), - create_dungeon_region('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 (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']), + 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']), - create_dungeon_region('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_cave_region('Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']), - create_cave_region('Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']), - create_dw_region('Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']) + create_dungeon_region(player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']), + create_dungeon_region(player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None), + create_cave_region(player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']), + create_cave_region(player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']), + create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']) ] 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) region.shop = shop world.shops.append(shop) for index, (item, price) in enumerate(default_shop_contents[region_name]): 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) region.shop = shop world.shops.append(shop) @@ -309,30 +309,30 @@ def create_regions(world): shop.add_inventory(1, 'Arrow Upgrade (+5)', 100, 7) world.intialize_regions() -def create_lw_region(name, locations=None, exits=None): - return _create_region(name, RegionType.LightWorld, 'Light World', locations, exits) +def create_lw_region(player, name, locations=None, exits=None): + return _create_region(player, name, RegionType.LightWorld, 'Light World', locations, exits) -def create_dw_region(name, locations=None, exits=None): - return _create_region(name, RegionType.DarkWorld, 'Dark World', locations, exits) +def create_dw_region(player, name, locations=None, exits=None): + return _create_region(player, name, RegionType.DarkWorld, 'Dark World', locations, exits) -def create_cave_region(name, hint='Hyrule', locations=None, exits=None): - return _create_region(name, RegionType.Cave, hint, locations, exits) +def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None): + return _create_region(player, name, RegionType.Cave, hint, locations, exits) -def create_dungeon_region(name, hint='Hyrule', locations=None, exits=None): - return _create_region(name, RegionType.Dungeon, hint, locations, exits) +def create_dungeon_region(player, name, hint='Hyrule', locations=None, exits=None): + return _create_region(player, name, RegionType.Dungeon, hint, locations, exits) -def _create_region(name, type, hint='Hyrule', locations=None, exits=None): - ret = Region(name, type, hint) +def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None): + ret = Region(name, type, hint, player) if locations is None: locations = [] if exits is None: exits = [] for exit in exits: - ret.exits.append(Entrance(exit, ret)) + ret.exits.append(Entrance(player, exit, ret)) for location in locations: 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 def mark_light_world_regions(world): @@ -398,221 +398,221 @@ default_shop_contents = { } location_table = {'Mushroom': (0x180013, False, 'in the woods'), - 'Bottle Merchant': (0x2EB18, False, 'with a merchant'), - 'Flute Spot': (0x18014A, False, 'underground'), + 'Bottle Merchant': (0x2eb18, False, 'with a merchant'), + 'Flute Spot': (0x18014a, False, 'underground'), 'Sunken Treasure': (0x180145, False, 'underwater'), - 'Purple Chest': (0x33D68, False, 'from a box'), - 'Blind\'s Hideout - Top': (0xEB0F, 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 - Far Left': (0xEB18, False, 'in a basement'), - 'Blind\'s Hideout - Far Right': (0xEB1B, False, 'in a basement'), - 'Link\'s Uncle': (0x2DF45, False, 'with your uncle'), - 'Secret Passage': (0xE971, False, 'near your uncle'), - 'King Zora': (0xEE1C3, False, 'at a high price'), - 'Zora\'s Ledge': (0x180149, False, 'near Zora'), - 'Waterfall Fairy - Left': (0xE9B0, False, 'near a fairy'), - 'Waterfall Fairy - Right': (0xE9D1, False, 'near a fairy'), - 'King\'s Tomb': (0xE97A, False, 'alone in a cave'), - 'Floodgate Chest': (0xE98C, False, 'in the dam'), - 'Link\'s House': (0xE9BC, False, 'in your home'), - 'Kakariko Tavern': (0xE9CE, False, 'in the bar'), - 'Chicken House': (0xE9E9, False, 'near poultry'), - 'Aginah\'s Cave': (0xE9F2, False, 'with Aginah'), - 'Sahasrahla\'s Hut - Left': (0xEA82, False, 'near the elder'), - 'Sahasrahla\'s Hut - Middle': (0xEA85, False, 'near the elder'), - 'Sahasrahla\'s Hut - Right': (0xEA88, False, 'near the elder'), - 'Sahasrahla': (0x2F1FC, False, 'with the elder'), - 'Kakariko Well - Top': (0xEA8E, False, 'in a well'), - 'Kakariko Well - Left': (0xEA91, False, 'in a well'), - 'Kakariko Well - Middle': (0xEA94, False, 'in a well'), - 'Kakariko Well - Right': (0xEA97, False, 'in a well'), - 'Kakariko Well - Bottom': (0xEA9A, False, 'in a well'), - 'Blacksmith': (0x18002A, False, 'with the smith'), + 'Purple Chest': (0x33d68, False, 'from a box'), + "Blind's Hideout - Top": (0xeb0f, 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 - Far Left": (0xeb18, False, 'in a basement'), + "Blind's Hideout - Far Right": (0xeb1b, False, 'in a basement'), + "Link's Uncle": (0x2df45, False, 'with your uncle'), + 'Secret Passage': (0xe971, False, 'near your uncle'), + 'King Zora': (0xee1c3, False, 'at a high price'), + "Zora's Ledge": (0x180149, False, 'near Zora'), + 'Waterfall Fairy - Left': (0xe9b0, False, 'near a fairy'), + 'Waterfall Fairy - Right': (0xe9d1, False, 'near a fairy'), + "King's Tomb": (0xe97a, False, 'alone in a cave'), + 'Floodgate Chest': (0xe98c, False, 'in the dam'), + "Link's House": (0xe9bc, False, 'in your home'), + 'Kakariko Tavern': (0xe9ce, False, 'in the bar'), + 'Chicken House': (0xe9e9, False, 'near poultry'), + "Aginah's Cave": (0xe9f2, False, 'with Aginah'), + "Sahasrahla's Hut - Left": (0xea82, False, 'near the elder'), + "Sahasrahla's Hut - Middle": (0xea85, False, 'near the elder'), + "Sahasrahla's Hut - Right": (0xea88, False, 'near the elder'), + 'Sahasrahla': (0x2f1fc, False, 'with the elder'), + 'Kakariko Well - Top': (0xea8e, False, 'in a well'), + 'Kakariko Well - Left': (0xea91, False, 'in a well'), + 'Kakariko Well - Middle': (0xea94, False, 'in a well'), + 'Kakariko Well - Right': (0xea97, False, 'in a well'), + 'Kakariko Well - Bottom': (0xea9a, False, 'in a well'), + 'Blacksmith': (0x18002a, False, 'with the smith'), 'Magic Bat': (0x180015, False, 'with the bat'), - 'Sick Kid': (0x339CF, False, 'with the sick'), - 'Hobo': (0x33E7D, False, 'with the hobo'), + 'Sick Kid': (0x339cf, False, 'with the sick'), + 'Hobo': (0x33e7d, False, 'with the hobo'), 'Lost Woods Hideout': (0x180000, False, 'near a thief'), 'Lumberjack Tree': (0x180001, False, 'in a hole'), 'Cave 45': (0x180003, False, 'alone in a cave'), 'Graveyard Cave': (0x180004, 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 - Left': (0xEB45, False, 'near Moldorms'), - 'Mini Moldorm Cave - Right': (0xEB48, False, 'near Moldorms'), - 'Mini Moldorm Cave - Far Right': (0xEB4B, False, 'near Moldorms'), + 'Mini Moldorm Cave - Far Left': (0xeb42, False, 'near Moldorms'), + 'Mini Moldorm Cave - Left': (0xeb45, False, 'near Moldorms'), + 'Mini Moldorm Cave - Right': (0xeb48, False, 'near Moldorms'), + 'Mini Moldorm Cave - Far Right': (0xeb4b, False, 'near Moldorms'), 'Mini Moldorm Cave - Generous Guy': (0x180010, False, 'near Moldorms'), - 'Ice Rod Cave': (0xEB4E, False, 'in a frozen cave'), - 'Bonk Rock Cave': (0xEB3F, False, 'alone in a cave'), + 'Ice Rod Cave': (0xeb4e, False, 'in a frozen cave'), + 'Bonk Rock Cave': (0xeb3f, False, 'alone in a cave'), 'Library': (0x180012, False, 'near books'), 'Potion Shop': (0x180014, False, 'near potions'), 'Lake Hylia Island': (0x180144, False, 'on an island'), 'Maze Race': (0x180142, False, 'at the race'), '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 - Map Chest': (0xE9B6, 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 - Map Chest': (0xe9b6, 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 - Boss': (0x180151, False, 'with Lanmolas'), - 'Eastern Palace - Compass Chest': (0xE977, False, 'in Eastern Palace'), - 'Eastern Palace - Big Chest': (0xE97D, 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 - Map Chest': (0xE9F5, False, 'in Eastern Palace'), + 'Eastern Palace - Compass Chest': (0xe977, False, 'in Eastern Palace'), + 'Eastern Palace - Big Chest': (0xe97d, 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 - Map Chest': (0xe9f5, False, 'in Eastern Palace'), 'Eastern Palace - Boss': (0x180150, False, 'with the Armos'), - 'Master Sword Pedestal': (0x289B0, False, 'at the pedestal'), - 'Hyrule Castle - Boomerang Chest': (0xE974, False, 'in Hyrule Castle'), - 'Hyrule Castle - Map Chest': (0xEB0C, False, 'in Hyrule Castle'), - 'Hyrule Castle - Zelda\'s Chest': (0xEB09, False, 'in Hyrule Castle'), - 'Sewers - Dark Cross': (0xE96E, 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 - Right': (0xEB63, False, 'in the sewers'), - 'Sanctuary': (0xEA79, False, 'in Sanctuary'), - 'Castle Tower - Room 03': (0xEAB5, False, 'in Castle Tower'), - 'Castle Tower - Dark Maze': (0xEAB2, False, 'in Castle Tower'), - 'Old Man': (0xF69FA, False, 'with the old man'), + 'Master Sword Pedestal': (0x289b0, False, 'at the pedestal'), + 'Hyrule Castle - Boomerang Chest': (0xe974, False, 'in Hyrule Castle'), + 'Hyrule Castle - Map Chest': (0xeb0c, False, 'in Hyrule Castle'), + "Hyrule Castle - Zelda's Chest": (0xeb09, False, 'in Hyrule Castle'), + 'Sewers - Dark Cross': (0xe96e, 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 - Right': (0xeb63, False, 'in the sewers'), + 'Sanctuary': (0xea79, False, 'in Sanctuary'), + 'Castle Tower - Room 03': (0xeab5, False, 'in Castle Tower'), + 'Castle Tower - Dark Maze': (0xeab2, False, 'in Castle Tower'), + 'Old Man': (0xf69fa, False, 'with the old man'), '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 - Left': (0xEB2D, 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 - Middle': (0xEB36, 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'), - 'Spiral Cave': (0xE9BF, False, 'in spiral cave'), + '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 - 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 - Middle': (0xeb36, 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'), + 'Spiral Cave': (0xe9bf, False, 'in spiral cave'), 'Ether Tablet': (0x180016, False, 'at a monolith'), 'Spectacle Rock': (0x180140, False, 'atop a rock'), '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 - Big Key Chest': (0xE9E6, 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 - Map Chest': (0xe9ad, 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 - Big Chest': (0xe9f8, False, 'in Tower of Hera'), 'Tower of Hera - Boss': (0x180152, False, 'with Moldorm'), 'Pyramid': (0x180147, False, 'on the pyramid'), - 'Catfish': (0xEE185, False, 'with a catfish'), - 'Stumpy': (0x330C7, False, 'with tree boy'), + 'Catfish': (0xee185, False, 'with a catfish'), + 'Stumpy': (0x330c7, False, 'with tree boy'), 'Digging Game': (0x180148, False, 'underground'), 'Bombos Tablet': (0x180017, False, 'at a monolith'), - 'Hype Cave - Top': (0xEB1E, 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 - Bottom': (0xEB27, 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 Left': (0xeb24, 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'), 'Peg Cave': (0x180006, False, 'alone in a cave'), - 'Pyramid Fairy - Left': (0xE980, False, 'near a fairy'), - 'Pyramid Fairy - Right': (0xE983, False, 'near a fairy'), - 'Brewery': (0xE9EC, False, 'alone in a home'), - 'C-Shaped House': (0xE9EF, False, 'alone in a home'), - 'Chest Game': (0xEDA8, False, 'as a prize'), + 'Pyramid Fairy - Left': (0xe980, False, 'near a fairy'), + 'Pyramid Fairy - Right': (0xe983, False, 'near a fairy'), + 'Brewery': (0xe9ec, False, 'alone in a home'), + 'C-Shaped House': (0xe9ef, False, 'alone in a home'), + 'Chest Game': (0xeda8, False, 'as a prize'), 'Bumper Cave Ledge': (0x180146, False, 'on a ledge'), - 'Mire Shed - Left': (0xEA73, False, 'near sparks'), - 'Mire Shed - Right': (0xEA76, False, 'near sparks'), - 'Superbunny Cave - Top': (0xEA7C, False, 'in a connection'), - 'Superbunny Cave - Bottom': (0xEA7F, False, 'in a connection'), - 'Spike Cave': (0xEA8B, False, 'beyond spikes'), - 'Hookshot Cave - Top Right': (0xEB51, False, 'across pits'), - 'Hookshot Cave - Top Left': (0xEB54, False, 'across pits'), - 'Hookshot Cave - Bottom Right': (0xEB5A, False, 'across pits'), - 'Hookshot Cave - Bottom Left': (0xEB57, False, 'across pits'), + 'Mire Shed - Left': (0xea73, False, 'near sparks'), + 'Mire Shed - Right': (0xea76, False, 'near sparks'), + 'Superbunny Cave - Top': (0xea7c, False, 'in a connection'), + 'Superbunny Cave - Bottom': (0xea7f, False, 'in a connection'), + 'Spike Cave': (0xea8b, False, 'beyond spikes'), + 'Hookshot Cave - Top Right': (0xeb51, False, 'across pits'), + 'Hookshot Cave - Top Left': (0xeb54, False, 'across pits'), + 'Hookshot Cave - Bottom Right': (0xeb5a, False, 'across pits'), + 'Hookshot Cave - Bottom Left': (0xeb57, False, 'across pits'), 'Floating Island': (0x180141, False, 'on an island'), - 'Mimic Cave': (0xE9C5, False, 'in a cave of mimicry'), - 'Swamp Palace - Entrance': (0xEA9D, False, 'in Swamp Palace'), - 'Swamp Palace - Map Chest': (0xE986, False, 'in Swamp Palace'), - 'Swamp Palace - Big Chest': (0xE989, 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 - West Chest': (0xEAA3, 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 - Waterfall Room': (0xEAAF, False, 'in Swamp Palace'), + 'Mimic Cave': (0xe9c5, False, 'in a cave of mimicry'), + 'Swamp Palace - Entrance': (0xea9d, False, 'in Swamp Palace'), + 'Swamp Palace - Map Chest': (0xe986, False, 'in Swamp Palace'), + 'Swamp Palace - Big Chest': (0xe989, 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 - West Chest': (0xeaa3, 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 - Waterfall Room': (0xeaaf, False, 'in Swamp Palace'), 'Swamp Palace - Boss': (0x180154, False, 'with Arrghus'), - 'Thieves\' Town - Big Key Chest': (0xEA04, False, 'in Thieves\' Town'), - 'Thieves\' Town - Map Chest': (0xEA01, False, 'in Thieves\' Town'), - 'Thieves\' Town - Compass Chest': (0xEA07, False, 'in Thieves\' Town'), - 'Thieves\' Town - Ambush Chest': (0xEA0A, False, 'in Thieves\' Town'), - 'Thieves\' Town - Attic': (0xEA0D, 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 - Boss': (0x180156, False, 'with Blind'), - 'Skull Woods - Compass Chest': (0xE992, False, 'in Skull Woods'), - 'Skull Woods - Map Chest': (0xE99B, False, 'in Skull Woods'), - 'Skull Woods - Big Chest': (0xE998, False, 'in Skull Woods'), - 'Skull Woods - Pot Prison': (0xE9A1, 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 - Bridge Room': (0xE9FE, False, 'near Mothula'), + "Thieves' Town - Big Key Chest": (0xea04, False, "in Thieves' Town"), + "Thieves' Town - Map Chest": (0xea01, False, "in Thieves' Town"), + "Thieves' Town - Compass Chest": (0xea07, False, "in Thieves' Town"), + "Thieves' Town - Ambush Chest": (0xea0a, False, "in Thieves' Town"), + "Thieves' Town - Attic": (0xea0d, 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 - Boss": (0x180156, False, 'with Blind'), + 'Skull Woods - Compass Chest': (0xe992, False, 'in Skull Woods'), + 'Skull Woods - Map Chest': (0xe99b, False, 'in Skull Woods'), + 'Skull Woods - Big Chest': (0xe998, False, 'in Skull Woods'), + 'Skull Woods - Pot Prison': (0xe9a1, 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 - Bridge Room': (0xe9fe, False, 'near Mothula'), 'Skull Woods - Boss': (0x180155, False, 'with Mothula'), - 'Ice Palace - Compass Chest': (0xE9D4, False, 'in Ice Palace'), - 'Ice Palace - Freezor Chest': (0xE995, 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 - Spike Room': (0xE9E0, 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 - Compass Chest': (0xe9d4, False, 'in Ice Palace'), + 'Ice Palace - Freezor Chest': (0xe995, 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 - Spike Room': (0xe9e0, 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 - Boss': (0x180157, False, 'with Kholdstare'), - 'Misery Mire - Big Chest': (0xEA67, False, 'in Misery Mire'), - 'Misery Mire - Map Chest': (0xEA6A, False, 'in Misery Mire'), - 'Misery Mire - Main Lobby': (0xEA5E, False, 'in Misery Mire'), - 'Misery Mire - Bridge Chest': (0xEA61, False, 'in Misery Mire'), - 'Misery Mire - Spike Chest': (0xE9DA, 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 Chest': (0xea67, False, 'in Misery Mire'), + 'Misery Mire - Map Chest': (0xea6a, False, 'in Misery Mire'), + 'Misery Mire - Main Lobby': (0xea5e, False, 'in Misery Mire'), + 'Misery Mire - Bridge Chest': (0xea61, False, 'in Misery Mire'), + 'Misery Mire - Spike Chest': (0xe9da, 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 - Boss': (0x180158, False, 'with Vitreous'), - 'Turtle Rock - Compass Chest': (0xEA22, 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 - Chain Chomps': (0xEA16, 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 - Crystaroller Room': (0xEA34, 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 - Top Left': (0xEA2B, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Top Right': (0xEA28, 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 - Right': (0xea1f, 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 Chest': (0xea19, 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 Right': (0xea2e, 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 - Boss': (0x180159, False, 'with Trinexx'), - '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 - Stalfos Basement': (0xEA49, 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 - Map Chest': (0xEA52, 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 - Right': (0xEA4F, 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 - Big Chest': (0xEA40, False, 'in Palace of Darkness'), - 'Palace of Darkness - Harmless Hellway': (0xEA46, 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 - Stalfos Basement': (0xea49, 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 - Map Chest': (0xea52, 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 - Right': (0xea4f, 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 - Big Chest': (0xea40, 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'), - '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 - Right': (0xEADC, 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 Right': (0xEAE8, 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 - 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 - Bottom Left': (0xEABE, 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 - 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 Right': (0xEAC7, 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 - Bob\'s Chest': (0xEADF, 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 - Right': (0xEAF7, 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 - Right': (0xEB00, 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 - Bob's Torch": (0x180161, 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 - 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 Right': (0xeae8, 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 - 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 - Bottom Left': (0xeabe, 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 - 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 Right': (0xeac7, 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 - Bob's Chest": (0xeadf, 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 - Right': (0xeaf7, 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 - Right': (0xeb00, 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"), 'Ganon': (None, False, 'from me'), 'Agahnim 1': (None, False, 'from Ganon\'s wizardry form'), 'Agahnim 2': (None, False, 'from Ganon\'s wizardry form'), diff --git a/Rom.py b/Rom.py index 9f21eb0d74..c439f07d84 100644 --- a/Rom.py +++ b/Rom.py @@ -6,13 +6,13 @@ import os import struct import random -from BaseClasses import ShopType +from BaseClasses import ShopType, Region, Location, Item from Dungeons import dungeon_music_addresses 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 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 Items import ItemFactory +from Items import ItemFactory, item_table JAP10HASH = '03a63945398191337e896e5771f77173' @@ -22,6 +22,7 @@ RANDOMIZERBASEHASH = 'cb560220b7b1b8202e92381aee19cd36' class JsonRom(object): def __init__(self): + self.name = None self.patches = {} def write_byte(self, address, value): @@ -52,6 +53,7 @@ class JsonRom(object): class LocalRom(object): def __init__(self, file, patch=True): + self.name = None with open(file, 'rb') as stream: self.buffer = read_rom(stream) if patch: @@ -273,15 +275,18 @@ class Sprite(object): # split into palettes of 15 colors 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 for location in world.get_locations(): - itemid = location.item.code if location.item is not None else 0x5A - - if itemid is None or location.address is None: + if location.player != player: + continue + + itemid = location.item.code if location.item is not None else 0x5A + + if location.address is None: continue - locationaddress = location.address if not location.crystal: # Keys in their native dungeon should use the orignal item code for keys if location.parent_region.dungeon: @@ -291,10 +296,10 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): itemid = 0x32 if location.item.type == "SmallKey": itemid = 0x24 - rom.write_byte(locationaddress, itemid) + rom.write_byte(location.address, itemid) else: # crystals - for address, value in zip(locationaddress, itemid): + for address, value in zip(location.address, itemid): rom.write_byte(address, value) # patch music @@ -312,7 +317,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): # patch entrance/exits/holes for region in world.regions: 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): 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 @@ -360,25 +365,25 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): # patch door table rom.write_byte(0xDBB73 + exit.addresses, exit.target) - write_custom_shops(rom, world) + write_custom_shops(rom, world, player) # 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(0x4FF2, 0x31) # sprite rom.write_byte(0x50D1, 0x80) 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(0x4FF2, 0x31) # sprite rom.write_byte(0x50D1, 0x88) 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(0x5020, 0x31) # sprite rom.write_byte(0x50FF, 0x90) 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(0x5020, 0x31) # sprite 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) GREEN_TWENTY_RUPEES = 0x47 - TRIFORCE_PIECE = ItemFactory('Triforce Piece').code - GREEN_CLOCK = ItemFactory('Green Clock').code + TRIFORCE_PIECE = ItemFactory('Triforce Piece', player).code + GREEN_CLOCK = ItemFactory('Green Clock', player).code rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on # handle difficulty @@ -713,7 +718,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): # assorted fixes 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(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(0x180168, 0x08) # Spike Cave Damage 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]) # 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. - 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 if world.shuffle in ['restricted', 'full', 'crossed', 'insanity']: rom.write_byte(0x18004C, 0x01) # 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) else: 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(0xFEE41, 0x2A) # preopen bombable exit - write_strings(rom, world) + write_strings(rom, world, player) # set rom name # 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 hashint = int(rom.get_hash(), 16) @@ -848,8 +856,8 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): return rom -def write_custom_shops(rom, world): - shops = [shop for shop in world.shops if shop.replaceable and shop.active] +def write_custom_shops(rom, world, player): + shops = [shop for shop in world.shops if shop.replaceable and shop.active and shop.region.player == player] shop_data = bytearray() items_data = bytearray() @@ -870,7 +878,7 @@ def write_custom_shops(rom, world): for item in shop.inventory: if item is None: 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) 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)) -def write_strings(rom, world): +def write_strings(rom, world, player): tt = TextTable() 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'] = '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. if world.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'}) hint_locations = HintLocations.copy() 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) - hint_count = 4 + hint_count = 4 if world.shuffle != 'vanilla' else 0 for entrance in all_entrances: if entrance.name in entrances_to_hint: - this_hint = entrances_to_hint[entrance.name] + ' leads to ' + entrance.connected_region.hint_text + '.' - tt[hint_locations.pop(0)] = this_hint - entrances_to_hint.pop(entrance.name) - hint_count -= 1 - if hint_count < 1: + if hint_count > 0: + this_hint = entrances_to_hint[entrance.name] + ' leads to ' + hint_text(entrance.connected_region) + '.' + tt[hint_locations.pop(0)] = this_hint + entrances_to_hint.pop(entrance.name) + hint_count -= 1 + else: break entrances_to_hint.update(OtherEntrances) @@ -1048,57 +1068,58 @@ def write_strings(rom, world): entrances_to_hint.update(InsanityEntrances) if world.shuffle_ganon: 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: if entrance.name in entrances_to_hint: - this_hint = entrances_to_hint[entrance.name] + ' leads to ' + entrance.connected_region.hint_text + '.' - tt[hint_locations.pop(0)] = this_hint - entrances_to_hint.pop(entrance.name) - hint_count -= 1 - if hint_count < 1: + if hint_count > 0: + this_hint = entrances_to_hint[entrance.name] + ' leads to ' + hint_text(entrance.connected_region) + '.' + tt[hint_locations.pop(0)] = this_hint + entrances_to_hint.pop(entrance.name) + hint_count -= 1 + else: break # 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() random.shuffle(locations_to_hint) - hint_count = 3 - del locations_to_hint[hint_count:] + hint_count = 3 if world.shuffle != 'vanilla' else 4 + del locations_to_hint[hint_count:] for location in locations_to_hint: if location == 'Swamp Left': if random.randint(0, 1) == 0: - first_item = world.get_location('Swamp Palace - West Chest').item.hint_text - second_item = world.get_location('Swamp Palace - Big Key Chest').item.hint_text + first_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item) + second_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item) else: - second_item = world.get_location('Swamp Palace - West Chest').item.hint_text - first_item = world.get_location('Swamp Palace - Big Key Chest').item.hint_text + second_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item) + 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 + '.') tt[hint_locations.pop(0)] = this_hint elif location == 'Mire Left': if random.randint(0, 1) == 0: - first_item = world.get_location('Misery Mire - Compass Chest').item.hint_text - second_item = world.get_location('Misery Mire - Big Key Chest').item.hint_text + first_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item) + second_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item) else: - second_item = world.get_location('Misery Mire - Compass Chest').item.hint_text - first_item = world.get_location('Misery Mire - Big Key Chest').item.hint_text + second_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item) + 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 + '.') tt[hint_locations.pop(0)] = this_hint 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 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 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 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 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 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 # 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: items_to_hint.extend(KeysanityItems) random.shuffle(items_to_hint) - hint_count = 5 - while(hint_count > 0): + hint_count = 5 if world.shuffle != 'vanilla' else 7 + while hint_count > 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) 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 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. junk_hints = junk_texts.copy() @@ -1125,16 +1144,16 @@ def write_strings(rom, world): tt[location] = junk_hints.pop(0) # 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) - 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 - crystal5 = world.find_items('Crystal 5')[0] - crystal6 = world.find_items('Crystal 6')[0] + crystal5 = world.find_items('Crystal 5', player)[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) - 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['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['kakariko_tavern_fisherman'] = TavernMan_texts[random.randint(0, len(TavernMan_texts) - 1)] - pedestalitem = world.get_location('Master Sword Pedestal').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' + pedestalitem = world.get_location('Master Sword Pedestal', player).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 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 - 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' + etheritem = world.get_location('Ether Tablet', player).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 - bombositem = world.get_location('Bombos Tablet').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' + bombositem = world.get_location('Bombos Tablet', player).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 rom.write_bytes(0xE0000, tt.getBytes()) 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 - 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 - 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 - 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 credits.update_credits_line('castle', 0, random.choice(KingsReturn_texts)) diff --git a/Rules.py b/Rules.py index 93df6081f1..bf6f0a50f9 100644 --- a/Rules.py +++ b/Rules.py @@ -2,29 +2,29 @@ import collections import logging -def set_rules(world): +def set_rules(world, player): if world.logic == 'nologic': logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!') - world.get_region('Links House').can_reach = lambda state: True - world.get_region('Sanctuary').can_reach = lambda state: True - old_rule = world.get_region('Old Man House').can_reach - world.get_region('Old Man House').can_reach = lambda state: state.can_reach('Old Man', 'Location') or old_rule(state) + world.get_region('Links House', player).can_reach = lambda state: True + world.get_region('Sanctuary', player).can_reach = lambda state: True + old_rule = world.get_region('Old Man House', player).can_reach + world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) return - global_rules(world) + global_rules(world, player) if world.mode == 'open': - open_rules(world) + open_rules(world, player) elif world.mode == 'standard': - standard_rules(world) + standard_rules(world, player) elif world.mode == 'swordless': - swordless_rules(world) + swordless_rules(world, player) else: raise NotImplementedError('Not implemented yet') if world.logic == 'noglitches': - no_glitches_rules(world) + no_glitches_rules(world, player) elif world.logic == 'minorglitches': logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.') else: @@ -32,18 +32,18 @@ def set_rules(world): if world.goal == 'dungeons': # require all dungeons to beat ganon - add_rule(world.get_location('Ganon'), lambda state: state.can_reach('Master Sword Pedestal', 'Location') and state.has('Beat Agahnim 1') and state.has('Beat Agahnim 2')) + add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has('Beat Agahnim 1', player) and state.has('Beat Agahnim 2', player)) elif world.goal == 'ganon': # require aga2 to beat ganon - add_rule(world.get_location('Ganon'), lambda state: state.has('Beat Agahnim 2')) + add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) - set_big_bomb_rules(world) + set_big_bomb_rules(world, player) # if swamp and dam have not been moved we require mirror for swamp palace - if not world.swamp_patch_required: - add_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has_Mirror()) + if not world.swamp_patch_required[player]: + add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has_Mirror(player)) - set_bunny_rules(world) + set_bunny_rules(world, player) def set_rule(spot, rule): @@ -64,375 +64,380 @@ def add_rule(spot, rule, combine='and'): spot.access_rule = lambda state: rule(state) and old_rule(state) -def add_lamp_requirement(spot): - add_rule(spot, lambda state: state.has('Lamp', state.world.lamps_needed_for_dark_rooms)) +def add_lamp_requirement(spot, player): + add_rule(spot, lambda state: state.has('Lamp', player, state.world.lamps_needed_for_dark_rooms)) -def forbid_item(location, item): +def forbid_item(location, item, player): old_rule = location.item_rule - location.item_rule = lambda i: i.name != item and old_rule(i) + location.item_rule = lambda i: (i.name != item or i.player != player) and old_rule(i) -def item_in_locations(state, item, locations): +def item_in_locations(state, item, player, locations): for location in locations: - if item_name(state, location) == item: + if item_name(state, location[0], location[1]) == (item, player): return True return False -def item_name(state, location): - location = state.world.get_location(location) +def item_name(state, location, player): + location = state.world.get_location(location, player) if location.item is None: return None - return location.item.name + return (location.item.name, location.item.player) -def global_rules(world): +def global_rules(world, player): + if world.goal == 'triforcehunt': + for location in world.get_locations(): + if location.player != player: + forbid_item(location, 'Triforce Piece', player) + # ganon can only carry triforce - world.get_location('Ganon').item_rule = lambda item: item.name == 'Triforce' + world.get_location('Ganon', player).item_rule = lambda item: item.name == 'Triforce' and item.player == player # these are default save&quit points and always accessible - world.get_region('Links House').can_reach = lambda state: True - world.get_region('Sanctuary').can_reach = lambda state: True + world.get_region('Links House', player).can_reach = lambda state: True + world.get_region('Sanctuary', player).can_reach = lambda state: True # we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled! - old_rule = world.get_region('Old Man House').can_reach - world.get_region('Old Man House').can_reach = lambda state: state.can_reach('Old Man', 'Location') or old_rule(state) + old_rule = world.get_region('Old Man House', player).can_reach + world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) # overworld requirements - set_rule(world.get_entrance('Kings Grave'), lambda state: state.has_Boots()) - set_rule(world.get_entrance('Kings Grave Outer Rocks'), lambda state: state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Kings Grave Inner Rocks'), lambda state: state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Kings Grave Mirror Spot'), lambda state: state.has_Pearl() and state.has_Mirror()) + set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has_Boots(player)) + set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player)) # Caution: If king's grave is releaxed at all to account for reaching it via a two way cave's exit in insanity mode, then the bomb shop logic will need to be updated (that would involve create a small ledge-like Region for it) - set_rule(world.get_entrance('Bonk Fairy (Light)'), lambda state: state.has_Boots()) - set_rule(world.get_location('Sunken Treasure'), lambda state: state.can_reach('Dam')) - set_rule(world.get_entrance('Bat Cave Drop Ledge'), lambda state: state.has('Hammer')) - set_rule(world.get_entrance('Lumberjack Tree Tree'), lambda state: state.has_Boots() and state.has('Beat Agahnim 1')) - set_rule(world.get_entrance('Bonk Rock Cave'), lambda state: state.has_Boots()) - set_rule(world.get_entrance('Desert Palace Stairs'), lambda state: state.has('Book of Mudora')) - set_rule(world.get_entrance('Sanctuary Grave'), lambda state: state.can_lift_rocks()) - set_rule(world.get_entrance('20 Rupee Cave'), lambda state: state.can_lift_rocks()) - set_rule(world.get_entrance('50 Rupee Cave'), lambda state: state.can_lift_rocks()) - set_rule(world.get_entrance('Death Mountain Entrance Rock'), lambda state: state.can_lift_rocks()) - set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Flute Spot 1'), lambda state: state.has('Ocarina')) - set_rule(world.get_entrance('Lake Hylia Central Island Teleporter'), lambda state: state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Dark Desert Teleporter'), lambda state: state.has('Ocarina') and state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('East Hyrule Teleporter'), lambda state: state.has('Hammer') and state.can_lift_rocks() and state.has_Pearl()) # bunny cannot use hammer - set_rule(world.get_entrance('South Hyrule Teleporter'), lambda state: state.has('Hammer') and state.can_lift_rocks() and state.has_Pearl()) # bunny cannot use hammer - set_rule(world.get_entrance('Kakariko Teleporter'), lambda state: ((state.has('Hammer') and state.can_lift_rocks()) or state.can_lift_heavy_rocks()) and state.has_Pearl()) # bunny cannot lift bushes - set_rule(world.get_location('Flute Spot'), lambda state: state.has('Shovel')) - set_rule(world.get_location('Dark Blacksmith Ruins'), lambda state: state.has('Return Smith')) - set_rule(world.get_location('Purple Chest'), lambda state: state.has('Pick Up Purple Chest')) # Can S&Q with chest + set_rule(world.get_entrance('Bonk Fairy (Light)', player), lambda state: state.has_Boots(player)) + set_rule(world.get_location('Sunken Treasure', player), lambda state: state.can_reach('Dam', 'Region', player)) + set_rule(world.get_entrance('Bat Cave Drop Ledge', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lumberjack Tree Tree', player), lambda state: state.has_Boots(player) and state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('Bonk Rock Cave', player), lambda state: state.has_Boots(player)) + set_rule(world.get_entrance('Desert Palace Stairs', player), lambda state: state.has('Book of Mudora', player)) + set_rule(world.get_entrance('Sanctuary Grave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('20 Rupee Cave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Flute Spot 1', player), lambda state: state.has('Ocarina', player)) + set_rule(world.get_entrance('Lake Hylia Central Island Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.has('Ocarina', player) and state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer + set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer + set_rule(world.get_entrance('Kakariko Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) # bunny cannot lift bushes + set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player)) + set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) + set_rule(world.get_location('Purple Chest', player), lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest - set_rule(world.get_location('Zora\'s Ledge'), lambda state: state.has('Flippers')) - set_rule(world.get_entrance('Waterfall of Wishing'), lambda state: state.has('Flippers')) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. Can be improved in future Todo - set_rule(world.get_location('Frog'), lambda state: state.can_lift_heavy_rocks()) # will get automatic moon pearl requirement - set_rule(world.get_location('Missing Smith'), lambda state: state.has('Get Frog')) # Can S&Q with smith - set_rule(world.get_location('Blacksmith'), lambda state: state.has('Return Smith')) - set_rule(world.get_location('Magic Bat'), lambda state: state.has('Magic Powder')) - set_rule(world.get_location('Sick Kid'), lambda state: state.has_bottle()) - set_rule(world.get_location('Library'), lambda state: state.has_Boots()) - set_rule(world.get_location('Potion Shop'), lambda state: state.has('Mushroom')) - set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks'), lambda state: state.can_lift_rocks()) - set_rule(world.get_entrance('Desert Ledge Return Rocks'), lambda state: state.can_lift_rocks()) # should we decide to place something that is not a dungeon end up there at some point - set_rule(world.get_entrance('Checkerboard Cave'), lambda state: state.can_lift_rocks()) - set_rule(world.get_location('Master Sword Pedestal'), lambda state: state.has('Red Pendant') and state.has('Blue Pendant') and state.has('Green Pendant')) - set_rule(world.get_location('Sahasrahla'), lambda state: state.has('Green Pendant')) - set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has_beam_sword() or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle - set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has_sword() and state.has_key('Small Key (Agahnims Tower)', 2)) - set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1')) - set_rule(world.get_location('Castle Tower - Dark Maze'), lambda state: state.has_key('Small Key (Agahnims Tower)')) - set_rule(world.get_entrance('Top of Pyramid'), lambda state: state.has('Beat Agahnim 1')) - set_rule(world.get_entrance('Old Man Cave Exit (West)'), lambda state: False) # drop cannot be climbed up - set_rule(world.get_entrance('Broken Bridge (West)'), lambda state: state.has('Hookshot')) - set_rule(world.get_entrance('Broken Bridge (East)'), lambda state: state.has('Hookshot')) - set_rule(world.get_entrance('East Death Mountain Teleporter'), lambda state: state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Fairy Ascension Rocks'), lambda state: state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Paradox Cave Push Block Reverse'), lambda state: state.has('Mirror')) # can erase block - set_rule(world.get_entrance('Death Mountain (Top)'), lambda state: state.has('Hammer')) - set_rule(world.get_entrance('Turtle Rock Teleporter'), lambda state: state.can_lift_heavy_rocks() and state.has('Hammer')) - set_rule(world.get_location('Ether Tablet'), lambda state: state.has('Book of Mudora') and state.has_beam_sword()) - set_rule(world.get_entrance('East Death Mountain (Top)'), lambda state: state.has('Hammer')) + set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Waterfall of Wishing', player), lambda state: state.has('Flippers', player)) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. Can be improved in future Todo + set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player)) # will get automatic moon pearl requirement + set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player)) # Can S&Q with smith + set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) + set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player)) + set_rule(world.get_location('Sick Kid', player), lambda state: state.has_bottle(player)) + set_rule(world.get_location('Library', player), lambda state: state.has_Boots(player)) + set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player)) + set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Desert Ledge Return Rocks', player), lambda state: state.can_lift_rocks(player)) # should we decide to place something that is not a dungeon end up there at some point + set_rule(world.get_entrance('Checkerboard Cave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_location('Master Sword Pedestal', player), lambda state: state.has('Red Pendant', player) and state.has('Blue Pendant', player) and state.has('Green Pendant', player)) + set_rule(world.get_location('Sahasrahla', player), lambda state: state.has('Green Pendant', player)) + set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle + set_rule(world.get_entrance('Agahnim 1', player), lambda state: state.has_sword(player) and state.has_key('Small Key (Agahnims Tower)', player, 2)) + set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player)) + set_rule(world.get_location('Castle Tower - Dark Maze', player), lambda state: state.has_key('Small Key (Agahnims Tower)', player)) + set_rule(world.get_entrance('Top of Pyramid', player), lambda state: state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up + set_rule(world.get_entrance('Broken Bridge (West)', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('Broken Bridge (East)', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('East Death Mountain Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Mirror', player)) # can erase block + set_rule(world.get_entrance('Death Mountain (Top)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) + set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) + set_rule(world.get_entrance('East Death Mountain (Top)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_location('Catfish'), lambda state: state.can_lift_rocks()) - set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass'), lambda state: state.has_Pearl() and (state.can_lift_rocks() or state.has('Hammer') or state.has('Flippers'))) - set_rule(world.get_entrance('East Dark World Broken Bridge Pass'), lambda state: state.has_Pearl() and (state.can_lift_rocks() or state.has('Hammer'))) - set_rule(world.get_entrance('South Dark World Bridge'), lambda state: state.has('Hammer') and state.has_Pearl()) - set_rule(world.get_entrance('Bonk Fairy (Dark)'), lambda state: state.has_Pearl() and state.has_Boots()) - set_rule(world.get_entrance('West Dark World Gap'), lambda state: state.has_Pearl() and state.has('Hookshot')) - set_rule(world.get_entrance('Palace of Darkness'), lambda state: state.has_Pearl()) # kiki needs pearl - set_rule(world.get_entrance('Hyrule Castle Ledge Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Hyrule Castle Main Gate'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Dark Lake Hylia Drop (East)'), lambda state: (state.has_Pearl() and state.has('Flippers') or state.has_Mirror())) # Overworld Bunny Revival - set_rule(world.get_location('Bombos Tablet'), lambda state: state.has('Book of Mudora') and state.has_beam_sword() and state.has_Mirror()) - set_rule(world.get_entrance('Dark Lake Hylia Drop (South)'), lambda state: state.has_Pearl() and state.has('Flippers')) # ToDo any fake flipper set up? - set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy'), lambda state: state.has_Pearl()) # bomb required - set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave'), lambda state: state.can_lift_rocks() and state.has_Pearl()) - set_rule(world.get_entrance('Dark Lake Hylia Teleporter'), lambda state: state.has_Pearl() and (state.has('Hammer') or state.can_lift_rocks())) # Fake Flippers - set_rule(world.get_entrance('Village of Outcasts Heavy Rock'), lambda state: state.has_Pearl() and state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Hype Cave'), lambda state: state.has_Pearl()) # bomb required - set_rule(world.get_entrance('Brewery'), lambda state: state.has_Pearl()) # bomb required - set_rule(world.get_entrance('Thieves Town'), lambda state: state.has_Pearl()) # bunny cannot pull - set_rule(world.get_entrance('Skull Woods First Section Hole (North)'), lambda state: state.has_Pearl()) # bunny cannot lift bush - set_rule(world.get_entrance('Skull Woods Second Section Hole'), lambda state: state.has_Pearl()) # bunny cannot lift bush - set_rule(world.get_entrance('Maze Race Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Cave 45 Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('East Dark World Bridge'), lambda state: state.has_Pearl() and state.has('Hammer')) - set_rule(world.get_entrance('Lake Hylia Island Mirror Spot'), lambda state: state.has_Pearl() and state.has_Mirror() and state.has('Flippers')) - set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('East Dark World River Pier'), lambda state: state.has_Pearl() and state.has('Flippers')) # ToDo any fake flipper set up? - set_rule(world.get_entrance('Graveyard Ledge Mirror Spot'), lambda state: state.has_Pearl() and state.has_Mirror()) - set_rule(world.get_entrance('Bumper Cave Entrance Rock'), lambda state: state.has_Pearl() and state.can_lift_rocks()) - set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Bat Cave Drop Ledge Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Dark World Hammer Peg Cave'), lambda state: state.has_Pearl() and state.has('Hammer')) - set_rule(world.get_entrance('Village of Outcasts Eastern Rocks'), lambda state: state.has_Pearl() and state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Peg Area Rocks'), lambda state: state.has_Pearl() and state.can_lift_heavy_rocks()) - set_rule(world.get_entrance('Village of Outcasts Pegs'), lambda state: state.has_Pearl() and state.has('Hammer')) - set_rule(world.get_entrance('Grassy Lawn Pegs'), lambda state: state.has_Pearl() and state.has('Hammer')) - set_rule(world.get_entrance('Bumper Cave Exit (Top)'), lambda state: state.has('Cape')) - set_rule(world.get_entrance('Bumper Cave Exit (Bottom)'), lambda state: state.has('Cape') or state.has('Hookshot')) + set_rule(world.get_location('Catfish', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass', player), lambda state: state.has_Pearl(player) and (state.can_lift_rocks(player) or state.has('Hammer', player) or state.has('Flippers', player))) + set_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: state.has_Pearl(player) and (state.can_lift_rocks(player) or state.has('Hammer', player))) + set_rule(world.get_entrance('South Dark World Bridge', player), lambda state: state.has('Hammer', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Bonk Fairy (Dark)', player), lambda state: state.has_Pearl(player) and state.has_Boots(player)) + set_rule(world.get_entrance('West Dark World Gap', player), lambda state: state.has_Pearl(player) and state.has('Hookshot', player)) + set_rule(world.get_entrance('Palace of Darkness', player), lambda state: state.has_Pearl(player)) # kiki needs pearl + set_rule(world.get_entrance('Hyrule Castle Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Hyrule Castle Main Gate', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: (state.has_Pearl(player) and state.has('Flippers', player) or state.has_Mirror(player))) # Overworld Bunny Revival + set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player) and state.has_Mirror(player)) + set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # ToDo any fake flipper set up? + set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: state.has_Pearl(player)) # bomb required + set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) # Fake Flippers + set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.has_Pearl(player) and state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Hype Cave', player), lambda state: state.has_Pearl(player)) # bomb required + set_rule(world.get_entrance('Brewery', player), lambda state: state.has_Pearl(player)) # bomb required + set_rule(world.get_entrance('Thieves Town', player), lambda state: state.has_Pearl(player)) # bunny cannot pull + set_rule(world.get_entrance('Skull Woods First Section Hole (North)', player), lambda state: state.has_Pearl(player)) # bunny cannot lift bush + set_rule(world.get_entrance('Skull Woods Second Section Hole', player), lambda state: state.has_Pearl(player)) # bunny cannot lift bush + set_rule(world.get_entrance('Maze Race Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Cave 45 Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('East Dark World Bridge', player), lambda state: state.has_Pearl(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Lake Hylia Island Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player) and state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # ToDo any fake flipper set up? + set_rule(world.get_entrance('Graveyard Ledge Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player)) + set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.has_Pearl(player) and state.can_lift_rocks(player)) + set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Bat Cave Drop Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Dark World Hammer Peg Cave', player), lambda state: state.has_Pearl(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Village of Outcasts Eastern Rocks', player), lambda state: state.has_Pearl(player) and state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Peg Area Rocks', player), lambda state: state.has_Pearl(player) and state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Village of Outcasts Pegs', player), lambda state: state.has_Pearl(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Grassy Lawn Pegs', player), lambda state: state.has_Pearl(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player)) + set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player)) - set_rule(world.get_entrance('Skull Woods Final Section'), lambda state: state.has('Fire Rod') and state.has_Pearl()) # bunny cannot use fire rod - set_rule(world.get_entrance('Misery Mire'), lambda state: state.has_Pearl() and state.has_sword() and state.has_misery_mire_medallion()) # sword required to cast magic (!) - set_rule(world.get_entrance('Desert Ledge (Northeast) Mirror Spot'), lambda state: state.has_Mirror()) + set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player) and state.has_Pearl(player)) # bunny cannot use fire rod + set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!) + set_rule(world.get_entrance('Desert Ledge (Northeast) Mirror Spot', player), lambda state: state.has_Mirror(player)) - set_rule(world.get_entrance('Desert Ledge Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Desert Palace Stairs Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Desert Palace Entrance (North) Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Spectacle Rock Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Hookshot Cave'), lambda state: state.can_lift_rocks() and state.has_Pearl()) + set_rule(world.get_entrance('Desert Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Desert Palace Stairs Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Desert Palace Entrance (North) Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Spectacle Rock Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Hookshot Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) - set_rule(world.get_entrance('East Death Mountain (Top) Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Mimic Cave Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Spiral Cave Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Fairy Ascension Mirror Spot'), lambda state: state.has_Mirror() and state.has_Pearl()) # need to lift flowers - set_rule(world.get_entrance('Isolated Ledge Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)'), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling + set_rule(world.get_entrance('East Death Mountain (Top) Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Mimic Cave Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Spiral Cave Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Fairy Ascension Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player)) # need to lift flowers + set_rule(world.get_entrance('Isolated Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling - set_rule(world.get_location('Spike Cave'), lambda state: - state.has('Hammer') and state.can_lift_rocks() and - ((state.has('Cape') and state.can_extend_magic(16, True)) or - (state.has('Cane of Byrna') and - (state.can_extend_magic(12, True) or - (state.world.can_take_damage and (state.has_Boots() or state.has_hearts(4)))))) + set_rule(world.get_location('Spike Cave', player), lambda state: + state.has('Hammer', player) and state.can_lift_rocks(player) and + ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or + (state.has('Cane of Byrna', player) and + (state.can_extend_magic(player, 12, True) or + (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4)))))) ) - set_rule(world.get_location('Hookshot Cave - Top Right'), lambda state: state.has('Hookshot')) - set_rule(world.get_location('Hookshot Cave - Top Left'), lambda state: state.has('Hookshot')) - set_rule(world.get_location('Hookshot Cave - Bottom Right'), lambda state: state.has('Hookshot') or state.has('Pegasus Boots')) - set_rule(world.get_location('Hookshot Cave - Bottom Left'), lambda state: state.has('Hookshot')) - set_rule(world.get_entrance('Floating Island Mirror Spot'), lambda state: state.has_Mirror()) - set_rule(world.get_entrance('Turtle Rock'), lambda state: state.has_Pearl() and state.has_sword() and state.has_turtle_rock_medallion() and state.can_reach('Turtle Rock (Top)', 'Region')) # sword required to cast magic (!) - set_rule(world.get_location('Mimic Cave'), lambda state: state.has('Hammer')) + set_rule(world.get_location('Hookshot Cave - Top Right', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_location('Hookshot Cave - Top Left', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_location('Hookshot Cave - Bottom Right', player), lambda state: state.has('Hookshot', player) or state.has('Pegasus Boots', player)) + set_rule(world.get_location('Hookshot Cave - Bottom Left', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('Floating Island Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) + set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Sewers Door'), lambda state: state.has_key('Small Key (Escape)')) - set_rule(world.get_entrance('Sewers Back Door'), lambda state: state.has_key('Small Key (Escape)')) + set_rule(world.get_entrance('Sewers Door', player), lambda state: state.has_key('Small Key (Escape)', player)) + set_rule(world.get_entrance('Sewers Back Door', player), lambda state: state.has_key('Small Key (Escape)', player)) - set_rule(world.get_location('Eastern Palace - Big Chest'), lambda state: state.has('Big Key (Eastern Palace)')) - set_rule(world.get_location('Eastern Palace - Boss'), lambda state: state.can_shoot_arrows() and state.has('Big Key (Eastern Palace)') and world.get_location('Eastern Palace - Boss').parent_region.dungeon.boss.can_defeat(state)) - set_rule(world.get_location('Eastern Palace - Prize'), lambda state: state.can_shoot_arrows() and state.has('Big Key (Eastern Palace)') and world.get_location('Eastern Palace - Prize').parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Eastern Palace - Big Chest', player), lambda state: state.has('Big Key (Eastern Palace)', player)) + set_rule(world.get_location('Eastern Palace - Boss', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Eastern Palace - Prize', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) for location in ['Eastern Palace - Boss', 'Eastern Palace - Big Chest']: - forbid_item(world.get_location(location), 'Big Key (Eastern Palace)') + forbid_item(world.get_location(location, player), 'Big Key (Eastern Palace)', player) - set_rule(world.get_location('Desert Palace - Big Chest'), lambda state: state.has('Big Key (Desert Palace)')) - set_rule(world.get_location('Desert Palace - Torch'), lambda state: state.has_Boots()) - set_rule(world.get_entrance('Desert Palace East Wing'), lambda state: state.has_key('Small Key (Desert Palace)')) - set_rule(world.get_location('Desert Palace - Prize'), lambda state: state.has_key('Small Key (Desert Palace)') and state.has('Big Key (Desert Palace)') and state.has_fire_source() and world.get_location('Desert Palace - Prize').parent_region.dungeon.boss.can_defeat(state)) - set_rule(world.get_location('Desert Palace - Boss'), lambda state: state.has_key('Small Key (Desert Palace)') and state.has('Big Key (Desert Palace)') and state.has_fire_source() and world.get_location('Desert Palace - Boss').parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player)) + set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has_Boots(player)) + set_rule(world.get_entrance('Desert Palace East Wing', player), lambda state: state.has_key('Small Key (Desert Palace)', player)) + set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and world.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state.has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and world.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) for location in ['Desert Palace - Boss', 'Desert Palace - Big Chest']: - forbid_item(world.get_location(location), 'Big Key (Desert Palace)') + forbid_item(world.get_location(location, player), 'Big Key (Desert Palace)', player) for location in ['Desert Palace - Boss', 'Desert Palace - Big Key Chest', 'Desert Palace - Compass Chest']: - forbid_item(world.get_location(location), 'Small Key (Desert Palace)') + forbid_item(world.get_location(location, player), 'Small Key (Desert Palace)', player) - set_rule(world.get_entrance('Tower of Hera Small Key Door'), lambda state: state.has_key('Small Key (Tower of Hera)') or item_name(state, 'Tower of Hera - Big Key Chest') == 'Small Key (Tower of Hera)') - set_rule(world.get_entrance('Tower of Hera Big Key Door'), lambda state: state.has('Big Key (Tower of Hera)')) - set_rule(world.get_location('Tower of Hera - Big Chest'), lambda state: state.has('Big Key (Tower of Hera)')) - set_rule(world.get_location('Tower of Hera - Big Key Chest'), lambda state: state.has_fire_source()) - set_always_allow(world.get_location('Tower of Hera - Big Key Chest'), lambda state, item: item.name == 'Small Key (Tower of Hera)') - set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize')) + set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state.has_key('Small Key (Tower of Hera)', player) or item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player)) + set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) + set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player)) + set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player)) + set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) + set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player)) for location in ['Tower of Hera - Boss', 'Tower of Hera - Big Chest', 'Tower of Hera - Compass Chest']: - forbid_item(world.get_location(location), 'Big Key (Tower of Hera)') + forbid_item(world.get_location(location, player), 'Big Key (Tower of Hera)', player) # for location in ['Tower of Hera - Big Key Chest']: -# forbid_item(world.get_location(location), 'Small Key (Tower of Hera)') +# forbid_item(world.get_location(location, player), 'Small Key (Tower of Hera)', player) - set_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has('Flippers') and state.has('Open Floodgate')) - add_rule(world.get_location('Sunken Treasure'), lambda state: state.has('Open Floodgate')) + set_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player)) + add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) - set_rule(world.get_entrance('Swamp Palace Small Key Door'), lambda state: state.has_key('Small Key (Swamp Palace)')) - set_rule(world.get_entrance('Swamp Palace (Center)'), lambda state: state.has('Hammer')) - set_rule(world.get_location('Swamp Palace - Big Chest'), lambda state: state.has('Big Key (Swamp Palace)') or item_name(state, 'Swamp Palace - Big Chest') == 'Big Key (Swamp Palace)') - set_always_allow(world.get_location('Swamp Palace - Big Chest'), lambda state, item: item.name == 'Big Key (Swamp Palace)') - set_rule(world.get_entrance('Swamp Palace (North)'), lambda state: state.has('Hookshot')) - set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize')) + set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state.has_key('Small Key (Swamp Palace)', player)) + set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player) or item_name(state, 'Swamp Palace - Big Chest', player) == ('Big Key (Swamp Palace)', player)) + set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) + set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player)) + set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player)) for location in ['Swamp Palace - Entrance']: - forbid_item(world.get_location(location), 'Big Key (Swamp Palace)') + forbid_item(world.get_location(location, player), 'Big Key (Swamp Palace)', player) - set_rule(world.get_entrance('Thieves Town Big Key Door'), lambda state: state.has('Big Key (Thieves Town)')) - set_rule(world.get_entrance('Blind Fight'), lambda state: state.has_key('Small Key (Thieves Town)')) - set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Prize')) - set_rule(world.get_location('Thieves\' Town - Big Chest'), lambda state: (state.has_key('Small Key (Thieves Town)') or item_name(state, 'Thieves\' Town - Big Chest') == 'Small Key (Thieves Town)') and state.has('Hammer')) - set_always_allow(world.get_location('Thieves\' Town - Big Chest'), lambda state, item: item.name == 'Small Key (Thieves Town)' and state.has('Hammer')) - set_rule(world.get_location('Thieves\' Town - Attic'), lambda state: state.has_key('Small Key (Thieves Town)')) + set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player)) + set_rule(world.get_entrance('Blind Fight', player), lambda state: state.has_key('Small Key (Thieves Town)', player)) + set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Prize', player)) + set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state.has_key('Small Key (Thieves Town)', player) or item_name(state, 'Thieves\' Town - Big Chest', player) == ('Small Key (Thieves Town)', player)) and state.has('Hammer', player)) + set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) + set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state.has_key('Small Key (Thieves Town)', player)) for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves\' Town - Boss']: - forbid_item(world.get_location(location), 'Big Key (Thieves Town)') + forbid_item(world.get_location(location, player), 'Big Key (Thieves Town)', player) for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Boss']: - forbid_item(world.get_location(location), 'Small Key (Thieves Town)') + forbid_item(world.get_location(location, player), 'Small Key (Thieves Town)', player) - set_rule(world.get_entrance('Skull Woods First Section South Door'), lambda state: state.has_key('Small Key (Skull Woods)')) - set_rule(world.get_entrance('Skull Woods First Section (Right) North Door'), lambda state: state.has_key('Small Key (Skull Woods)')) - set_rule(world.get_entrance('Skull Woods First Section West Door'), lambda state: state.has_key('Small Key (Skull Woods)', 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section - set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit'), lambda state: state.has_key('Small Key (Skull Woods)', 2)) - set_rule(world.get_location('Skull Woods - Big Chest'), lambda state: state.has('Big Key (Skull Woods)') or item_name(state, 'Skull Woods - Big Chest') == 'Big Key (Skull Woods)') - set_always_allow(world.get_location('Skull Woods - Big Chest'), lambda state, item: item.name == 'Big Key (Skull Woods)') - set_rule(world.get_entrance('Skull Woods Torch Room'), lambda state: state.has_key('Small Key (Skull Woods)', 3) and state.has('Fire Rod') and state.has_sword()) # sword required for curtain - set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize')) + set_rule(world.get_entrance('Skull Woods First Section South Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player)) + set_rule(world.get_entrance('Skull Woods First Section (Right) North Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player)) + set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section + set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) + set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) or item_name(state, 'Skull Woods - Big Chest', player) == ('Big Key (Skull Woods)', player)) + set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) + set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and state.has_sword(player)) # sword required for curtain + set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player)) for location in ['Skull Woods - Boss']: - forbid_item(world.get_location(location), 'Small Key (Skull Woods)') + forbid_item(world.get_location(location, player), 'Small Key (Skull Woods)', player) - set_rule(world.get_entrance('Ice Palace Entrance Room'), lambda state: state.has('Fire Rod') or (state.has('Bombos') and state.has_sword())) - set_rule(world.get_location('Ice Palace - Big Chest'), lambda state: state.has('Big Key (Ice Palace)')) - set_rule(world.get_entrance('Ice Palace (Kholdstare)'), lambda state: state.can_lift_rocks() and state.has('Hammer') and state.has('Big Key (Ice Palace)') and (state.has_key('Small Key (Ice Palace)', 2) or (state.has('Cane of Somaria') and state.has_key('Small Key (Ice Palace)', 1)))) + set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or (state.has('Bombos', player) and state.has_sword(player))) + set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player)) + set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state.has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state.has_key('Small Key (Ice Palace)', player, 1)))) # TODO: investigate change from VT. Changed to hookshot or 2 keys (no checking for big key in specific chests) - set_rule(world.get_entrance('Ice Palace (East)'), lambda state: (state.has('Hookshot') or (item_in_locations(state, 'Big Key (Ice Palace)', ['Ice Palace - Spike Room', 'Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']) and state.has_key('Small Key (Ice Palace)'))) and (state.world.can_take_damage or state.has('Hookshot') or state.has('Cape') or state.has('Cane of Byrna'))) - set_rule(world.get_entrance('Ice Palace (East Top)'), lambda state: state.can_lift_rocks() and state.has('Hammer')) - set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Prize')) + set_rule(world.get_entrance('Ice Palace (East)', player), lambda state: (state.has('Hookshot', player) or (item_in_locations(state, 'Big Key (Ice Palace)', player, [('Ice Palace - Spike Room', player), ('Ice Palace - Big Key Chest', player), ('Ice Palace - Map Chest', player)]) and state.has_key('Small Key (Ice Palace)', player))) and (state.world.can_take_damage or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player))) + set_rule(world.get_entrance('Ice Palace (East Top)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) + set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Prize', player)) for location in ['Ice Palace - Big Chest', 'Ice Palace - Boss']: - forbid_item(world.get_location(location), 'Big Key (Ice Palace)') + forbid_item(world.get_location(location, player), 'Big Key (Ice Palace)', player) - set_rule(world.get_entrance('Misery Mire Entrance Gap'), lambda state: (state.has_Boots() or state.has('Hookshot')) and (state.has_sword() or state.has('Fire Rod') or state.has('Ice Rod') or state.has('Hammer') or state.has('Cane of Somaria') or state.can_shoot_arrows())) # need to defeat wizzrobes, bombs don't work ... - set_rule(world.get_location('Misery Mire - Big Chest'), lambda state: state.has('Big Key (Misery Mire)')) - set_rule(world.get_location('Misery Mire - Spike Chest'), lambda state: (state.world.can_take_damage and state.has_hearts(4)) or state.has('Cane of Byrna') or state.has('Cape')) - set_rule(world.get_entrance('Misery Mire Big Key Door'), lambda state: state.has('Big Key (Misery Mire)')) + set_rule(world.get_entrance('Misery Mire Entrance Gap', player), lambda state: (state.has_Boots(player) or state.has('Hookshot', player)) and (state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player))) # need to defeat wizzrobes, bombs don't work ... + set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player)) + set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) + set_rule(world.get_entrance('Misery Mire Big Key Door', player), lambda state: state.has('Big Key (Misery Mire)', player)) # you can squander the free small key from the pot by opening the south door to the north west switch room, locking you out of accessing a color switch ... # big key gives backdoor access to that from the teleporter in the north west - set_rule(world.get_location('Misery Mire - Map Chest'), lambda state: state.has_key('Small Key (Misery Mire)', 1) or state.has('Big Key (Misery Mire)')) + set_rule(world.get_location('Misery Mire - Map Chest', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 1) or state.has('Big Key (Misery Mire)', player)) # in addition, you can open the door to the map room before getting access to a color switch, so this is locked behing 2 small keys or the big key... - set_rule(world.get_location('Misery Mire - Main Lobby'), lambda state: state.has_key('Small Key (Misery Mire)', 2) or state.has_key('Big Key (Misery Mire)')) + set_rule(world.get_location('Misery Mire - Main Lobby', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 2) or state.has_key('Big Key (Misery Mire)', player)) # we can place a small key in the West wing iff it also contains/blocks the Big Key, as we cannot reach and softlock with the basement key door yet - set_rule(world.get_entrance('Misery Mire (West)'), lambda state: state.has_key('Small Key (Misery Mire)', 2) if ((item_name(state, 'Misery Mire - Compass Chest') in ['Big Key (Misery Mire)']) or - (item_name(state, 'Misery Mire - Big Key Chest') in ['Big Key (Misery Mire)'])) else state.has_key('Small Key (Misery Mire)', 3)) - set_rule(world.get_location('Misery Mire - Compass Chest'), lambda state: state.has_fire_source()) - set_rule(world.get_location('Misery Mire - Big Key Chest'), lambda state: state.has_fire_source()) - set_rule(world.get_entrance('Misery Mire (Vitreous)'), lambda state: state.has('Cane of Somaria')) - set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize')) + set_rule(world.get_entrance('Misery Mire (West)', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 2) if ((item_name(state, 'Misery Mire - Compass Chest', player) in [('Big Key (Misery Mire)', player)]) or + (item_name(state, 'Misery Mire - Big Key Chest', player) in [('Big Key (Misery Mire)', player)])) else state.has_key('Small Key (Misery Mire)', player, 3)) + set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: state.has_fire_source(player)) + set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: state.has_fire_source(player)) + set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player)) + set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player)) for location in ['Misery Mire - Big Chest', 'Misery Mire - Boss']: - forbid_item(world.get_location(location), 'Big Key (Misery Mire)') + forbid_item(world.get_location(location, player), 'Big Key (Misery Mire)', player) - set_rule(world.get_entrance('Turtle Rock Entrance Gap'), lambda state: state.has('Cane of Somaria')) - set_rule(world.get_entrance('Turtle Rock Entrance Gap Reverse'), lambda state: state.has('Cane of Somaria')) - set_rule(world.get_location('Turtle Rock - Compass Chest'), lambda state: state.has('Cane of Somaria')) # We could get here from the middle section without Cane as we don't cross the entrance gap! - set_rule(world.get_location('Turtle Rock - Roller Room - Left'), lambda state: state.has('Cane of Somaria') and state.has('Fire Rod')) - set_rule(world.get_location('Turtle Rock - Roller Room - Right'), lambda state: state.has('Cane of Somaria') and state.has('Fire Rod')) - set_rule(world.get_location('Turtle Rock - Big Chest'), lambda state: state.has('Big Key (Turtle Rock)') and (state.has('Cane of Somaria') or state.has('Hookshot'))) - set_rule(world.get_entrance('Turtle Rock (Big Chest) (North)'), lambda state: state.has('Cane of Somaria') or state.has('Hookshot')) - set_rule(world.get_entrance('Turtle Rock Big Key Door'), lambda state: state.has('Big Key (Turtle Rock)')) - set_rule(world.get_entrance('Turtle Rock (Dark Room) (North)'), lambda state: state.has('Cane of Somaria')) - set_rule(world.get_entrance('Turtle Rock (Dark Room) (South)'), lambda state: state.has('Cane of Somaria')) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) - set_rule(world.get_entrance('Turtle Rock (Trinexx)'), lambda state: state.has_key('Small Key (Turtle Rock)', 4) and state.has('Big Key (Turtle Rock)') and state.has('Cane of Somaria')) - set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize')) + set_rule(world.get_entrance('Turtle Rock Entrance Gap', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('Turtle Rock Entrance Gap Reverse', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_location('Turtle Rock - Compass Chest', player), lambda state: state.has('Cane of Somaria', player)) # We could get here from the middle section without Cane as we don't cross the entrance gap! + set_rule(world.get_location('Turtle Rock - Roller Room - Left', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player)) + set_rule(world.get_location('Turtle Rock - Roller Room - Right', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player)) + set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player) and (state.has('Cane of Somaria', player) or state.has('Hookshot', player))) + set_rule(world.get_entrance('Turtle Rock (Big Chest) (North)', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player)) + set_rule(world.get_entrance('Turtle Rock Big Key Door', player), lambda state: state.has('Big Key (Turtle Rock)', player)) + set_rule(world.get_entrance('Turtle Rock (Dark Room) (North)', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('Turtle Rock (Dark Room) (South)', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_entrance('Turtle Rock (Trinexx)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 4) and state.has('Big Key (Turtle Rock)', player) and state.has('Cane of Somaria', player)) + set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player)) - set_rule(world.get_entrance('Palace of Darkness Bonk Wall'), lambda state: state.can_shoot_arrows()) - set_rule(world.get_entrance('Palace of Darkness Hammer Peg Drop'), lambda state: state.has('Hammer')) - set_rule(world.get_entrance('Palace of Darkness Bridge Room'), lambda state: state.has_key('Small Key (Palace of Darkness)', 1)) # If we can reach any other small key door, we already have back door access to this area - set_rule(world.get_entrance('Palace of Darkness Big Key Door'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6) and state.has('Big Key (Palace of Darkness)') and state.can_shoot_arrows() and state.has('Hammer')) - set_rule(world.get_entrance('Palace of Darkness (North)'), lambda state: state.has_key('Small Key (Palace of Darkness)', 4)) - set_rule(world.get_location('Palace of Darkness - Big Chest'), lambda state: state.has('Big Key (Palace of Darkness)')) + set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: state.can_shoot_arrows(player)) + set_rule(world.get_entrance('Palace of Darkness Hammer Peg Drop', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Palace of Darkness Bridge Room', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 1)) # If we can reach any other small key door, we already have back door access to this area + set_rule(world.get_entrance('Palace of Darkness Big Key Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) and state.has('Big Key (Palace of Darkness)', player) and state.can_shoot_arrows(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Palace of Darkness (North)', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 4)) + set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player)) - set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6) or (item_name(state, 'Palace of Darkness - Big Key Chest') in ['Small Key (Palace of Darkness)'] and state.has_key('Small Key (Palace of Darkness)', 3))) - set_always_allow(world.get_location('Palace of Darkness - Big Key Chest'), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and state.has_key('Small Key (Palace of Darkness)', 5)) + set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 3))) + set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) - set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6) or (item_name(state, 'Palace of Darkness - Harmless Hellway') in ['Small Key (Palace of Darkness)'] and state.has_key('Small Key (Palace of Darkness)', 4))) - set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway'), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and state.has_key('Small Key (Palace of Darkness)', 5)) - set_rule(world.get_entrance('Palace of Darkness Maze Door'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6)) - set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Boss')) - set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize')) + set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Harmless Hellway', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 4))) + set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + set_rule(world.get_entrance('Palace of Darkness Maze Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6)) + set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize', player)) # these key rules are conservative, you might be able to get away with more lenient rules randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'] compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'] - set_rule(world.get_location('Ganons Tower - Bob\'s Torch'), lambda state: state.has_Boots()) - set_rule(world.get_entrance('Ganons Tower (Tile Room)'), lambda state: state.has('Cane of Somaria')) - set_rule(world.get_entrance('Ganons Tower (Hookshot Room)'), lambda state: state.has('Hammer')) + set_rule(world.get_location('Ganons Tower - Bob\'s Torch', player), lambda state: state.has_Boots(player)) + set_rule(world.get_entrance('Ganons Tower (Tile Room)', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has_key('Small Key (Ganons Tower)', 4) or (item_name(state, 'Ganons Tower - Map Chest') in ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)'] and state.has_key('Small Key (Ganons Tower)', 3))) - set_always_allow(world.get_location('Ganons Tower - Map Chest'), lambda state, item: item.name == 'Small Key (Ganons Tower)' and state.has_key('Small Key (Ganons Tower)', 3)) + set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player), ('Small Key (Ganons Tower)', player)] and state.has_key('Small Key (Ganons Tower)', player, 3))) + set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state.has_key('Small Key (Ganons Tower)', player, 3)) # It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere. We reflect this in the chest requirements. # However we need to leave these at the lower values to derive that with 3 keys it is always possible to reach Bob and Ice Armos. - set_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has_key('Small Key (Ganons Tower)', 2)) + set_rule(world.get_entrance('Ganons Tower (Double Switch Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 2)) # It is possible to need more than 3 keys .... - set_rule(world.get_entrance('Ganons Tower (Firesnake Room)'), lambda state: state.has_key('Small Key (Ganons Tower)', 3)) + set_rule(world.get_entrance('Ganons Tower (Firesnake Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3)) #The actual requirements for these rooms to avoid key-lock - set_rule(world.get_location('Ganons Tower - Firesnake Room'), lambda state: state.has_key('Small Key (Ganons Tower)', 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has_key('Small Key (Ganons Tower)', 2))) + set_rule(world.get_location('Ganons Tower - Firesnake Room', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(randomizer_room_chests, [player] * len(randomizer_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 2))) for location in randomizer_room_chests: - set_rule(world.get_location(location), lambda state: state.has_key('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has_key('Small Key (Ganons Tower)', 3))) + set_rule(world.get_location(location, player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(randomizer_room_chests, [player] * len(randomizer_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 3))) # Once again it is possible to need more than 3 keys... - set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door'), lambda state: state.has_key('Small Key (Ganons Tower)', 3) and state.has('Fire Rod')) + set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3) and state.has('Fire Rod', player)) # Actual requirements for location in compass_room_chests: - set_rule(world.get_location(location), lambda state: state.has('Fire Rod') and (state.has_key('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', compass_room_chests) and state.has_key('Small Key (Ganons Tower)', 3)))) + set_rule(world.get_location(location, player), lambda state: state.has('Fire Rod', player) and (state.has_key('Small Key (Ganons Tower)', player, 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(compass_room_chests, [player] * len(compass_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 3)))) - set_rule(world.get_location('Ganons Tower - Big Chest'), lambda state: state.has('Big Key (Ganons Tower)')) + set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player)) - set_rule(world.get_location('Ganons Tower - Big Key Room - Left'), lambda state: world.get_location('Ganons Tower - Big Key Room - Left').parent_region.dungeon.bosses['bottom'].can_defeat(state)) - set_rule(world.get_location('Ganons Tower - Big Key Chest'), lambda state: world.get_location('Ganons Tower - Big Key Chest').parent_region.dungeon.bosses['bottom'].can_defeat(state)) - set_rule(world.get_location('Ganons Tower - Big Key Room - Right'), lambda state: world.get_location('Ganons Tower - Big Key Room - Right').parent_region.dungeon.bosses['bottom'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Big Key Room - Left', player), lambda state: world.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Big Key Chest', player), lambda state: world.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Big Key Room - Right', player), lambda state: world.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) - set_rule(world.get_entrance('Ganons Tower Big Key Door'), lambda state: state.has('Big Key (Ganons Tower)') and state.can_shoot_arrows()) - set_rule(world.get_entrance('Ganons Tower Torch Rooms'), lambda state: state.has_fire_source() and world.get_entrance('Ganons Tower Torch Rooms').parent_region.dungeon.bosses['middle'].can_defeat(state)) - set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest'), lambda state: state.has_key('Small Key (Ganons Tower)', 3)) - set_rule(world.get_entrance('Ganons Tower Moldorm Door'), lambda state: state.has_key('Small Key (Ganons Tower)', 4)) - set_rule(world.get_entrance('Ganons Tower Moldorm Gap'), lambda state: state.has('Hookshot') and world.get_entrance('Ganons Tower Moldorm Gap').parent_region.dungeon.bosses['top'].can_defeat(state)) - set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2')) - set_rule(world.get_entrance('Pyramid Hole'), lambda state: state.has('Beat Agahnim 2')) + set_rule(world.get_entrance('Ganons Tower Big Key Door', player), lambda state: state.has('Big Key (Ganons Tower)', player) and state.can_shoot_arrows(player)) + set_rule(world.get_entrance('Ganons Tower Torch Rooms', player), lambda state: state.has_fire_source(player) and world.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3)) + set_rule(world.get_entrance('Ganons Tower Moldorm Door', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4)) + set_rule(world.get_entrance('Ganons Tower Moldorm Gap', player), lambda state: state.has('Hookshot', player) and world.get_entrance('Ganons Tower Moldorm Gap', player).parent_region.dungeon.bosses['top'].can_defeat(state)) + set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player)) + set_rule(world.get_entrance('Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player)) for location in ['Ganons Tower - Big Chest', 'Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right', 'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Validation Chest']: - forbid_item(world.get_location(location), 'Big Key (Ganons Tower)') + forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player) - set_rule(world.get_location('Ganon'), lambda state: state.has_beam_sword() and state.has_fire_source() and state.has('Crystal 1') and state.has('Crystal 2') - and state.has('Crystal 3') and state.has('Crystal 4') and state.has('Crystal 5') and state.has('Crystal 6') and state.has('Crystal 7') - and (state.has('Tempered Sword') or state.has('Golden Sword') or (state.has('Silver Arrows') and state.can_shoot_arrows()) or state.has('Lamp') or state.can_extend_magic(12))) # need to light torch a sufficient amount of times - set_rule(world.get_entrance('Ganon Drop'), lambda state: state.has_beam_sword()) # need to damage ganon to get tiles to drop + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has('Crystal 1', player) and state.has('Crystal 2', player) + and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player) + and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times + set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop - set_rule(world.get_entrance('Ganons Tower'), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic. + set_rule(world.get_entrance('Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic. - set_trock_key_rules(world) + set_trock_key_rules(world, player) - set_rule(world.get_entrance('Ganons Tower'), lambda state: state.has('Crystal 1') and state.has('Crystal 2') and state.has('Crystal 3') and state.has('Crystal 4') and state.has('Crystal 5') and state.has('Crystal 6') and state.has('Crystal 7')) + set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has('Crystal 1', player) and state.has('Crystal 2', player) and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)) -def no_glitches_rules(world): - set_rule(world.get_entrance('Zoras River'), lambda state: state.has('Flippers') or state.can_lift_rocks()) - set_rule(world.get_entrance('Lake Hylia Central Island Pier'), lambda state: state.has('Flippers')) # can be fake flippered to - set_rule(world.get_entrance('Hobo Bridge'), lambda state: state.has('Flippers')) - set_rule(world.get_entrance('Dark Lake Hylia Drop (East)'), lambda state: state.has_Pearl() and state.has('Flippers')) - set_rule(world.get_entrance('Dark Lake Hylia Teleporter'), lambda state: state.has_Pearl() and state.has('Flippers') and (state.has('Hammer') or state.can_lift_rocks())) - set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop'), lambda state: state.has_Pearl() and state.has('Flippers')) - add_rule(world.get_entrance('Ganons Tower (Hookshot Room)'), lambda state: state.has('Hookshot') or state.has_Boots()) - add_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has('Hookshot')) +def no_glitches_rules(world, player): + set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player)) + set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Flippers', player)) # can be fake flippered to + set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) + set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) + add_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player)) + add_rule(world.get_entrance('Ganons Tower (Double Switch Room)', player), lambda state: state.has('Hookshot', player)) DMs_room_chests = ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right', 'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'] for location in DMs_room_chests: - add_rule(world.get_location(location), lambda state: state.has('Hookshot')) - set_rule(world.get_entrance('Paradox Cave Push Block Reverse'), lambda state: False) # no glitches does not require block override - set_rule(world.get_entrance('Paradox Cave Bomb Jump'), lambda state: False) - set_rule(world.get_entrance('Skull Woods First Section Bomb Jump'), lambda state: False) + add_rule(world.get_location(location, player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: False) # no glitches does not require block override + set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False) + set_rule(world.get_entrance('Skull Woods First Section Bomb Jump', player), lambda state: False) # Light cones in standard depend on which world we actually are in, not which one the location would normally be # We add Lamp requirements only to those locations which lie in the dark world (or everything if open @@ -447,11 +452,11 @@ def no_glitches_rules(world): def add_conditional_lamp(spot, region, spottype='Location'): if spottype == 'Location': - spot = world.get_location(spot) + spot = world.get_location(spot, player) else: - spot = world.get_entrance(spot) - if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region))) or (not world.light_world_light_cone and not check_is_dark_world(world.get_region(region))): - add_lamp_requirement(spot) + spot = world.get_entrance(spot, player) + if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))): + add_lamp_requirement(spot, player) add_conditional_lamp('Misery Mire (Vitreous)', 'Misery Mire (Entrance)', 'Entrance') add_conditional_lamp('Turtle Rock (Dark Room) (North)', 'Turtle Rock (Entrance)', 'Entrance') @@ -473,79 +478,79 @@ def no_glitches_rules(world): add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location') if not world.sewer_light_cone: - add_lamp_requirement(world.get_location('Sewers - Dark Cross')) - add_lamp_requirement(world.get_entrance('Sewers Back Door')) - add_lamp_requirement(world.get_entrance('Throne Room')) + add_lamp_requirement(world.get_location('Sewers - Dark Cross', player), player) + add_lamp_requirement(world.get_entrance('Sewers Back Door', player), player) + add_lamp_requirement(world.get_entrance('Throne Room', player), player) -def open_rules(world): +def open_rules(world, player): # softlock protection as you can reach the sewers small key door with a guard drop key - forbid_item(world.get_location('Hyrule Castle - Boomerang Chest'), 'Small Key (Escape)') - forbid_item(world.get_location('Hyrule Castle - Zelda\'s Chest'), 'Small Key (Escape)') + forbid_item(world.get_location('Hyrule Castle - Boomerang Chest', player), 'Small Key (Escape)', player) + forbid_item(world.get_location('Hyrule Castle - Zelda\'s Chest', player), 'Small Key (Escape)', player) - set_rule(world.get_location('Hyrule Castle - Boomerang Chest'), lambda state: state.has_key('Small Key (Escape)')) - set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest'), lambda state: state.has_key('Small Key (Escape)')) + set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), lambda state: state.has_key('Small Key (Escape)', player)) + set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player), lambda state: state.has_key('Small Key (Escape)', player)) -def swordless_rules(world): +def swordless_rules(world, player): # for the time being swordless mode just inherits all fixes from open mode. # should there ever be fixes that apply to open mode but not swordless, this # can be revisited. - open_rules(world) + open_rules(world, player) - set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has('Hammer') or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle - set_rule(world.get_entrance('Agahnim 1'), lambda state: (state.has('Hammer') or state.has('Fire Rod') or state.can_shoot_arrows() or state.has('Cane of Somaria')) and state.has_key('Small Key (Agahnims Tower)', 2)) - set_rule(world.get_location('Ether Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer')) - set_rule(world.get_location('Bombos Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer') and state.has_Mirror()) - set_rule(world.get_entrance('Misery Mire'), lambda state: state.has_Pearl() and state.has_misery_mire_medallion()) # sword not required to use medallion for opening in swordless (!) - set_rule(world.get_entrance('Turtle Rock'), lambda state: state.has_Pearl() and state.has_turtle_rock_medallion() and state.can_reach('Turtle Rock (Top)', 'Region')) # sword not required to use medallion for opening in swordless (!) - set_rule(world.get_entrance('Skull Woods Torch Room'), lambda state: state.has_key('Small Key (Skull Woods)', 3) and state.has('Fire Rod')) # no curtain - set_rule(world.get_entrance('Ice Palace Entrance Room'), lambda state: state.has('Fire Rod') or state.has('Bombos')) #in swordless mode bombos pads are present in the relevant parts of ice palace - set_rule(world.get_location('Ganon'), lambda state: state.has('Hammer') and state.has_fire_source() and state.has('Silver Arrows') and state.can_shoot_arrows() and state.has('Crystal 1') and state.has('Crystal 2') - and state.has('Crystal 3') and state.has('Crystal 4') and state.has('Crystal 5') and state.has('Crystal 6') and state.has('Crystal 7')) - set_rule(world.get_entrance('Ganon Drop'), lambda state: state.has('Hammer')) # need to damage ganon to get tiles to drop + set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle + set_rule(world.get_entrance('Agahnim 1', player), lambda state: (state.has('Hammer', player) or state.has('Fire Rod', player) or state.can_shoot_arrows(player) or state.has('Cane of Somaria', player)) and state.has_key('Small Key (Agahnims Tower)', player, 2)) + set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) + set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player) and state.has_Mirror(player)) + set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain + set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) #in swordless mode bombos pads are present in the relevant parts of ice palace + set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has('Crystal 1', player) and state.has('Crystal 2', player) + and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)) + set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop -def standard_rules(world): - for loc in ['Sanctuary','Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', +def standard_rules(world, player): + for loc in ['Sanctuary', 'Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', 'Sewers - Secret Room - Right']: - add_rule(world.get_location(loc), lambda state: state.can_kill_most_things() and state.has_key('Small Key (Escape)')) + add_rule(world.get_location(loc, player), lambda state: state.can_kill_most_things(player) and state.has_key('Small Key (Escape)', player)) # easiest way to enforce key placement not relevant for open - set_rule(world.get_location('Sewers - Dark Cross'), lambda state: state.can_kill_most_things()) + set_rule(world.get_location('Sewers - Dark Cross', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_location('Hyrule Castle - Boomerang Chest'), lambda state: state.can_kill_most_things()) - set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest'), lambda state: state.can_kill_most_things()) + set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), lambda state: state.can_kill_most_things(player)) + set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player), lambda state: state.can_kill_most_things(player)) -def set_trock_key_rules(world): +def set_trock_key_rules(world, player): all_state = world.get_all_state(True) # First set all relevant locked doors to impassible. for entrance in ['Turtle Rock Dark Room Staircase', 'Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)', 'Turtle Rock Pokey Room']: - set_rule(world.get_entrance(entrance), lambda state: False) + set_rule(world.get_entrance(entrance, player), lambda state: False) # Check if each of the four main regions of the dungoen can be reached. The previous code section prevents key-costing moves within the dungeon. - can_reach_back = all_state.can_reach(world.get_region('Turtle Rock (Eye Bridge)')) if world.can_access_trock_eyebridge is None else world.can_access_trock_eyebridge + can_reach_back = all_state.can_reach(world.get_region('Turtle Rock (Eye Bridge)', player)) if world.can_access_trock_eyebridge is None else world.can_access_trock_eyebridge world.can_access_trock_eyebridge = can_reach_back - can_reach_front = all_state.can_reach(world.get_region('Turtle Rock (Entrance)')) if world.can_access_trock_front is None else world.can_access_trock_front + can_reach_front = all_state.can_reach(world.get_region('Turtle Rock (Entrance)', player)) if world.can_access_trock_front is None else world.can_access_trock_front world.can_access_trock_front = can_reach_front - can_reach_big_chest = all_state.can_reach(world.get_region('Turtle Rock (Big Chest)')) if world.can_access_trock_big_chest is None else world.can_access_trock_big_chest + can_reach_big_chest = all_state.can_reach(world.get_region('Turtle Rock (Big Chest)', player)) if world.can_access_trock_big_chest is None else world.can_access_trock_big_chest world.can_access_trock_big_chest = can_reach_big_chest - can_reach_middle = all_state.can_reach(world.get_region('Turtle Rock (Second Section)')) if world.can_access_trock_middle is None else world.can_access_trock_middle + can_reach_middle = all_state.can_reach(world.get_region('Turtle Rock (Second Section)', player)) if world.can_access_trock_middle is None else world.can_access_trock_middle world.can_access_trock_middle = can_reach_middle # No matter what, the key requirement for going from the middle to the bottom should be three keys. - set_rule(world.get_entrance('Turtle Rock Dark Room Staircase'), lambda state: state.has_key('Small Key (Turtle Rock)', 3)) + set_rule(world.get_entrance('Turtle Rock Dark Room Staircase', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 3)) # The following represent the most common and most restrictive key rules. These are overwritten later as needed. - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)'), lambda state: state.has_key('Small Key (Turtle Rock)', 4)) - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)'), lambda state: state.has_key('Small Key (Turtle Rock)', 4)) - set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has_key('Small Key (Turtle Rock)', 4)) + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 4)) + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 4)) + set_rule(world.get_entrance('Turtle Rock Pokey Room', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 4)) # No matter what, the Big Key cannot be in the Big Chest or held by Trinexx. non_big_key_locations = ['Turtle Rock - Big Chest', 'Turtle Rock - Boss'] @@ -553,45 +558,45 @@ def set_trock_key_rules(world): def tr_big_key_chest_keys_needed(state): # This function handles the key requirements for the TR Big Chest in the situations it having the Big Key should logically require 2 keys, small key # should logically require no keys, and anything else should logically require 4 keys. - item = item_name(state, 'Turtle Rock - Big Key Chest') - if item in ['Small Key (Turtle Rock)']: + item = item_name(state, 'Turtle Rock - Big Key Chest', player) + if item in [('Small Key (Turtle Rock)', player)]: return 0 - if item in ['Big Key (Turtle Rock)']: + if item in [('Big Key (Turtle Rock)', player)]: return 2 return 4 # Now we need to set rules based on which entrances we have access to. The most important point is whether we have back access. If we have back access, we # might open all the locked doors in any order so we need maximally restrictive rules. if can_reach_back: - set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: (state.has_key('Small Key (Turtle Rock)', 4) or item_name(state, 'Turtle Rock - Big Key Chest') == 'Small Key (Turtle Rock)')) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)') + set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state.has_key('Small Key (Turtle Rock)', player, 4) or item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player))) + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) elif can_reach_front and can_reach_middle: - set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has_key('Small Key (Turtle Rock)', tr_big_key_chest_keys_needed(state))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)') + set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, tr_big_key_chest_keys_needed(state))) + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] elif can_reach_front: - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)'), lambda state: state.has_key('Small Key (Turtle Rock)', 2)) - set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has_key('Small Key (Turtle Rock)', 1)) - set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has_key('Small Key (Turtle Rock)', tr_big_key_chest_keys_needed(state))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)' and state.has_key('Small Key (Turtle Rock)', 2)) + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 2)) + set_rule(world.get_entrance('Turtle Rock Pokey Room', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 1)) + set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, tr_big_key_chest_keys_needed(state))) + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player and state.has_key('Small Key (Turtle Rock)', player, 2)) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] elif can_reach_big_chest: - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)'), lambda state: state.has_key('Small Key (Turtle Rock)', 2) if item_in_locations(state, 'Big Key (Turtle Rock)', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left', 'Turtle Rock - Roller Room - Right']) else state.has_key('Small Key (Turtle Rock)', 4)) - set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: (state.has_key('Small Key (Turtle Rock)', 4) or item_name(state, 'Turtle Rock - Big Key Chest') == 'Small Key (Turtle Rock)')) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)') + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 2) if item_in_locations(state, 'Big Key (Turtle Rock)', player, [('Turtle Rock - Compass Chest', player), ('Turtle Rock - Roller Room - Left', player), ('Turtle Rock - Roller Room - Right', player)]) else state.has_key('Small Key (Turtle Rock)', player, 4)) + set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state.has_key('Small Key (Turtle Rock)', player, 4) or item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player))) + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] if not world.keysanity: non_big_key_locations += ['Turtle Rock - Big Key Chest'] else: - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)'), lambda state: state.has_key('Small Key (Turtle Rock)', 2) if item_in_locations(state, 'Big Key (Turtle Rock)', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left', 'Turtle Rock - Roller Room - Right']) else state.has_key('Small Key (Turtle Rock)', 4)) - set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: (state.has_key('Small Key (Turtle Rock)', 4) or item_name(state, 'Turtle Rock - Big Key Chest') == 'Small Key (Turtle Rock)')) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)') + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 2) if item_in_locations(state, 'Big Key (Turtle Rock)', player, [('Turtle Rock - Compass Chest', player), ('Turtle Rock - Roller Room - Left', player), ('Turtle Rock - Roller Room - Right', player)]) else state.has_key('Small Key (Turtle Rock)', player, 4)) + set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state.has_key('Small Key (Turtle Rock)', player, 4) or item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player))) + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] @@ -600,16 +605,16 @@ def set_trock_key_rules(world): # set big key restrictions for location in non_big_key_locations: - forbid_item(world.get_location(location), 'Big Key (Turtle Rock)') + forbid_item(world.get_location(location, player), 'Big Key (Turtle Rock)', player) # small key restriction for location in ['Turtle Rock - Boss']: - forbid_item(world.get_location(location), 'Small Key (Turtle Rock)') + forbid_item(world.get_location(location, player), 'Small Key (Turtle Rock)', player) -def set_big_bomb_rules(world): +def set_big_bomb_rules(world, player): # this is a mess - bombshop_entrance = world.get_region('Big Bomb Shop').entrances[0] + bombshop_entrance = world.get_region('Big Bomb Shop', player).entrances[0] Normal_LW_entrances = ['Blinds Hideout', 'Bonk Fairy (Light)', 'Lake Hylia Fairy', @@ -723,23 +728,23 @@ def set_big_bomb_rules(world): Desert_mirrorable_ledge_entrances = ['Desert Palace Entrance (West)', 'Desert Palace Entrance (North)', 'Desert Palace Entrance (South)', - 'Checkerboard Cave',] + 'Checkerboard Cave'] - set_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.can_reach('East Dark World', 'Region') and state.can_reach('Big Bomb Shop', 'Region') and state.has('Crystal 5') and state.has('Crystal 6')) + set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) #crossing peg bridge starting from the southern dark world def cross_peg_bridge(state): - return state.has('Hammer') and state.has_Pearl() + return state.has('Hammer', player) and state.has_Pearl(player) # returning via the eastern and southern teleporters needs the same items, so we use the southern teleporter for out routing. # crossing preg bridge already requires hammer so we just add the gloves to the requirement def southern_teleporter(state): - return state.can_lift_rocks() and cross_peg_bridge(state) + return state.can_lift_rocks(player) and cross_peg_bridge(state) # the basic routes assume you can reach eastern light world with the bomb. # you can then use the southern teleporter, or (if you have beaten Aga1) the hyrule castle gate warp def basic_routes(state): - return southern_teleporter(state) or state.can_reach('Top of Pyramid', 'Entrance') + return southern_teleporter(state) or state.can_reach('Top of Pyramid', 'Entrance', player) # Key for below abbreviations: # P = pearl @@ -752,90 +757,90 @@ def set_big_bomb_rules(world): #1. basic routes #2. Can reach Eastern dark world some other way, mirror, get bomb, return to mirror spot, walk to pyramid: Needs mirror # -> M or BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: basic_routes(state) or state.has_Mirror()) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: basic_routes(state) or state.has_Mirror(player)) elif bombshop_entrance.name in LW_walkable_entrances: #1. Mirror then basic routes # -> M and BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and basic_routes(state)) elif bombshop_entrance.name in Northern_DW_entrances: #1. Mirror and basic routes #2. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl # -> (Mitts and CPB) or (M and BR) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.can_lift_heavy_rocks() and cross_peg_bridge(state)) or (state.has_Mirror() and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.has_Mirror(player) and basic_routes(state))) elif bombshop_entrance.name == 'Bumper Cave (Bottom)': #1. Mirror and Lift rock and basic_routes #2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case) #3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl # -> (Mitts and CPB) or (((G or Flute) and M) and BR)) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.can_lift_heavy_rocks() and cross_peg_bridge(state)) or (((state.can_lift_rocks() or state.has('Ocarina')) and state.has_Mirror()) and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (((state.can_lift_rocks(player) or state.has('Ocarina', player)) and state.has_Mirror(player)) and basic_routes(state))) elif bombshop_entrance.name in Southern_DW_entrances: #1. Mirror and enter via gate: Need mirror and Aga1 #2. cross peg bridge: Need hammer and moon pearl # -> CPB or (M and A) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: cross_peg_bridge(state) or (state.has_Mirror() and state.can_reach('Top of Pyramid', 'Entrance'))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or (state.has_Mirror(player) and state.can_reach('Top of Pyramid', 'Entrance', player))) elif bombshop_entrance.name in Isolated_DW_entrances: # 1. mirror then flute then basic routes # -> M and Flute and BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() and state.has('Ocarina') and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and state.has('Ocarina', player) and basic_routes(state)) elif bombshop_entrance.name in Isolated_LW_entrances: # 1. flute then basic routes # Prexisting mirror spot is not permitted, because mirror might have been needed to reach these isolated locations. # -> Flute and BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and basic_routes(state)) elif bombshop_entrance.name in West_LW_DM_entrances: # 1. flute then basic routes or mirror # Prexisting mirror spot is permitted, because flute can be used to reach west DM directly. # -> Flute and (M or BR) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and (state.has_Mirror() or basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and (state.has_Mirror(player) or basic_routes(state))) elif bombshop_entrance.name in East_LW_DM_entrances: # 1. flute then basic routes or mirror and hookshot # Prexisting mirror spot is permitted, because flute can be used to reach west DM directly and then east DM via Hookshot # -> Flute and ((M and Hookshot) or BR) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and ((state.has_Mirror() and state.has('Hookshot')) or basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and ((state.has_Mirror(player) and state.has('Hookshot', player)) or basic_routes(state))) elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)': # Same as East_LW_DM_entrances except navigation without BR requires Mitts # -> Flute and ((M and Hookshot and Mitts) or BR) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has('Ocarina') and ((state.has_Mirror() and state.has('Hookshot') and state.can_lift_heavy_rocks()) or basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and ((state.has_Mirror(player) and state.has('Hookshot', player) and state.can_lift_heavy_rocks(player)) or basic_routes(state))) elif bombshop_entrance.name in Castle_ledge_entrances: # 1. mirror on pyramid to castle ledge, grab bomb, return through mirror spot: Needs mirror # 2. flute then basic routes # -> M or (Flute and BR) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: state.has_Mirror() or (state.has('Ocarina') and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) or (state.has('Ocarina', player) and basic_routes(state))) elif bombshop_entrance.name in Desert_mirrorable_ledge_entrances: # Cases when you have mire access: Mirror to reach locations, return via mirror spot, move to center of desert, mirror anagin and: # 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes # 2. flute then basic routes # -> (Mire access and M) or Flute) and BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: ((state.can_reach('Dark Desert', 'Region') and state.has_Mirror()) or state.has('Ocarina')) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: ((state.can_reach('Dark Desert', 'Region', player) and state.has_Mirror(player)) or state.has('Ocarina', player)) and basic_routes(state)) elif bombshop_entrance.name == 'Old Man Cave (West)': # 1. Lift rock then basic_routes # 2. flute then basic_routes # -> (Flute or G) and BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has('Ocarina') or state.can_lift_rocks()) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Ocarina', player) or state.can_lift_rocks(player)) and basic_routes(state)) elif bombshop_entrance.name == 'Graveyard Cave': # 1. flute then basic routes # 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge # -> (Flute or (M and P and West Dark World access) and BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has('Ocarina') or (state.can_reach('West Dark World', 'Region') and state.has_Pearl() and state.has_Mirror())) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Ocarina', player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)) elif bombshop_entrance.name in Mirror_from_SDW_entrances: # 1. flute then basic routes # 2. (has South dark world access) use existing mirror spot, mirror again off ledge # -> (Flute or (M and South Dark World access) and BR - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has('Ocarina') or (state.can_reach('South Dark World', 'Region') and state.has_Mirror())) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Ocarina', player) or (state.can_reach('South Dark World', 'Region', player) and state.has_Mirror(player))) and basic_routes(state)) elif bombshop_entrance.name == 'Dark World Potion Shop': # 1. walk down by lifting rock: needs gloves and pearl` # 2. walk down by hammering peg: needs hammer and pearl # 3. mirror and basic routes # -> (P and (H or Gloves)) or (M and BR) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.has_Pearl() and (state.has('Hammer') or state.can_lift_rocks())) or (state.has_Mirror() and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has_Pearl(player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) or (state.has_Mirror(player) and basic_routes(state))) elif bombshop_entrance.name == 'Kings Grave': # same as the Normal_LW_entrances case except that the pre-existing mirror is only possible if you have mitts # (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot) # to account for insanity, must consider a way to escape without a cave for basic_routes # -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR) - add_rule(world.get_entrance('Pyramid Fairy'), lambda state: (state.can_lift_heavy_rocks() and state.has_Mirror()) or ((state.can_lift_heavy_rocks() or state.has('Ocarina') or (state.can_reach('West Dark World', 'Region') and state.has_Pearl() and state.has_Mirror())) and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and state.has_Mirror(player)) or ((state.can_lift_heavy_rocks(player) or state.has('Ocarina', player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state))) -def set_bunny_rules(world): +def set_bunny_rules(world, player): # regions for the exits of multi-entrace caves/drops that bunny cannot pass # Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing. @@ -853,12 +858,12 @@ def set_bunny_rules(world): def get_rule_to_add(region): if not region.is_light_world: - return lambda state: state.has_Pearl() + return lambda state: state.has_Pearl(player) # in this case we are mixed region. # we collect possible options. # The base option is having the moon pearl - possible_options = [lambda state: state.has_Pearl()] + possible_options = [lambda state: state.has_Pearl(player)] # We will search entrances recursively until we find # one that leads to an exclusively light world region @@ -885,7 +890,7 @@ def set_bunny_rules(world): return options_to_access_rule(possible_options) # Add requirements for bunny-impassible caves if they occur in the dark world - for region in [world.get_region(name) for name in bunny_impassable_caves]: + for region in [world.get_region(name, player) for name in bunny_impassable_caves]: if not region.is_dark_world: continue @@ -893,13 +898,13 @@ def set_bunny_rules(world): for exit in region.exits: add_rule(exit, rule) - paradox_shop = world.get_region('Light World Death Mountain Shop') + paradox_shop = world.get_region('Light World Death Mountain Shop', player) if paradox_shop.is_dark_world: add_rule(paradox_shop.entrances[0], get_rule_to_add(paradox_shop)) # Add requirements for all locations that are actually in the dark world, except those available to the bunny for location in world.get_locations(): - if location.parent_region.is_dark_world: + if location.player == player and location.parent_region.is_dark_world: if location.name in bunny_accessible_locations: continue From 0b9e144f8e0155724c05ead9f25971dc8de9e3a7 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Tue, 9 Jul 2019 22:18:24 -0400 Subject: [PATCH 04/52] Minor fixes and clarifications for Bonta's Multiworld code --- BaseClasses.py | 15 +++++++-------- EntranceShuffle.py | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 9361236462..01f7168d0c 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -244,12 +244,11 @@ class World(object): return False - def has_beaten_game(self, state): - 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 False + def has_beaten_game(self, state, player=None): + if player: + return state.has('Triforce', player) or (self.goal in ['triforcehunt'] and (state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > self.treasure_hunt_count)) + else: + return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1))) def can_beat_game(self, starting_state=None): if starting_state: @@ -410,7 +409,7 @@ class CollectionState(object): return self.bottle_count(player) > 0 def bottle_count(self, player): - return len([pritem for pritem in self.prog_items if pritem[0].startswith('Bottle') and pritem[1] == player]) + return len([item for (item, itemplayer) in self.prog_items if item.startswith('Bottle') and itemplayer == player]) def has_hearts(self, player, count): # Warning: This only considers items that are marked as advancement items @@ -641,7 +640,7 @@ class Region(object): is_dungeon_item = item.key or item.map or item.compass sewer_hack = self.world.mode == 'standard' and item.name == 'Small Key (Escape)' if sewer_hack or (is_dungeon_item and not self.world.keysanity): - return self.dungeon and self.dungeon.is_dungeon_item(item) and (item.player == self.player or self.world.keysanity) + return self.dungeon and self.dungeon.is_dungeon_item(item) and item.player == self.player return True diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 0265dae842..da4602442a 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -28,7 +28,7 @@ def link_entrances(world, player): for exitname, regionname in default_connections: connect_simple(world, exitname, regionname, player) - simple_shuffle_dungeons(world) + simple_shuffle_dungeons(world, player) elif world.shuffle == 'dungeonsfull': for exitname, regionname in default_connections: connect_simple(world, exitname, regionname, player) From 16abf033c31753603bd79f9f13f8a4af75399452 Mon Sep 17 00:00:00 2001 From: Bonta-kun <40473493+Bonta0@users.noreply.github.com> Date: Thu, 30 May 2019 01:10:16 +0200 Subject: [PATCH 05/52] Enemizer support --- .gitignore | 1 + EntranceRandomizer.py | 11 +++- Gui.py | 70 ++++++++++++++++++-- Main.py | 28 ++++++-- Rom.py | 145 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 238 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 4ac9fd67e7..65c35fa8ae 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ README.html .vs/ *multidata *multisave +EnemizerCLI/ diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 55bf1d585b..da16e2a6e6 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -207,14 +207,19 @@ def start(): ''') parser.add_argument('--suppress_rom', help='Do not create an output rom file.', action='store_true') parser.add_argument('--gui', help='Launch the GUI', action='store_true') - # Deliberately not documented, only useful for vt site integration right now: - parser.add_argument('--shufflebosses', help=argparse.SUPPRESS, default='none', const='none', nargs='?', choices=['none', 'basic', 'normal', 'chaos']) parser.add_argument('--jsonout', action='store_true', help='''\ Output .json patch to stdout instead of a patched rom. Used 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('--enemizercli', default='') + parser.add_argument('--shufflebosses', default='none', choices=['none', 'basic', 'normal', 'chaos']) + parser.add_argument('--shuffleenemies', default=False, action='store_true') + parser.add_argument('--enemy_health', default='default', choices=['default', 'easy', 'normal', 'hard', 'expert']) + parser.add_argument('--enemy_damage', default='default', choices=['default', 'shuffled', 'chaos']) + parser.add_argument('--shufflepalette', default=False, action='store_true') + parser.add_argument('--shufflepots', default=False, action='store_true') + parser.add_argument('--multi', default=1, type=lambda value: min(max(int(value), 1), 255)) parser.add_argument('--outputpath') args = parser.parse_args() diff --git a/Gui.py b/Gui.py index 50f6075647..be544aa837 100755 --- a/Gui.py +++ b/Gui.py @@ -5,7 +5,7 @@ import json import random import os import shutil -from tkinter import Checkbutton, OptionMenu, Toplevel, LabelFrame, PhotoImage, Tk, LEFT, RIGHT, BOTTOM, TOP, StringVar, IntVar, Frame, Label, W, E, X, Entry, Spinbox, Button, filedialog, messagebox, ttk +from tkinter import Checkbutton, OptionMenu, Toplevel, LabelFrame, PhotoImage, Tk, LEFT, RIGHT, BOTTOM, TOP, StringVar, IntVar, Frame, Label, W, E, X, BOTH, Entry, Spinbox, Button, filedialog, messagebox, ttk from urllib.parse import urlparse from urllib.request import urlopen @@ -242,12 +242,67 @@ def guiMain(args=None): heartcolorFrame.pack(expand=True, anchor=E) fastMenuFrame.pack(expand=True, anchor=E) - bottomFrame = Frame(randomizerWindow) + enemizerFrame = LabelFrame(randomizerWindow, text="Enemizer", padx=5, pady=5) + enemizerFrame.columnconfigure(0, weight=1) + enemizerFrame.columnconfigure(1, weight=1) + enemizerFrame.columnconfigure(2, weight=1) + + enemizerPathFrame = Frame(enemizerFrame) + enemizerPathFrame.grid(row=0, column=0, columnspan=3, sticky=W) + enemizerCLIlabel = Label(enemizerPathFrame, text="EnemizerCLI path: ") + enemizerCLIlabel.pack(side=LEFT) + enemizerCLIpathVar = StringVar() + enemizerCLIpathEntry = Entry(enemizerPathFrame, textvariable=enemizerCLIpathVar, width=80) + enemizerCLIpathEntry.pack(side=LEFT) + def EnemizerSelectPath(): + path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")]) + if path: + enemizerCLIpathVar.set(path) + enemizerCLIbrowseButton = Button(enemizerPathFrame, text='...', command=EnemizerSelectPath) + enemizerCLIbrowseButton.pack(side=LEFT) + + enemyShuffleVar = IntVar() + enemyShuffleButton = Checkbutton(enemizerFrame, text="Enemy shuffle", variable=enemyShuffleVar) + enemyShuffleButton.grid(row=1, column=0) + paletteShuffleVar = IntVar() + paletteShuffleButton = Checkbutton(enemizerFrame, text="Palette shuffle", variable=paletteShuffleVar) + paletteShuffleButton.grid(row=1, column=1) + potShuffleVar = IntVar() + potShuffleButton = Checkbutton(enemizerFrame, text="Pot shuffle", variable=potShuffleVar) + potShuffleButton.grid(row=1, column=2) + + enemizerBossFrame = Frame(enemizerFrame) + enemizerBossFrame.grid(row=2, column=0) + enemizerBossLabel = Label(enemizerBossFrame, text='Boss shuffle') + enemizerBossLabel.pack(side=LEFT) + enemizerBossVar = StringVar() + enemizerBossVar.set('none') + enemizerBossOption = OptionMenu(enemizerBossFrame, enemizerBossVar, 'none', 'basic', 'normal', 'chaos') + enemizerBossOption.pack(side=LEFT) + + enemizerDamageFrame = Frame(enemizerFrame) + enemizerDamageFrame.grid(row=2, column=1) + enemizerDamageLabel = Label(enemizerDamageFrame, text='Enemy damage') + enemizerDamageLabel.pack(side=LEFT) + enemizerDamageVar = StringVar() + enemizerDamageVar.set('default') + enemizerDamageOption = OptionMenu(enemizerDamageFrame, enemizerDamageVar, 'default', 'shuffled', 'chaos') + enemizerDamageOption.pack(side=LEFT) + + enemizerHealthFrame = Frame(enemizerFrame) + enemizerHealthFrame.grid(row=2, column=2) + enemizerHealthLabel = Label(enemizerHealthFrame, text='Enemy health') + enemizerHealthLabel.pack(side=LEFT) + enemizerHealthVar = StringVar() + enemizerHealthVar.set('default') + enemizerHealthOption = OptionMenu(enemizerHealthFrame, enemizerHealthVar, 'default', 'easy', 'normal', 'hard', 'expert') + enemizerHealthOption.pack(side=LEFT) + + bottomFrame = Frame(randomizerWindow, pady=5) worldLabel = Label(bottomFrame, text='Worlds') worldVar = StringVar() worldSpinbox = Spinbox(bottomFrame, from_=1, to=100, width=5, textvariable=worldVar) - seedLabel = Label(bottomFrame, text='Seed #') seedVar = StringVar() seedEntry = Entry(bottomFrame, width=15, textvariable=seedVar) @@ -281,6 +336,13 @@ def guiMain(args=None): guiargs.disablemusic = bool(disableMusicVar.get()) guiargs.shuffleganon = bool(shuffleGanonVar.get()) guiargs.hints = bool(hintsVar.get()) + guiargs.enemizercli = enemizerCLIpathVar.get() + guiargs.shufflebosses = enemizerBossVar.get() + guiargs.shuffleenemies = bool(enemyShuffleVar.get()) + guiargs.enemy_health = enemizerHealthVar.get() + guiargs.enemy_damage = enemizerDamageVar.get() + guiargs.shufflepalette = bool(paletteShuffleVar.get()) + guiargs.shufflepots = bool(potShuffleVar.get()) guiargs.custom = bool(customVar.get()) guiargs.customitemarray = [int(bowVar.get()), int(silverarrowVar.get()), int(boomerangVar.get()), int(magicboomerangVar.get()), int(hookshotVar.get()), int(mushroomVar.get()), int(magicpowderVar.get()), int(firerodVar.get()), int(icerodVar.get()), int(bombosVar.get()), int(etherVar.get()), int(quakeVar.get()), int(lampVar.get()), int(hammerVar.get()), int(shovelVar.get()), int(fluteVar.get()), int(bugnetVar.get()), @@ -291,7 +353,6 @@ def guiMain(args=None): int(arrow1Var.get()), int(arrow10Var.get()), int(bomb1Var.get()), int(bomb3Var.get()), int(rupee1Var.get()), int(rupee5Var.get()), int(rupee20Var.get()), int(rupee50Var.get()), int(rupee100Var.get()), int(rupee300Var.get()), int(rupoorVar.get()), int(blueclockVar.get()), int(greenclockVar.get()), int(redclockVar.get()), int(triforcepieceVar.get()), int(triforcecountVar.get()), int(triforceVar.get()), int(rupoorcostVar.get()), int(universalkeyVar.get())] - guiargs.shufflebosses = None guiargs.rom = romVar.get() guiargs.jsonout = None guiargs.sprite = sprite @@ -326,6 +387,7 @@ def guiMain(args=None): rightHalfFrame.pack(side=RIGHT) topFrame.pack(side=TOP) bottomFrame.pack(side=BOTTOM) + enemizerFrame.pack(side=BOTTOM, fill=BOTH) # Adjuster Controls diff --git a/Main.py b/Main.py index 413c1a0e45..fe06a64ddd 100644 --- a/Main.py +++ b/Main.py @@ -3,13 +3,14 @@ import copy from itertools import zip_longest import json import logging +import os import random import time from BaseClasses import World, CollectionState, Item, Region, Location, Shop from Regions import create_regions, mark_light_world_regions from EntranceShuffle import link_entrances -from Rom import patch_rom, Sprite, LocalRom, JsonRom +from Rom import patch_rom, get_enemizer_patch, apply_rom_settings, Sprite, LocalRom, JsonRom from Rules import set_rules from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression @@ -124,6 +125,8 @@ 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) + use_enemizer = args.enemizercli and (args.shufflebosses != 'none' or args.shuffleenemies or args.enemy_health != 'default' or args.enemy_health != 'default' or args.enemy_damage or args.shufflepalette or args.shufflepots) + jsonout = {} if not args.suppress_rom: if world.players > 1: @@ -131,17 +134,32 @@ def main(args, seed=None): else: player = 1 + local_rom = None if args.jsonout: rom = JsonRom() else: - rom = LocalRom(args.rom) - patch_rom(world, player, rom, bytearray(logic_hash), args.heartbeep, args.heartcolor, sprite, player_names) + if use_enemizer: + local_rom = LocalRom(args.rom) + rom = JsonRom() + else: + rom = LocalRom(args.rom) + + patch_rom(world, player, rom, bytearray(logic_hash)) + + enemizer_patch = [] + if use_enemizer: + enemizer_patch = get_enemizer_patch(world, player, rom, args.rom, args.enemizercli, args.shuffleenemies, args.enemy_health, args.enemy_damage, args.shufflepalette, args.shufflepots) if args.jsonout: jsonout['patch'] = rom.patches - + if use_enemizer: + jsonout['enemizer' % player] = enemizer_patch else: - apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite, player_names) + if use_enemizer: + local_rom.patch_enemizer(rom.patches, os.path.join(os.path.dirname(args.enemizercli), "enemizerBasePatch.json"), enemizer_patch) + rom = local_rom + + apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite) rom.write_to_file(output_path('%s.sfc' % outfilebase)) if args.create_spoiler and not args.jsonout: diff --git a/Rom.py b/Rom.py index c439f07d84..61105b5b10 100644 --- a/Rom.py +++ b/Rom.py @@ -4,6 +4,7 @@ import hashlib import logging import os import struct +import subprocess import random from BaseClasses import ShopType, Region, Location, Item @@ -11,7 +12,7 @@ from Dungeons import dungeon_music_addresses 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 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 output_path, local_path, int16_as_bytes, int32_as_bytes from Items import ItemFactory, item_table @@ -84,7 +85,7 @@ class LocalRom(object): logging.getLogger('').warning('Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway.') # extend to 2MB - self.buffer.extend(bytearray([0x00] * (2097152 - len(self.buffer)))) + self.buffer.extend(bytearray([0x00] * (0x200000 - len(self.buffer)))) # load randomizer patches with open(local_path('data/base2current.json'), 'r') as stream: @@ -100,6 +101,24 @@ class LocalRom(object): if RANDOMIZERBASEHASH != patchedmd5.hexdigest(): raise RuntimeError('Provided Base Rom unsuitable for patching. Please provide a JAP(1.0) "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc" rom to use as a base.') + def patch_enemizer(self, rando_patch, base_enemizer_patch_path, enemizer_patch): + # extend to 4MB + self.buffer.extend(bytearray([0x00] * (0x400000 - len(self.buffer)))) + + # apply randomizer patches + for address, values in rando_patch.items(): + self.write_bytes(int(address), values) + + # load base enemizer patches + with open(base_enemizer_patch_path, 'r') as f: + base_enemizer_patch = json.load(f) + for patch in base_enemizer_patch: + self.write_bytes(patch["address"], patch["patchData"]) + + # apply enemizer patches + for patch in enemizer_patch: + self.write_bytes(patch["address"], patch["patchData"]) + def write_crc(self): crc = (sum(self.buffer[:0x7FDC] + self.buffer[0x7FE0:]) + 0x01FE) & 0xFFFF inv = crc ^ 0xFFFF @@ -117,6 +136,124 @@ def read_rom(stream): buffer = buffer[0x200:] return buffer +def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shuffleenemies, enemy_health, enemy_damage, shufflepalette, shufflepots): + baserom_path = os.path.abspath(baserom_path) + basepatch_path = os.path.abspath(local_path('data/base2current.json')) + randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json')) + options_path = os.path.abspath(output_path('enemizer_options.json')) + enemizer_output_path = os.path.abspath(output_path('enemizer_output.json')) + + # write options file for enemizer + options = { + 'RandomizeEnemies': shuffleenemies, + 'RandomizeEnemiesType': 3, + 'RandomizeBushEnemyChance': True, + 'RandomizeEnemyHealthRange': enemy_health != 'default', + 'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[enemy_health], + 'OHKO': False, + 'RandomizeEnemyDamage': enemy_damage != 'default', + 'AllowEnemyZeroDamage': True, + 'ShuffleEnemyDamageGroups': enemy_damage != 'default', + 'EnemyDamageChaosMode': enemy_damage == 'chaos', + 'EasyModeEscape': False, + 'EnemiesAbsorbable': False, + 'AbsorbableSpawnRate': 10, + 'AbsorbableTypes': { + 'FullMagic': True, 'SmallMagic': True, 'Bomb_1': True, 'BlueRupee': True, 'Heart': True, 'BigKey': True, 'Key': True, + 'Fairy': True, 'Arrow_10': True, 'Arrow_5': True, 'Bomb_8': True, 'Bomb_4': True, 'GreenRupee': True, 'RedRupee': True + }, + 'BossMadness': False, + 'RandomizeBosses': True, + 'RandomizeBossesType': 0, + 'RandomizeBossHealth': False, + 'RandomizeBossHealthMinAmount': 0, + 'RandomizeBossHealthMaxAmount': 300, + 'RandomizeBossDamage': False, + 'RandomizeBossDamageMinAmount': 0, + 'RandomizeBossDamageMaxAmount': 200, + 'RandomizeBossBehavior': False, + 'RandomizeDungeonPalettes': shufflepalette, + 'SetBlackoutMode': False, + 'RandomizeOverworldPalettes': shufflepalette, + 'RandomizeSpritePalettes': shufflepalette, + 'SetAdvancedSpritePalettes': False, + 'PukeMode': False, + 'NegativeMode': False, + 'GrayscaleMode': False, + 'GenerateSpoilers': False, + 'RandomizeLinkSpritePalette': False, + 'RandomizePots': shufflepots, + 'ShuffleMusic': False, + 'BootlegMagic': True, + 'CustomBosses': False, + 'AndyMode': False, + 'HeartBeepSpeed': 0, + 'AlternateGfx': False, + 'ShieldGraphics': "shield_gfx/normal.gfx", + 'SwordGraphics': "sword_gfx/normal.gfx", + 'BeeMizer': False, + 'BeesLevel': 0, + 'RandomizeTileTrapPattern': True, + 'RandomizeTileTrapFloorTile': False, + 'AllowKillableThief': shuffleenemies, + 'RandomizeSpriteOnHit': False, + 'DebugMode': False, + 'DebugForceEnemy': False, + 'DebugForceEnemyId': 0, + 'DebugForceBoss': False, + 'DebugForceBossId': 0, + 'DebugOpenShutterDoors': False, + 'DebugForceEnemyDamageZero': False, + 'DebugShowRoomIdInRupeeCounter': False, + 'UseManualBosses': True, + 'ManualBosses': { + 'EasternPalace': world.get_dungeon("Eastern Palace", player).boss.enemizer_name, + 'DesertPalace': world.get_dungeon("Desert Palace", player).boss.enemizer_name, + 'TowerOfHera': world.get_dungeon("Tower of Hera", player).boss.enemizer_name, + 'AgahnimsTower': 'Agahnim', + 'PalaceOfDarkness': world.get_dungeon("Palace of Darkness", player).boss.enemizer_name, + 'SwampPalace': world.get_dungeon("Swamp Palace", player).boss.enemizer_name, + 'SkullWoods': world.get_dungeon("Skull Woods", player).boss.enemizer_name, + 'ThievesTown': world.get_dungeon("Thieves Town", player).boss.enemizer_name, + 'IcePalace': world.get_dungeon("Ice Palace", player).boss.enemizer_name, + 'MiseryMire': world.get_dungeon("Misery Mire", player).boss.enemizer_name, + 'TurtleRock': world.get_dungeon("Turtle Rock", player).boss.enemizer_name, + 'GanonsTower1': world.get_dungeon('Ganons Tower', player).bosses['bottom'].enemizer_name, + 'GanonsTower2': world.get_dungeon('Ganons Tower', player).bosses['middle'].enemizer_name, + 'GanonsTower3': world.get_dungeon('Ganons Tower', player).bosses['top'].enemizer_name, + 'GanonsTower4': 'Agahnim2', + 'Ganon': 'Ganon', + } + } + + rom.write_to_file(randopatch_path) + + with open(options_path, 'w') as f: + json.dump(options, f) + + subprocess.check_call([os.path.abspath(enemizercli), + '--rom', baserom_path, + '--seed', str(world.rom_seeds[player]), + '--base', basepatch_path, + '--randomizer', randopatch_path, + '--enemizer', options_path, + '--output', enemizer_output_path], + cwd=os.path.dirname(enemizercli), stdout=subprocess.DEVNULL) + + with open(enemizer_output_path, 'r') as f: + ret = json.load(f) + + if os.path.exists(randopatch_path): + os.remove(randopatch_path) + + if os.path.exists(options_path): + os.remove(options_path) + + if os.path.exists(enemizer_output_path): + os.remove(enemizer_output_path) + + return ret + class Sprite(object): default_palette = [255, 127, 126, 35, 183, 17, 158, 54, 165, 20, 255, 1, 120, 16, 157, 89, 71, 54, 104, 59, 74, 10, 239, 18, 92, 42, 113, 21, 24, 122, @@ -275,7 +412,7 @@ class Sprite(object): # split into palettes of 15 colors return array_chunk(palette_as_colors, 15) -def patch_rom(world, player, rom, hashtable, beep='normal', color='red', sprite=None): +def patch_rom(world, player, rom, hashtable): random.seed(world.rom_seeds[player]) # patch items for location in world.get_locations(): @@ -852,8 +989,6 @@ def patch_rom(world, player, rom, hashtable, beep='normal', color='red', sprite= ] rom.write_bytes(0x180215, code) - apply_rom_settings(rom, beep, color, world.quickswap, world.fastmenu, world.disable_music, sprite) - return rom def write_custom_shops(rom, world, player): From ea07c3d9c0ff5b6820feab0a1d03a7adc01ae023 Mon Sep 17 00:00:00 2001 From: Bonta-kun <40473493+Bonta0@users.noreply.github.com> Date: Sat, 18 May 2019 16:10:52 +0200 Subject: [PATCH 06/52] Update sprites --- .../{toadette.1.zspr => Toadette.2.zspr} | Bin 28869 -> 28869 bytes data/sprites/official/abigail.1.zspr | Bin 0 -> 28883 bytes data/sprites/official/alice.1.zspr | Bin 0 -> 28861 bytes data/sprites/official/bandit.1.zspr | Bin 0 -> 28863 bytes data/sprites/official/batman.1.zspr | Bin 0 -> 28869 bytes data/sprites/official/birb.1.zspr | Bin 0 -> 28866 bytes data/sprites/official/blackmage.1.zspr | Bin 0 -> 28873 bytes data/sprites/official/blossom.1.zspr | Bin 0 -> 28865 bytes data/sprites/official/bottle_o_goo.1.zspr | Bin 0 -> 28895 bytes data/sprites/official/bowser.1.zspr | Bin 0 -> 28863 bytes data/sprites/official/branch.1.zspr | Bin 0 -> 28863 bytes data/sprites/official/bubbles.1.zspr | Bin 0 -> 28865 bytes data/sprites/official/bullet_bill.1.zspr | Bin 0 -> 28873 bytes data/sprites/official/buttercup.1.zspr | Bin 0 -> 28869 bytes data/sprites/official/clyde.1.zspr | Bin 0 -> 28861 bytes data/sprites/official/cucco.1.zspr | Bin 0 -> 28879 bytes data/sprites/official/drake.1.zspr | Bin 0 -> 28916 bytes data/sprites/official/ezlo.1.zspr | Bin 0 -> 28859 bytes data/sprites/official/finny_bear.1.zspr | Bin 0 -> 28874 bytes data/sprites/official/fish_floodgate.1.zspr | Bin 0 -> 28888 bytes data/sprites/official/frisk.1.zspr | Bin 0 -> 28944 bytes data/sprites/official/gamer.1.zspr | Bin 0 -> 28871 bytes data/sprites/official/garnet.1.zspr | Bin 0 -> 28863 bytes data/sprites/official/garomaster.1.zspr | Bin 0 -> 28873 bytes data/sprites/official/gobli.1.zspr | Bin 0 -> 28858 bytes ...randpoobear.1.zspr => grandpoobear.2.zspr} | Bin 28898 -> 28896 bytes data/sprites/official/hardhat_beetle.1.zspr | Bin 0 -> 28879 bytes data/sprites/official/hello_kitty.1.zspr | Bin 0 -> 28867 bytes data/sprites/official/hint_tile.1.zspr | Bin 0 -> 28878 bytes data/sprites/official/ibazly.1.zspr | Bin 0 -> 28854 bytes data/sprites/official/informant_woman.1.zspr | Bin 0 -> 28881 bytes data/sprites/official/jason_frudnick.1.zspr | Bin 0 -> 28879 bytes data/sprites/official/kenny_mccormick.1.zspr | Bin 0 -> 28881 bytes data/sprites/official/king_gothalion.1.zspr | Bin 0 -> 28885 bytes data/sprites/official/lily.1.zspr | Bin 0 -> 28870 bytes data/sprites/official/locke_merchant.1.zspr | Bin 0 -> 28881 bytes data/sprites/official/lucario.1.zspr | Bin 0 -> 28856 bytes data/sprites/official/marin.1.zspr | Bin 0 -> 28885 bytes data/sprites/official/mario_tanooki.1.zspr | Bin 0 -> 28902 bytes data/sprites/official/medallions.1.zspr | Bin 0 -> 28892 bytes data/sprites/official/medli.1.zspr | Bin 0 -> 28864 bytes data/sprites/official/mikejones.1.zspr | Bin 0 -> 28881 bytes data/sprites/official/minish_link.1.zspr | Bin 0 -> 28873 bytes data/sprites/official/moosh.1.zspr | Bin 0 -> 28870 bytes data/sprites/official/nia.1.zspr | Bin 0 -> 28857 bytes data/sprites/official/paula.1.zspr | Bin 0 -> 28879 bytes data/sprites/official/peach.1.zspr | Bin 0 -> 28873 bytes data/sprites/official/pete.1.zspr | Bin 0 -> 28886 bytes data/sprites/official/poppy.1.zspr | Bin 0 -> 28861 bytes data/sprites/official/powerpuff_girl.1.zspr | Bin 0 -> 28879 bytes .../{pridelink.1.zspr => pridelink.2.zspr} | Bin 28894 -> 28892 bytes data/sprites/official/primm.1.zspr | Bin 0 -> 28861 bytes data/sprites/official/rick.1.zspr | Bin 0 -> 28930 bytes data/sprites/official/rocko.1.zspr | Bin 0 -> 28866 bytes data/sprites/official/rottytops.1.zspr | Bin 0 -> 28878 bytes data/sprites/official/samus_classic.1.zspr | Bin 0 -> 28899 bytes data/sprites/official/sevens1ns.1.zspr | Bin 0 -> 28863 bytes data/sprites/official/shadow.1.zspr | Bin 0 -> 28869 bytes data/sprites/official/solaire.1.zspr | Bin 0 -> 28882 bytes data/sprites/official/sora_kh1.1.zspr | Bin 0 -> 28882 bytes data/sprites/official/stalfos.1.zspr | Bin 0 -> 28865 bytes data/sprites/official/stick_man.1.zspr | Bin 0 -> 28872 bytes data/sprites/official/superbomb.1.zspr | Bin 0 -> 28877 bytes data/sprites/official/tetra.1.zspr | Bin 0 -> 28877 bytes data/sprites/official/toadette_captain.1.zspr | Bin 0 -> 28885 bytes data/sprites/official/two_faced.1.zspr | Bin 0 -> 28873 bytes data/sprites/official/wario.1.zspr | Bin 0 -> 28861 bytes data/sprites/official/yoshi.1.zspr | Bin 0 -> 28861 bytes data/sprites/official/zebraunicorn.1.zspr | Bin 0 -> 28883 bytes 69 files changed, 0 insertions(+), 0 deletions(-) rename data/sprites/official/{toadette.1.zspr => Toadette.2.zspr} (98%) create mode 100644 data/sprites/official/abigail.1.zspr create mode 100644 data/sprites/official/alice.1.zspr create mode 100644 data/sprites/official/bandit.1.zspr create mode 100644 data/sprites/official/batman.1.zspr create mode 100644 data/sprites/official/birb.1.zspr create mode 100644 data/sprites/official/blackmage.1.zspr create mode 100644 data/sprites/official/blossom.1.zspr create mode 100644 data/sprites/official/bottle_o_goo.1.zspr create mode 100644 data/sprites/official/bowser.1.zspr create mode 100644 data/sprites/official/branch.1.zspr create mode 100644 data/sprites/official/bubbles.1.zspr create mode 100644 data/sprites/official/bullet_bill.1.zspr create mode 100644 data/sprites/official/buttercup.1.zspr create mode 100644 data/sprites/official/clyde.1.zspr create mode 100644 data/sprites/official/cucco.1.zspr create mode 100644 data/sprites/official/drake.1.zspr create mode 100644 data/sprites/official/ezlo.1.zspr create mode 100644 data/sprites/official/finny_bear.1.zspr create mode 100644 data/sprites/official/fish_floodgate.1.zspr create mode 100644 data/sprites/official/frisk.1.zspr create mode 100644 data/sprites/official/gamer.1.zspr create mode 100644 data/sprites/official/garnet.1.zspr create mode 100644 data/sprites/official/garomaster.1.zspr create mode 100644 data/sprites/official/gobli.1.zspr rename data/sprites/official/{grandpoobear.1.zspr => grandpoobear.2.zspr} (99%) create mode 100644 data/sprites/official/hardhat_beetle.1.zspr create mode 100644 data/sprites/official/hello_kitty.1.zspr create mode 100644 data/sprites/official/hint_tile.1.zspr create mode 100644 data/sprites/official/ibazly.1.zspr create mode 100644 data/sprites/official/informant_woman.1.zspr create mode 100644 data/sprites/official/jason_frudnick.1.zspr create mode 100644 data/sprites/official/kenny_mccormick.1.zspr create mode 100644 data/sprites/official/king_gothalion.1.zspr create mode 100644 data/sprites/official/lily.1.zspr create mode 100644 data/sprites/official/locke_merchant.1.zspr create mode 100644 data/sprites/official/lucario.1.zspr create mode 100644 data/sprites/official/marin.1.zspr create mode 100644 data/sprites/official/mario_tanooki.1.zspr create mode 100644 data/sprites/official/medallions.1.zspr create mode 100644 data/sprites/official/medli.1.zspr create mode 100644 data/sprites/official/mikejones.1.zspr create mode 100644 data/sprites/official/minish_link.1.zspr create mode 100644 data/sprites/official/moosh.1.zspr create mode 100644 data/sprites/official/nia.1.zspr create mode 100644 data/sprites/official/paula.1.zspr create mode 100644 data/sprites/official/peach.1.zspr create mode 100644 data/sprites/official/pete.1.zspr create mode 100644 data/sprites/official/poppy.1.zspr create mode 100644 data/sprites/official/powerpuff_girl.1.zspr rename data/sprites/official/{pridelink.1.zspr => pridelink.2.zspr} (99%) create mode 100644 data/sprites/official/primm.1.zspr create mode 100644 data/sprites/official/rick.1.zspr create mode 100644 data/sprites/official/rocko.1.zspr create mode 100644 data/sprites/official/rottytops.1.zspr create mode 100644 data/sprites/official/samus_classic.1.zspr create mode 100644 data/sprites/official/sevens1ns.1.zspr create mode 100644 data/sprites/official/shadow.1.zspr create mode 100644 data/sprites/official/solaire.1.zspr create mode 100644 data/sprites/official/sora_kh1.1.zspr create mode 100644 data/sprites/official/stalfos.1.zspr create mode 100644 data/sprites/official/stick_man.1.zspr create mode 100644 data/sprites/official/superbomb.1.zspr create mode 100644 data/sprites/official/tetra.1.zspr create mode 100644 data/sprites/official/toadette_captain.1.zspr create mode 100644 data/sprites/official/two_faced.1.zspr create mode 100644 data/sprites/official/wario.1.zspr create mode 100644 data/sprites/official/yoshi.1.zspr create mode 100644 data/sprites/official/zebraunicorn.1.zspr diff --git a/data/sprites/official/toadette.1.zspr b/data/sprites/official/Toadette.2.zspr similarity index 98% rename from data/sprites/official/toadette.1.zspr rename to data/sprites/official/Toadette.2.zspr index f503b383f26de35076c1202170f34046d6a4fb40..8c6498b2d7b89016432a6501fe835bea4fcb8ac5 100644 GIT binary patch delta 141 zcmX^5kn!k4M$V|@>mhX4BU%Er7#ydXXc!!HKT z59X)*CcJ5Y@c%LV|Md6NxeY!WHlH*-z-V5_P^(mJV9x;74pfrI7|G b!|+e$lYp6k3{bwLV~WF^grv;}%vmh~5d$gY delta 141 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/alice.1.zspr b/data/sprites/official/alice.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..4c673acd710584e78af762b56b97a3e02c831af2 GIT binary patch literal 28861 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 l*X38Q{Ogw$ literal 0 HcmV?d00001 diff --git a/data/sprites/official/batman.1.zspr b/data/sprites/official/batman.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..a4a1e9c0eff79c3552d1bc0972a40efdd577ab62 GIT binary patch literal 28869 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 P{nE|eyCAr~bM^lO*?D#1 literal 0 HcmV?d00001 diff --git a/data/sprites/official/birb.1.zspr b/data/sprites/official/birb.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..d6d86bb68857a153e89335a3ad59290decfeab9b GIT binary patch literal 28866 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

5$|M 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/bubbles.1.zspr b/data/sprites/official/bubbles.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..bbba3b7546c073a27096393efec2a04fbfdc1b17 GIT binary patch literal 28865 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 ICG#WuKXbC>IRF3v literal 0 HcmV?d00001 diff --git a/data/sprites/official/clyde.1.zspr b/data/sprites/official/clyde.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..b590a2ef919bd3542ccb082ba24f2ab8bcba0e3c GIT binary patch literal 28861 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/cucco.1.zspr b/data/sprites/official/cucco.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..f237de4ab0a7c248cb387cdee4ca78a76ef4b41a GIT binary patch literal 28879 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 F{|nmGI9mV! literal 0 HcmV?d00001 diff --git a/data/sprites/official/drake.1.zspr b/data/sprites/official/drake.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..1be94a75671672319036a88d9bcc71ffea095566 GIT binary patch literal 28916 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/finny_bear.1.zspr b/data/sprites/official/finny_bear.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..9c3a530b10b6f2db63934d9d51d9ec5ab389605c GIT binary patch literal 28874 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/fish_floodgate.1.zspr b/data/sprites/official/fish_floodgate.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..86684e7d8e59f891ce91d55e432b8dbcddca227e GIT binary patch literal 28888 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/frisk.1.zspr b/data/sprites/official/frisk.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..d521cae3910f02e3f887d5d5e3c7a6eaf0e76ec7 GIT binary patch literal 28944 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 ZacFnAJ@mHj>kRAn1|BSwrVdRV`akmB;LZR5 literal 0 HcmV?d00001 diff --git a/data/sprites/official/garomaster.1.zspr b/data/sprites/official/garomaster.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..65b9959d479864f4f253521c7adf9f9aad7e4f0d GIT binary patch literal 28873 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

SfDd@5#YK52;}DwjO@w_Ax1k^-HYOv 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/grandpoobear.1.zspr b/data/sprites/official/grandpoobear.2.zspr similarity index 99% rename from data/sprites/official/grandpoobear.1.zspr rename to data/sprites/official/grandpoobear.2.zspr index 857265f9ace47aa91a641c15aeec6f87d96e4a1b..726636803443b554f9fbd93f441b8c5306faea94 100644 GIT binary patch delta 58 zcmaF#knzDoMuDi{fFQ<$J$A(@3_wtjGEqQXx`3gGA)ldwA(J5&h)WnM8H$1YQieQ+ Jl#L;M1pp)n5g`Bo delta 60 zcmaFxknzz&MuDi{fFQ=^o|@t`1|TR%n<$_z>&lSJkjbFH5WtYgP{NSPP{feOkk3#I NWEU~yZw%@y001z!5aR#< diff --git a/data/sprites/official/hardhat_beetle.1.zspr b/data/sprites/official/hardhat_beetle.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..80b63af16f7b8bd582d7809a6e42f511cb16a6fe GIT binary patch literal 28879 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 E0PEv%Pyhe` literal 0 HcmV?d00001 diff --git a/data/sprites/official/hello_kitty.1.zspr b/data/sprites/official/hello_kitty.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..a2f5df063ab2bc88941773921deff8fd073ed7e2 GIT binary patch literal 28867 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 V>RnRu@xS+LzW>G7@A&oF{{!-ug9iWr literal 0 HcmV?d00001 diff --git a/data/sprites/official/hint_tile.1.zspr b/data/sprites/official/hint_tile.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..9cfd7e90ce26df62fc738c35db0875c43893282a GIT binary patch literal 28878 zcmeHQe~eUD^*{4tX9ha#%<=`D4a>d(Z84_bc0`onNB2Dlw%dfZ2*%PMvV&5nqOd61 zEz*4hY}>UpwQ2aH5+gLxZ@&pKU%rXy53SwdYlHpPMrut=tJMyrfu=!rVk$dj-@fPP z-1~mbfFG zGHs#ll&8<(?W=SL;#T3go;J}pfo-6hKo_xJUBBs@cWk(6%N?YUl2R@ckWwb9RYFENEl>Ng3bs?;N7&y(ySf zwTvhk>-~+yT`(f&p$9ELX!_w**0sYopSQFasvj1R zRh5E5DivsmBkfC9rUo4Y`7%Dk{+u8;Y!b zQct~NLoQu6>QMJhx!j2QYe8?0-*##34{^|B{1nEfF-8SesJ;YcPyr4daPc{ zRL)~{D(x2)xjw^UR@yJHdW}#dj@7NSUkK|3FGw-Ta_8U04?}>efBsFpNOtWf4{ZnC z^xIEZ;8>J_6pPt@pK4-oO>xrbB^ep`uHWap=Ahfs6N-I;cYn{n&GLDEGy&@nda4y5VQnxO&p-@t}G0g8(EGbI@iti%{{i6}JyK(6ifBT)cW4N@syE<5|Gt=dt?t9{%KG;OyVZIz zQoAl5ar%8Hrit!EPu?en8s82O>e09ZB%8Y>0(a+91CA7cSE;JGPyxM)-q(RDe(3An zFMfW8VKk6^v$6bg^XSsFKXtzG2{ocD6|-{aU`&K2)QO&KPbJlH8N=+UXa$&oikSz` zK6oC2zZMma_T6uM(!AR;AWNzDqx2+w8+|k|SM#zBRJb|#p|N*Ib7|<^FT6h#)V}HT z>v)cDd?M>sYq_=jj~lI9f7vhi|0Depwcr^v>fiYFT>SoN(DWI~#4{~`cqW-nPD@Y* z>w;;3G~P1Q3Kreu2%7AzpL)4y$Q|JDPiU+l2cN8jGQJ^H|lFA_~lNJu9$NkvIUsMQ8& zzf*}sM$ZUHBu%sa?y=_vurg#w#?4%P{jq@or$UqU8Hu#DMW`j7j4Lyh>FO1X6U7l` z&YV8|iYvHYgM$_A`R;nLo-;N`T+_O{i^W`S!-fb#EDB+zkvgwm&v-OSlul2dp3h%@ zJyE$_;l3?jUuz5)DaJK~e17fPfq@iof9PN3$8h|j0gpt?m;LleU>%GbNdbVCul(^2<9rr%Xw> zxG|%4sGh48Dq6YB^cgb_9m?ej1f>V^Pjy{d!Hi^UlSK)9I+d&8RsS z?QUB8liukijHVX|Fu^S|dr*RA_M2GiBE?~U{yaX9oz-{fyM>*G?Q?Q` z-h(&5PI_jAvCixPEHjV;&U+n!{fJLk9cCL6=Eo(at8=!}spM64k+gmE!}iogY|F&j zwo#({s6V0S#8_s4AA6~jN8cn6VHoT{7wVR20pQF8UxxGD+=o7fL zTi>R{W6@+n3S#%}ZQJ7UXf$Ettm8_>AZyMK_wg?zPBdrEK7o%tX7z4uP13x1Ibn-= z?6Kb7)>d-yp?j?sbH0I*E}iD|_ukvmGJiggd=i)QOY0-netm^rSi-AwQ@baBaNqtF zpD7e#G243Zy!Bi2$h)Z%Z-UO&gV)-Ad*nze_2!#6z0^LuM!%^zucuAnuhyhrXt#CW zl%JPvV+ru-CF}pJ>W%MMSDlJiDx5m{dwOeyk+>?(I6@k*9O6D-IR1qb;&ha$J*$hx zVtKX=@IsvbT`%auws8+^ZAlS9d-fBT;?RSo4?2Ginm(ZTE?IIYn{A+ZH$!OyEjf6NYWdoMnn$le|A+7| zU=XV+uLtJ#*mO zFK}{Q{o*y7id*MH`_dA|U;RC_Lwx-@%w5$5)%n%`E&VnBPL&N`sam?;YGlk-BVA9` z0vFHf_Oy?ks7CEK+yBo!{~MfxiOnHs{e$*Djpdj5`?#q67kl279Xr6|@S>uecV)8& z4)8c!Eb_c7L03nHTFiv8;H3)ByNDu@mX<_f!GcO9sC{Q*wHLhfp!E;h|9B(s`0QK3 zB@pHiN+6U#D1lG{p#(w+gc1lP5K17Fz+{quMjxfyv_u2Tn%01Z$iK7%QrIPGtYGub zW+OGkLJ5Qt2qkc7Bp`SGyu6yaB99Qh{SzcGk{Z$u^f0J6fFcS|?cD?3OURE!$D%D~ z8OHeb4i3EY=m1c^4O-CsHfTwAcW`XFw*zzu(u+L9KjV_XkT$e#==NuCS^B%Dt{c*z zf1UcVfAnpfpx?@^0NUR%w)mcXOMgB4jjEMocIag z-8=ss7$SB*@^QK-1*|qp((we6uw`)1h`8dUZuZoDPsSf`~P9=AokFHoI7VqoM7&@|Q+>h)r$@1ns{k`uxg7OW*asu|06mD@y+x+XDykb=N+Y8N+d4`+Tn$ zJ%LSd2+ywsQ-I@tGcCa$IJm>l@xN(BO90sZH)lo@F;mQirT+~)aKuPE;rb6&`!IDV zflvaITms2da!Mj4A&I&qMb9PsF}{=(v+TM}kMn$+XWgdV&lz^zOJy4z-1PyrBk+j8 zp~=6=7{N%$<#9mj(xdrDiw?bn^n6bP93Dha@`}PGtaos%K-g2~n2q0;x=+;LgoD#V zg2U4`?$po8KjHS%@&|fZ-_QB?+rQUE?_5PaWjL8S`Trsh2gfS;sK`S`xH7Woo{Zp6 zZ}ToB1iE7LUKKJTcAzM49Naj#X>ikbHi8bEPT+=^VF5BjE*o_mWI6}t{~>@=zYFn? zvMM5>{fEmI5uWaadky(?FC*$rhs1eZ=nJ;qQNBHt$y#CBBj+~Yo7>lk)CsukaFFBT zFgO{{z;G$G?fYbvS>If0jM;e^ll#)3a_w#0B#gTKDnq z$j9cNxgXrv4c*%Hm;C<*x)l+F8>^l9&Y~wDzB#B1V9V!1{7a^}rB&=#@o@F;j~w4S zx{Ek`my_WTir~4s4#gXy&$_-O} z5F{XN_Yda>ii62sbRPDXBflS=GMBfuT-EaNmXAlTeC}hZDmZ2e-~Z0vSyNx~L8wp2 zLnwiXkN`dl?4()Oxn;{QXvD#RihKMV-2Tpn#r=bPLk@2L{zT^|+22~;04{F-=J^wb zKEC}d%hvNpdqI~L^c)iaVVF<}IQKR}_t1Z}2R);>w^8U9o?Onojj$q#d;DONAHvL6 zwhru~FKlPc+jAqX$u(kTv~(Kv4VRrQkn-OJA12<@#Fuk5@4LNw&Aoox5U1clXmz zA3VtU_hG23@Qb)Ird}Tyh(^1*_(rYaVfWrfLB}8V?6088c5v@)bRu}{WH0wi6#RSd zWzJT%EcdVHxozKs=2Glm2IPZVy|h7|{;ah^_>Xc4@&>@a$@2r!hyShzthvH}lX&BxD3_Y^lTR`A`)yMUls_*IQsq^MGQ@xz~KmYaI)XlHn@aheJ2X2yBRsaA1 literal 0 HcmV?d00001 diff --git a/data/sprites/official/ibazly.1.zspr b/data/sprites/official/ibazly.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..01114c9e01e92ffd46eb3528eb43c62064dad80e GIT binary patch literal 28854 zcmd^o4|r77nfE(?GWUi|=1xc?H)NPQV1y_s83D~ehRmp`Mx_uHD_SH&Wfhge2nYj? znNd^C`te|NZJTYW&06ap^uv6+w5BeLSC>_`55?4Fx2aDGx0Y(ES-4`vD~>bY``$BW z?gX{Evd`1)_nrG>GQWH7IrrRi-}9dL-#K&LRqa>WzVU;{R?Y`Z@%eFpB-rRXrkmkH zSOHsLHEc$70o(wq*tZ*2Z3YLp9bU;R1r&BTpxjn1C{k1P%(9tf^CxQ{Yfi(SnPB*} ztNX5hTmwUUxMy16;%H|q73cr~WFeqLBdy`?#DU<|=L%5kXq1-9Hw4~_y$Xwfm!B4{ z@UM68*s4J&u&e+6?gQ>WINyVHh4RrzI+aeT5mvrKxoO5bxBly;|60n++cUPTE$hfo z`MJRzPwsr?r@MZ->xsv#`)`Y@6W2ag4kF`{wRGn zDMpJLRzs?0=0BUh>)ss?Kh`^tX81d69*KQF))8p*&N1^}2pqWL;N@4Af5p!5QyVwC zu63Psj*0ut{EwaM?>~F+vi@CBhS_^hr(%j%lJJUKXTE*Q>(@Or@kW;y2($NY%eo9v z*AbsT_o^$eX#dU7%W8L;@TcGZ3eTUu|NU`({{zAA-1eO{%LB{vPjJ|KAbL2u+}18N zIOqx1yPw+j)W$8&E%_&S4{mqnT%BDL^tmc~0=(!nQhRVX*t_ccu@o#8Z>kH!V%WLj zW$eL(aJ&5*NIetW^E@?^u61SMxc5!WKMuEd-GDv#NNi{Hb(z0JIYL}Lo?ceOV)b$}AC!Hsq)ZW{>=KSKK`F|aspwA!Ieg_B+k8?udNgUvG zl$A&&{6q$_gNL+az{0bJTSkZb80MvUd0v{A=cVb5QOW=-t*a(PR^_k>_7ZS1h}do` zu?u#=!A@i$Hxdc#NgGj)tB)Rjvr`|4Gc15lm|OkLGxz0r9*$vQrgD8Gw~6KBn9&+W zF5X+2=6FauXgr_E9p&ZSLX|MPa@ACh%Y<6#D@sQbzn+z!l;)U~&yEt7ph~C{T57|# zuz7z8YNcl7tl9`3l&?WhtJfB)zV7go3=e8U`fhz}_nJDRGL zQdg-5~IFF5r<5FCz@Jw{G{9~K+HF2rNE)VRrzc>fmL2u4CamZ2WWg)dxQJ7+B)h!Qlp@@# zA?G)`adUqsnF z5BtQ+FwN-~M;13VRuiTL*K5$O^{X5w zASY+4Hy55a0dFPV*_2{LfEUHUi1m6ydHz7SH#QRA$H@w>pu3v_MZQY9yJQYZ5$B1~ z`re-YzPOo(SUeb7rMGK(y2|ZEYPaaO5~;5kFN-ni9;%H8z0Dzpy8_Ho;a4^ zme1#mk(t}^5xbWmf33z|Piita8gXR;r zhS;lrMDwcjJjpyRJ+-`ReEI%9BbrDLM0xpk4STMCAj-=V|1CqFS?Xf^hw)!gkn-4w zjlhw_-cTzZ$lRecK1_XZhlLGTxX;~PjCUDh`SBw|SWbo_%Df7eFGHQ&8VV@0O#Z>nN$}6A zJC;Nk|DkIncu&8tOJPjj#8U2#-0w4# zM`@BU&CwtvDj5Q(krpV;)ob(fCqSFps#dA~eLR0a-)&?@PMG}5nthX1lut7%n|*IJ z%Uj&bZ?@l>eUIn)AFuV#_1^`hwn}@ADCPNu3p~OUPqkEPj(=*E47*pI6i*ZwoD2t7 zo!EMEcf#f?CyM3tml7%^5$TcBUtM1v?CJ@tyQ9kjM1R9ZW@z3UXv*hF6K zNgTmcJ|i4a)JQaPNuHuV{3~s_9!W$}c^uJZ7>1{Ey2I*n~f8lAxG-(KspQUf4fkNbQbu?gKCuBFwqZpS9`vo7=!; zBku2&bSa$zl>J>$=J2>Cxian#(PE*4TessIv}FZDIzf0;J85(#f4TpAJvYZrSN})) zl(-H`76=K>cP;n75P7!ogH8W-)q@o(1d-o64YGld7OFx#x+=D$ec94mR^7a6R=ol% zYcFW|cI>Wiz6-w-Ux+Gjd&RdJ4o4$_?T){7o%|}`tThBUe$pp4Oszl=6|QLp%@<1l0a0z#M4wlK%Rg zHQ%}I0Llk*puy2DEw>$xq9*GF*bieO>90?1d}`ZzQ?8>=rM+?ZGJEc=m z9KF#EW;M>)5enYiD{3%CKp~sle6H35$Xkh8g}W{hw*? z0H1DuxIfOdcY5b^;0sd}tp7j0KD=yr*;e;u69Pb(y?1-Yt$Q^MaYOy%kNsWWYlHjM z_9)@C^7P4y&IX|bS{eT3$_J=u-mxN`2Ep)4sFx- z1rvy?-I7BAW^1AH6?V5n$=hNGhmiKVj9!#@Vgx}9I=#+*S?fUwkWw_1PO{d^ro6OR ze7fF9bSRBdt%I1q-Dlfl%i1$eLpIz027Jx^z_#ZH<^~fnjVdr_yMQvga-Qwhz$&Ca z0j88pvo}BsT90rz0?l@e{$C2egj0Y9L+Y3w4o#Ndu0PsfX`gHq2{75-;EpIA%9i=h z&p*D7m1g6&V!x8c0BpNpLyFtd*>bczB;Dfh4XxiU80x4gU*RsD|Z@SNu{0hkCfoCPj*l7T-|p?-?}{i7--L=$!1Ob zd`q@EQEscz1iT{&BkU7ri59!O80%K+>Nj|BVDzX|0B(e+&iXxYNW; z|3EqxE2OFZGkw!ciAs@3f;-zE@wJ$^(H#i{1D5>u-r>XApefHW`K=mSDuVd*(|i-% zBwQ<=#)_c3!qejOo7nJX#MM2b-jyKk2`~~LTeaFN>bn!fJprcMH=T!6GNr=9G{^ii>42)5^G9aH&@_mpV|mjB5E`$6(L41D~hucEk z@n21KfaD0f=S;k{{-(g;_D4X4kh(QK`N*{JgR^R8op&K(*d2VN>p!aAv%d?+Ci41X zD3|H!0Eaj`p@mG-R5m|w@5CeVymuw`XX@8$oA{GIu?_$a6O>68533-w+UrTzz|(NeC#G5+(x9ZZAsAQz$J?&ca? z2CYCo7&EvAmyrqYiuPY{Y0EYB>pfHbBtblY$4!CGv*WgT@{|$*Ua&x zM%Etu^}U;`-Z}f%mp!-O)dP|5rDo;=sJ*Lc17vScdJWFAnyR9$*nkl|u5Hs@Vaq#_ za-Xa{UTJwJ*1P|Z`p?v*@S*sg9eo?lz2NH=(~+02nf1dpvxesnXF^8|L%*Pf{E{lZ zz1utr^4iNMOtsA{`%TR=4R^W~C~+K!W+7?XH~ag7fq3li-P_`;y<@A4SUd8W9!*pF zl>WxS#qHA4>QJ>JzH|B8m%V<=^S7*GdWbT1oWBo*}pWs0qOH(8>7!Qv`Z0v{=IWN zeMcjNC);P1MU*=i5QXRIyT2{HB)tx6;N=s@*-mynkL8O!xKsUv$%E_n%guWYi- zZX+odYR4inSd9##Y9p<=!(C1jL;V7tV*Gz%2wxyVJznzAk%Lp+?=6 zAp2GVuOC6gaMHwN)pUY@x_}K2>>adf3XYNjc6+CVU(DXWf#h?N&@=i=qmDz{Ospd& z-agJgVa`vyH8|gR$J-~4onoH=tN};9_vH{E2lylpD5g)cwp#Sr=^?y4-I=~KozhxV zJ@yCZ{LmWjl?|<-l?hdi#hlJi=)wgn{EWUYX^u1iYzf^gPP701%>4^5FAF2iLMPVH zqHb1?+Yc=C!EyJZ;F9_PsOsJJg$n_F&t9BwNq!Cqg3aMLtHcXul&p4teNx24&qgC^ z8H1C zQ=?i`3m8i?T^TX^Xp1X~DG*q?w5v-LAAQu7A2@gpjy(3!-=wag@~D+X0iJy3;qP6U z$9A-HyX?pQ=r73kPUpmlH6On5hIu(?u_KSY=g4|;_c0ucJ^2hOhSGg`4Dgk};a@2g zxj(RcB*@^@ci(;YUkVs0*_Vucd>79L@cCk~d+)LEr+*Vckz}MG?!S{zcJ-qFjr;E+ z^?q*%#}>E0{rc!p9G{l`&TVty2&Xa4NJT=ervx#x-Xk6Kh0@`HBMDx9*f7++Mi*ix z|2b_F9AtmwBT{gbn;rnpAC$CzFl;=A7H=kB=H5K2Q~A9iCSQicV4(QPL@_W9`(!ur zNGr**a~w{z>FK_7I?b6H=`IHk%5pGBe^C>s)~8maqN!DpkmPpQZMOC6SFDIeSFH+#+-^JK z?7dlgb{bOO!&;2W-+Qm!K5bf8*TWCTOq^(o1Us&j+|A9578frL2Cux*UBDMcXN9s8 zGhKQ0AJXcOF>zv7m(0uoh3B{}Iz6zhJ5s_=5S4d#y9$_}|E&LSb|n)&=#+VWS62er z`Q!rT=RfOzG^+I_+OpL=KN{`pyLeG$0gEtsV$->)L7p#yec}bTr3VGm-Wl|q<*O9W zs-58zPzS9R=84U6HFB#tUS>Tk_1xvU{^D;v`jlUX?LDs#z3=?U{*U(G!V4GX<-^c{ z&<}6BdKQ;YukpSWI2!A~Xo|di=v(jm`L-P!HhY+S`nGt?eY^7{MpM|}GU$QXPzTfD zZs_IBJLNyK&rcRTdD>(4`AOvy3jI3(%ft!5^8p56$&;S331g>Jgu@yQGo5&(@P8O^ zzCR_!kufGQhZG+Jne6AJoGtfaBrMrc;b9bOd-26^*lrg^PQhO9N10p=nm!Xmf2ykK zU74C1mn&i7tUdQ&YGowsurmhWa6I_n%9T_D!|8M^_B$QpH*9+zm5CbCX>AZ&WwSkM z;@ppO@wFZSX3Q{i;qQJNkCQWj0E!k0FJt|rUpSQ?LPR&U*t!`4GUacCjXZ8nY!({R3s0t%$ za@BQ9_d|C7iP4D}Y7vW|YmFov*Ue-&q$JRXlN6Sioo1L;*l8KRdfh?FH^683(TCHb zofBu$FX;%>igb!ezh5bzQ`ua*-2ZSD*Y7ZXQU7WCKV3||Jc?g@yzhW|3?uYTw|udF z+1J7J%NfG$=WUpKaLG?f-V)gTEeoOsOQUmx8RMj}eA)HO7B8abp%g<4oCX_A|6?A< za;aMtGt>M~k;01QPZcy(*R1?c|Hj+*S@D^lUh0dV4J#x)SoRwxk6QMdFiw4BuWAi3 z`;8Oh_?Z2Q*>5&hFLobwzK1b9%zk~dvOa9AKM_la%znLQsaExBOVvkuj=_(-KX^lhShOb{f;EtH z9dmu`x+i&8=Utu1ew7YLh6n2Zt^Fr;&(7*>33^k3iz2^X`_Y8A(Q7>_?iYs!u_xet zk1KQCGrN|Kh3ZjLv*fDaNMrtU0Y(xfgKr$1!jh3s=#{*(#$XU97; z6NLemfK2LOYHdOO`0V%tYlN*qX@lFRD1LV>5yip`|h$ILW0eu4d;Is7i zd<6CctGq`))bPGpo^h~6(c^)f%Iyb4hh*X6_s>UAV>}1vfMnrvAit&i^9btacA0Y5 zr6((LkJ3CmFGOo5MQ2Fww};}`sCIj=vk_?fIWlGh)Y{aO}l%f`naRN+Xp)Yfp2 z>~M zn0F+A{!7!UdH()KRyPa{1p%h|U&Q{qtMlebECRcE@?rm5H-6jst$YOb$FApk7RW7- zj3%P7D8^5yYt`Sq{N~VOLj!7`if@xt`;K@c_FA=!Hore!`p)G$F1zlWE2qtxB%^LC z7C+RR288q&s$?HCKfuvLSFSq&Vs8tVEiOf?J}9J3nKqVX!gM?x-V<+B=! zG1b6u1TGO4OY>?-z@!v`L?j-Wt|$V-891)Ld88}N^E2SdOwJ??Lu2_=g4JMQRs+Yp z1`D$qIOa7JW2(WztcGGtHE>M+LlJ5U_7(CUN>2EghWX`)*9`C3pW!di zJWq8LF2FBTM=_>4#^W`^qHUr%fAjb~)c-La)ry~O=uTqve{YP(?~M(GmxhOWV?6%F z5M{ic4i>@sfmDIt{dh1dVqKcmz~|e{5UYV>qB(a7YXi-IL~~_k8z=(L4|Pa$4c%ci zgX#<=vR-e%7Gz|GMVN^9x8e^af$nd$KNx6>lf1#(KZx;g#qozST4tyQ9)YU}Wo8XL z0$0Wvm`O0K4g=?3Cc&^6Afh=Ds_>p{s1VID8w$fjbHTt;GlsA`9tWbiK~2``b&Nnn zTw$vz^_t!$Myh^k64r(OxBT2}M0K>Mr%`%Io6kQGkB`GdGZi*Pnp9d>J8en?;}@$v zeym|q>5SS4Vxk$UgDkXE577+OL0Cl`f=fbVB0QpGFnX>*2tg{8i0Bb*`GJg6D^fNtp19Fy`q zlI1K=YVu_mZpBjU8~faC3>38)1GiUNnAbG;}4{ z{2P%KQL@npX0z@Lf3?SBV&*~Ltqz!Dhik~mAsK74BG^8!JjD+){Uc;P0L2d%moK%A zkN@?Y68}5Yp6nz3$M64Sd|8P2AJ@=X%SjSpi7=Dg878}DoM+;HHtMYt+R>PP@e4l^_sI#-t;Kw8H3kHbC(l&v4v?3N?Cz6Rp?WT^UjN^)xgWM*{^s$ zWHng%tOg5HiQ?-&Ie$_O#n)2}3@d0wSEQO!K7A_~!GSy&c>v2NUNfR~r#b&H0M{<) z3Ek3Pcn!RMUIWLx1`D$qI3_C!wV@bO8!XIfD8^KSi18kD9iP7?w7wV5-|lX?M;@fL zcWJZem@%T)$1O}er+EG*o>M%3yZ8BelNc?rKgj0mG2@8gjysLvJQnetix~S?Sbv1` zFJZp^hXJXQyk>+s(ura{& zXTod@5Y34=YnC}DmXiiTG)MY)nX|H-G!UXWtizP~amF-lE`ugQ-!kIUl|ShDi{%e& z$vF9g?p`c^=%>gZM03UR2hm)y{6REVEPoKq70VwD@B?_lb*;P_nqeEn(2}V*Lw=lW z^C^giP7g{MdS9l{GcwVC#F|JC-ZZpANhGJ>sG^ ze!UJ0^%I$&735#kY~WSt_h<5a!g_AFNd7lquR1$u18qcL}P&fvBC1DsZ|KOU7A)b$PTq4@| z{BK4RA|3GgACQhQ3O_CP@ar+I5~Y_B{UMkISHkt~^Wkscn?T}&8~pA{kM(A$LZ4dEdL|3A6WiJCckt}2s=H^0jnT8DSdS6Zg{aSsTl zL$3{AwcyMtK0DW;dwX!LYP!dqK}m;Rg)?V7?KLsk0Ir|7HjoNN@OoDMTfvS6ix(^y zUp_h*;4&zd*ZXk|Fkx;(C^YIX@QYDnRIEekiCZAvN*&u2FS4M>J>w#* zY#2>13vpbDLpJ$nL`SGr zqI2F!xF<%|M@xUl=rtSJPPhdEiMY5@E>I4f%0^%RCHWY}bV`=0W7~`#jh-;Ua1yMtA%VLOVrY%G_&e>4I*+bv8ZVCNvm zUu6FL^!-mH@JV&Ttg@Dh78~gg5>(>uALrFJPKjVY5cdmeL9I=dArzA}!lZ+u?M>^| z2vf4^;F#CJaG{Pg$Gi^WzJ=Wz#47YSF^)>RH#A9=$Pg&vf$!e1RVU2%UZA}sh~Ja! z%2r=SBZyIE6_FQ^#vdKe?@x3*j{F~c+K&B=kydamHYWzZ#}$VmAtVI+m(Hd3IlyCg zA?KMVf5S_d#c$ld)#fTS)d~zyO8jEqI{3)`G3K)N(p>Z9;`;^cy$K}~N?7=kU=xrF zdP+Pcf*F7}dD_?H*m9AgfO-MqkZDG~<=ZoOwnKL^D`t0f+j?T1umb z=}$wN9AlV`Uy?ld-XT=sshCPqbajZ`4rtu%M%ydLtltx;!IayT(ufkkcOFGe!HL@a zq*CKD1(ENwsV8 zgv%#<#TF?)T)wAai@U~ihQ#9glNi6Dq4g~mrTBidA0>KY8d~4_RQ6aj2AZnRtw;np zrWO3NzR>q`Utnv4;`RTXkyO*E>;FG({}a0R4B@M`WpdKliLa69Iiz$p-_(3Z z)8-}`Z`Sy0!1fi7zx_BKDY5pSUAi!DKcG75mGhNN%`D%lgJV918D`~Ie2M*6a*qY2;ORrXKMt9Fmk!LJYjSG#b>8P<1hb`KNY8{69U~4*Q}ZA$LE>p0{!LI3EA{i~R0!w|9L({@tJGDPEt|1|#WDuFs-ata0nJ%=NclWPO&|wSVRh zl0ckAZ7?N}y!=k19`!zO7wm%?c+msL;}@tGalNn8hx0c}0`0$swlI#-c8S0Ax zVbU|{y{Gd#09xyBziF?+m9f_PEVcunwLa@*bwop-lGq?c9m}iN%xrN)FgQ9W&p`?H z=BBIB4u~sSpvpT}x2s!5HXcl33_%Fo@_r+m`R?eB;qcOo96IgtBHAy<`!C3TY56Z) z&Zc0hZBfC0aWeC2_PGO#3jPZ(%&Q41GL~SZqDa$`_hKw$9!El1>?iWvv-C=qFnR7- zLi?eu3b}1SL7s`H6y%;Va=?&+JQcBwbVvRJvOv=0XrL{WPe^ARlV#dyu|&S48P3BDN9o`4o+ z5_~Zx37%uJzq!!^Zs9WYq@3!%+%U-Vbs+!cGeaD68N3*i3~pg2gBN3x!5OB}$97QT znD3y*@aN6nmIT1AXIzye0FHSL7G@Fv$6xk8&A-JFG&KJfN6?V>s4Z^IAmlyjM*871 zNR5d54&e-TLCZy9GX7Mf?up(vX+_I=6DLgxf?51&550^Q$5Bf-sL>vJD6b>$vm|&I z%J0Pzd}sT334Z6`cnMy%`ATRHK9V;`3s!_$K{h>_%nzUJM&6@}`d}btcG1{C#?(Ib zJmz{Em*ctK#xd91Xw1+Y%=I=d$A`S*cTj7r9>0THOT)4l^&a;>zBW332epM_vg(c^ z31Cz6t>Yws+6m(&fcKA{Dgk`iH~&AQ9NI6?Z{bJZRv5L8l-VfKFZphE=(RTU=o$%* zA+?HT9@9am0@*H+#&CXR2UXxSEm;AyW(6=#j;vecyJX3U+8R3;CjGAXpf#Sw2T^D7 z!AtZ1R@nr$BT`hB+DTl6AAV@T*6?z~?Z(Rs`3C-f_U{9D7oV7Vk$qC%qZV8EJcjR_ z^&r~Rf&lJAr03KAncqXEf#7J}4|19zHPCS>xQ`Y@izIA_=Pw$Z3 zpO@tKC)%R9jwT-V$!g%A8M9@qmGsH>Xk@KqpZvTvK8z#U1*Y^M6PfgM?jd`AM zeAviO#<2#6G@NA%@~s9fS}yxmLH;Fbpc<^iYOpY?p%_yQ7G^b!$98(ng+w(J0Dhr1 z6l3(07uv_p2TsAhQ?T zp#-C9Xbm(m(VW}q_XL!S!gaU=k!X%0a01E=;V@yOIjSMpN3%HY(uy=kHMFA?O%}KY z>724@j_HnBWee;rC~ePC&QlgB3u@<1UNE@@%g@!947RE()eVEYqe;x)Z#;uhbGwHc zmf{YIn18zZ3(?#{X}dIh&2k56KmY-GedN8hPj9-DG$6!2dA2@JZ+?4=N*WN(j`D%b zt0(^b=pNF55VPb)o;k^>6xb%l0ZFyxR{S~2ff$L#X zVNwFWp0p971ddVP#{4!8K|{UB(2f4W3FPoTF=Ro+)n$=t>jc$MA1KB;?&8g{0%2bg zEr1q2Qy~i=#~Sv}^gv5tylW^4AIiKr%<}IZlkA}^Pn zVEmVUf8oph{y_w0)J*kHM@nQV;ySzclpvjNu}j6gQX5!|_Ja zH_efonzuC3`W9koNdOb}G9NRsL;{Rwb0gS+9%Y{*<%%pdyA^l7Ln8^=LLOQ1Li7L}Zl`_xem<{5 zNx7a~_s-UHJ$xU(&G&w6-{gE3_&$Et`o1}4f3Wp^U;OtEIQ@_nMvZ8=V21trh57p% zrGLDXtkrz=sv{#9+PKwYPdsYg^fW%W{c%)&-}Wx9q#{jv{^YB?A9&eHDt7;A;U@Xn z^aF+cp1aX7?|;J1 zQ3MZ<-=#BItU1Mq*t`Jk5!w4teqc|~-ngV;Pt?F1cKP(tHCyco<{x%-dFn@ONo3YEH3!^$< z{3&89kq@yrmY@8P#j*Sp!M#eet|tpfuda<-Jq9p-^%y?QxYc6-pSF6;s`J18?fc@7 zyst)I6>kdM0KrPHW9){vH~f6#So5;xl&?`yq-(t&O?c1#LDh+>qxJ56qT2ZK)wn;@ z%;9r}|06Vp`$K)+{`c9vpSJ(~hHmm7Q6lL9_nj|{SDG_$C&vOm>Wq2=`}MmscPCex zJaLaYsJeAm=HBFL6JK-Y_im@(@8G|HvJiFz==VFW@UR_7-`@Yno%H)1{1;GW!_`ju z{SIpfQnHMBAgfz*F-Ci2JQ;>tVMptd`g-q@`W>yU*xpt++M28nd6V@=O+3A%r6g?g zlgGq-p5`gdQzEspi%CBKEBkQ$LYjPt%!4pIFg%c48q&slAxXC(>X}~HkAhK;wSIr?U(^cE(Y zPO<%wMbPtn9zoCXmyLfXt&M(xVq*EUDKq35$RF4a0#nM*JELB#M|p^kUynXf>v)_n zUw_*f+Z^EcCwZfoe~|UFn14_NPiM@>FUNO2Yxt2P*7^s!FV#@E05R2IVOB#irW!0v z-`JdEoh!yENW%jUSa~VPrQ$iF9HCPtdj6pS67Fg1HX~$7Ih*(JL%||1fSn*E}U%bJEyI1bU_Yd&9?CY8P z_q&W_9-ECSd?X6r*q2lBC-v9=uJvzx1u|~{t^TDI$Qgl}l! zzPfZ>dbBNx8flTNqa$*?rSgff8z$XbjM9M|MqMBUwKXZ@i|XilbCG|6g>PoV5S| literal 0 HcmV?d00001 diff --git a/data/sprites/official/informant_woman.1.zspr b/data/sprites/official/informant_woman.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..6465a0e9f32e95f365c44bce137586c13df6b8e5 GIT binary patch literal 28881 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/jason_frudnick.1.zspr b/data/sprites/official/jason_frudnick.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..2411759c2afc6671250d5baef416dfc12432b010 GIT binary patch literal 28879 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/kenny_mccormick.1.zspr b/data/sprites/official/kenny_mccormick.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..c66a74a586789717bf08d01a989711344c4ac184 GIT binary patch literal 28881 zcmeHw5112GmhVYYT?tK*6irK4(;+FafmRs_4PwMLDUbmiQF>in8<&AnK^S$0fQ|-i zNfvvt-+PR(j&G(#M|ym&j)g3;GyACqn$11armG)a#OFqWsiOxi1HDKtx zbL!@%ZZ&@*9lz)Jy?3j-lT)|uy;Zl)J@=g7IrsK0f82S4=a#L1nYsxurEW?Abc2V! zW4abr!gugk4?0{2x5L%22G+s1;am9qF3g{U)V;78ueue!OZ2*XSFgO4zWG4$H3g=s zap=^sN}ExqHoYw@|FD1BDJZ;gcWk{qBpeH zH9u&6Jvt}3AhEUk@pNC>f&slT;O&`gj&8q8`&hF;v!ueN;-T)35|2lkBUFBxx)fX9 zX%t`(Sb1tWo1ufs9}moyf>7u6LkL)TZ_!)y4ASvMv|r2pkCfJp`th>PX-0e2yaN{N zBVnEQ#}hi$>tF}0*E``UvwfeEZ}`CTh)=prIwn0Owa1h|gSr+z^KOTFd|s#j_G^1x z{kJ2%!_|KywK1;#I}tfy_utv?f4i&v>HWVXvL2S}NtmWK1r)yz-!An$y?Zqj@FOe0 zDSXW*bIZ-yN%t(a{YlR9T8+cU~k>N z3!5&%Deo!oN$Ksw9cjvEZ=K%1WYe{_Uu*fruo&)utobuz0Q@>cQ#j7n!wz$6>ftmb zA$kmsVZKNHp0@=o2?{~D9-e{|@<+jUBT$6G8<9u4IU$yyj>9 zf9ve|slS?cW4ZsEBR@+V>i&oBc`s-d)yPw0V6-w`;uL@yqJz^4yfklJ>n_!yXYG@V zQi0RWaB1U}fwmExW)eh?60JZ)TGw;Hd@iKM8S#V+4`oH#8}L3C|H$@8@D{xIJt3_?~3RMh}!R(#{L960)9rvx2f4}xs;Uy z{J});Y#Sa%meGqY`nJD4nGE~ZC6jk0R^YDb{b|m07?aJE@kQ(*>GZZ;nQT z9+WkdsC7>?93ojnN!!0uXr0;@TLsLv zwMs1MGUveM)8g0}PjcS&cqWmBv70xqdw7Qw8}$(#I<)&{iHF3NY#8jQ?88 z(_JVaw2w5&AUaP;hLHEgxp&4GMf6B{#%nbGoHYFM2H=1)voXzJwGnNZ*%a7woYR8r ze6_!M!~Dc>pWn@W0B-IBaC0AkoBL|#-!^sPXkCrFjKr zUV{dh&@jWJm1Pid9kmZ-8T6R8S`9z}rC!dKfHddOp_V0J_3~Oui#?a;PeA44c<}No zVS;*v+7#jvjB8(9f<2o3pWC*)fW59ViA9=ExDdu#7+vf0bN*qUrklhW)P3UY{Ac

%ymYKA)p_ZP{3(|f&^B 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/lily.1.zspr b/data/sprites/official/lily.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..5cb5d2aa9ac2da8f1879c390a10f68c631e5766c GIT binary patch literal 28870 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 CUKE)C literal 0 HcmV?d00001 diff --git a/data/sprites/official/locke_merchant.1.zspr b/data/sprites/official/locke_merchant.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..bfd87c7da6560cd1cbbe2d9f05576676e3f3cb16 GIT binary patch literal 28881 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/lucario.1.zspr b/data/sprites/official/lucario.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..44ce395e48e9e6b6ea86e9225e5e48d378fa4afe GIT binary patch literal 28856 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/marin.1.zspr b/data/sprites/official/marin.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..51c27bb698de39194b38347e824375021f0d3f8b GIT binary patch literal 28885 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/mario_tanooki.1.zspr b/data/sprites/official/mario_tanooki.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..255350dd8c7ec58af27e0b273f53ca5679c0ef58 GIT binary patch literal 28902 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/medallions.1.zspr b/data/sprites/official/medallions.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..dc4b04d170ae43e1d0d79f95b81cf957dcc9929d GIT binary patch literal 28892 zcmeHQe`p)m9sf!vOP55k&W>xzo}IhRUUArpln`2VQ6j~5m;)OLS)pbu*n^}vbX%RS zP%j8eWQO>UU=k>#fzcVmpfs@czm$-~7cYeTF^1W{D@6-r2u8~VlAmsKBcp(6d~_Tj4IcIx(@)_voPuA% zuTajwJMbI$t^Ms8cpJ}X@CLk#$M;|i|Gtm^e#g$9dHb~X#=EEA8$12}Y4*id64?V1 z1f|xU(Uy}vBR!uTxio+}C{kZ&SRGbx9QYuiLrlLPE67D((f4I*(*o*nUoRL%^SpVs zw6`R(?ptNkdti1TP?O2Uq`&5iZ3{5#zFI>3Ag}$@`B{y0-!@GtK(ls zY#sD07yEjFc&h(20)AI8s*GqO+I(t0#r%Jl6b+52BkFu|KI!)VW$&_g#s7=ua%q|D z|J?4*5t&dU-m2Xl>mw6tBvs{&DE#ce^&!@GQDs*!C`oWJIg>n$eHRVwl7d*fZ42FV z#k)7|7N&qO(3&E559HfOfy^=CF`vA-bSkFokQn-|TZ zSupO~^{4tj<3ELeRJ`c;A-a5O{GryL%0DW8v&SE5{i*SXT0WJ3RR6CBo-%%Jgz_o% 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 Kwl-&0!v6s|HM^?- literal 0 HcmV?d00001 diff --git a/data/sprites/official/medli.1.zspr b/data/sprites/official/medli.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..59284a366d34453174b170303b686dfc4ca45837 GIT binary patch literal 28864 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/mikejones.1.zspr b/data/sprites/official/mikejones.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..6b83b72cd8daf5ef85a12694871d94252b2580d6 GIT binary patch literal 28881 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 WaLa9=Q+uwFxr;Y*%jXB*cKvV6UP~hY literal 0 HcmV?d00001 diff --git a/data/sprites/official/minish_link.1.zspr b/data/sprites/official/minish_link.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..4b342c1acbf473e946d0052458c8eec24448d1a0 GIT binary patch literal 28873 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/nia.1.zspr b/data/sprites/official/nia.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..5d01ba4bd86303453337299b17c89e69ace601e1 GIT binary patch literal 28857 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/paula.1.zspr b/data/sprites/official/paula.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..657752ea29a0a6b13ca0e52732003bf065be38db GIT binary patch literal 28879 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/peach.1.zspr b/data/sprites/official/peach.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..7973f95299960040428ddfa2dc42170b0312df41 GIT binary patch literal 28873 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

B%MKPs+fq;mlIsuj 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/powerpuff_girl.1.zspr b/data/sprites/official/powerpuff_girl.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..fbf3c694676574e4c2e5de84bfd3986cf74c39f9 GIT binary patch literal 28879 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/pridelink.1.zspr b/data/sprites/official/pridelink.2.zspr similarity index 99% rename from data/sprites/official/pridelink.1.zspr rename to data/sprites/official/pridelink.2.zspr index 310a6b7565f0fd25e3f8700f7c1550c3d5f52ecc..662310133a5dcf2b852cf5881caba89b9e86da3e 100644 GIT binary patch delta 58 zcmccjknzq#MuDi{fFQ<+B6+V97=WN4VWNPtbOA#VLq0 NkX^)(ztOL+001kr5X%4n diff --git a/data/sprites/official/primm.1.zspr b/data/sprites/official/primm.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..e9ff2d0566e531632f8968b5a152922891bdcaa8 GIT binary patch literal 28861 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 ~Z*X7cTU&`0AA%Kl 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 Y&067KXFaj>Pc>VNk=8HPezEp{1HbWMO#lD@ literal 0 HcmV?d00001 diff --git a/data/sprites/official/samus_classic.1.zspr b/data/sprites/official/samus_classic.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..6559e25ce6e6aad3471855835c7dbb7887daccba GIT binary patch literal 28899 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/sevens1ns.1.zspr b/data/sprites/official/sevens1ns.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..d59a1b529fa62342604de95cf01dd2c0d91625c0 GIT binary patch literal 28863 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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/shadow.1.zspr b/data/sprites/official/shadow.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..fcd0d49b60f2834089b346f6e10f58ee16b816ff GIT binary patch literal 28869 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 NzpX#I@TsSR{~sl98ifD= literal 0 HcmV?d00001 diff --git a/data/sprites/official/sora_kh1.1.zspr b/data/sprites/official/sora_kh1.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..e77c922d20de21139db7cb355d5d7269505b9a06 GIT binary patch literal 28882 zcmdtL4|rSEnK$@Kx|XhOS-Sp1EGLR|^M}Mhh?T@7O6*uFb&K0ci78B*PH8M4L)@>K z#7#0p5MyiElyM;iLmBFA^MR+s&c{x97!T#4X={=TUrODno0tw?F*HpyY$mwWO+-i& z#VFGL-g|U(CAXCBcAwp6_b9RTyXT&B?>+ZD@BefBS2uTl+IGtiztjF1Lh1QT4^c1K z_&dtYw1aliZKP8p{RMT<3cAWXdIyf}rfu{^+D>1>S$_?@oonv6ZTGe>ZvPU#I>_Z} zC|~7>1a~z3sGYIuo#Sow>cP&)5BrGD{%`NUlO4-l$jooFGcG6JUR8*foz??jrCvlUR3wI@tf-1G~IDf%M51m z^mo~FD|^-FH%)bJ8@wJ*fBC_m)s)`-vje50x95%0(YwoDI(nBM6wfb5|JeaC{&Mv9 zydlP4j{aQ>#rVt7|J8%vUD>59SG_hD-#E(8`t~kfTJ9;}HhNC|*MVQ$l1Zh^`mAwA z-7)ab$y{xv@J>SG{PQLiefqlIl=};=l^-D50pV|BN z^J8P75fjVnSCzRO4%A!MF9T%|Va#jz`Xj?h!vMZqxc)FGk?6|{wl0o1+k8HInT^li z-1Pa@wa%p!w7cw#kBy%n$#k}E@TR$``|50+E4G>KQJ_yGOz!e_BW$*={%@9WL`h3<+k-VWIZ`b6J|7> zt^T@xC%sF#Sh|OCZs(zqzB&)(Fh@MVU#q#1-k~hzoIwxc!^;l&n&;r^F7fDh(#Y^LuG5nu@0J_Qdm}3$rMsI?J6EN-3_)V+Gf)&>rjFrpdEC zN=4ni?(qUv9IC6_+2EA*#h6#yfPgSNqCF&J&s%7D;CXVH=r;uHB zg}oEr=Zf3^eZ{|*wy#K46}SIM=)oR|&GsW+6Zg;nO}Kwoafg3_$-jDNN=vy@e-WD> z3iA0*T)i^%hW=u-&0uT?t_lT*Bb(YL854!n9`~Sk(3>>RC(_kUcgmY8V2@T6s!CO) zl(abCsa1w5Q=U9_QBa9!D{S$`e)D~~Xr9ufwRu0=wAaLEuX_KVvje%+%)z<+FV_C$ zvv&>rWHoc}IqE#cOD=pAlYWD0eN|Gzh#X^p96Lf1|DJ>GbJa%v)#n4qLd zzs;3lSDS5P=wsn6zA*Y;HgC0et?NeH7x1nbXQ?mIn@F4I^0jAn@7({)MA+<$@zB;M zZrfpAi4pG2`-W?qc_#wjdtb|uEUqQXy@{Wu+6)W7>v_-J>uAzy-8>tJQajFkVV8HA zW1cx%h#u}a7HTujt79SY>bw{B{NQM+*_bqE8_~O-UG7%2U*-0T_8a@IU4{0mNMLX3 zjMke-74F}AuJ5t&k)DKMo=vpgzInmM&{Cf`8_!>Rb5*x%X}&elalI8+&%ZXb=^Y-& z)h(P1_IUjcZ(#&dUE6}SUUy*xl=QNR-h|bIJKa1vKC&AlD#{%TKil4~&aVr)1poa) zTg$dRYNh4_)iFPQZn%xMFFv9k*pyBtfj3WQjW-h+bxYJ&7}YRsQU);@VnN-czYujR zOntGSpkIsn1pQgm=g!}TPCe=%D7Zks)Tv?ycvE?Lc2X56xQKqK5)>@yS3C*$0(gl0 z&c92sj~3JJgjMgOR@zX6`Fw?0ZlM%og#xsUwj^XFu+mDm7BC;T+e+Kd#%;#dlJnb) zuKT#1EQ+I|On&Fzxc?HYCC5U=^%5I+6B9H@J?`r&xE?r~!kp`LuP`wmQ$bO?)&2a{ zEGpFjXC%aL1uUs}{45L&kLJ=!j9#n34S?+dT8`0cH9}sd{zd)*{X*+n=2%pqUw5h% z)SBdGW@%23r@}@ukBCORaaX_=Bsu?{?z%Hr4T`SK&p##Y#`$TI^AGf!9>bBb{QOg0 zb>JY&tLs%S#@|M&%U`+LdzGsWbHWY^4jU@CoIlAtNJ}P7>`$Lc`;x%8FP!gavo&el zo{Q?5Q@O(1Xtrk@Pc)Bl`wYG1zrMAVI)J&oKY5yN1O>Sreg|Jan4f`eN1b_ne|`oo zH3HD~toD(Y14&=XY7bqpVuhzklkLlu`4wwCS2<<-M##NsEd%K#u0N$stC@*YlgD*& z{aW`T{|XPk12I)cLVM-@50|(-K{Zq6799HxCT&%4* z%+VwAL3b~AIibt&=n*ZRIPW zWC~oC$C&@_9&ZRXgE*jrb~}r(n*#d$WO>S!=NE3O(QA`l;3)KvL5eHfTEqV+uAHI{ zgV;4B`q=dI~O-be>t@oCSZ+<8t{Dc z1O%?2dJ5WtOadre|0taU6^g?3Pf#|I8TmTsZ0naR1yw{U$w>A?_YJ4Q0!b11kB@{1*!D0oNUATw^!ny4bf z9Au^O7A-V+Oc?XmY7KftYoAND#&e=|7ISB_-iO&r)SNz&4&d6GN#H$dn=L{coA*@o zoBBr2H4B2RjxW*QlE6npTZS@pZrPd6ta&}p^ZO=SjbG5S&`g-;vCIpDgDv$fGFf0x zFdSSGY;Y~HEixZK0nG^>C|*4fI2iLlfmgW~1lHS^S1t0d2Tz42R}~7hI#$^h+SWtj z5S`SYh>eFV9U%BjV5)b-(gBv$Hfc*5R(QfL!2$GIy(TojDX=Eb0fNbgdjiRw`_E`+ zv;*ibc-nhcU-AdW_QduC<9gMH5v*5h-D1pwYMpl;xF2X8wS+g$Z_m=dGNI-%zI(&0 zl|Mohr>(GBBKZR$xDdK1;)s|w7$0nub%u zaJh%^L~MM-IP~)9Cti4X=ZD+>%-M?l58y9F{)b}U1KXmErSE}l(TO781KXmpBHshs zqVs)ibM9^2d(}+$<$>eTzE0QNtLi^{^=FoSW80pAAI7eX^6cI~ClXtt&D5xE2r(X~ z&F)b={ggHi437f^o%7YT+N$b)5FI=c)ZnbIXqvO!f2%oM3Fc*qj%b)Vbv}>)Z;opR zVM9&;n;r~TOL?SXN7Z|)M1Lx+rt9oG=a1usV#E?5J+5V9rx!oGk8up`r|3ZJ&DOo= zSDD)HQh$xx&0SNIU&#L&a2PI3J?GU^;f@e)73I9jw0i_^SKtV(QHwQ*h_lI8tUBl`Bf$ZXv8946~JqWzY5mbVT*DkDA(3y80xL6el>jU=h@)j-EeB!>|=)vJ6+P}K| z>}x-5?p-)an?MOfZ2$K+!hR=d|DUbCSzm;=D0=t|?EmawTPL(#ljbjf=eISmMx$wP zcmwUF@4Upe@*mG3JpNBR;|+8D<0ldXRruB5-1cp}-I7{eGgRRx_DHfgPZe}IpBZ1YuN z&A`8qc&z*Rjtt&(480L`Xs3E^iM1N+KjHG^!0}t|R)f_+ugA%gZO_zq_U!R^sx@c< zE^z<%-??=6;mF~kcxMV87@{lA+~05XZ=HO40zCo~&3Cs2`WL;YegEl5_1jyCYCQ{r zw=O#C|LmDFuMYJSbkX#1U#PF|+_?*{1y!Qq=%(SLBZ;g#d-d^)Q$!J4u4`}0-3>3U zzj)VmR}!_^hP&D$cLrbX|7zcxXVIRfk_QHl8rk&4^Z&Szu`jhP)s}LmD>J|9<2xw! zz%efNz%h=Q9=Mr-C*gr(EIe>K2hPF+$2el|3ckH{m!m&;)?`5I?Xk#Ee=qEp36r6X zJ~jPxT4xXDo1J|Arc<{ZoYdKa`Rs1S%tO5PAl(ZOq{LOgw?h{en0=6O(*r3m`yeNW z*#jvs|Fql6>}3UhIUC#QZS{YsUoD(&r~g#fs2JX&VnZKA5Hq_J2a~Ie;`(5+=_P?# z`{{9)=wHylEX?x1D#RL;IG^QzWr#JXuDSind-OwJmn#Ae9j3otw^zy1X~=_!4<9<( zkulPtbTSQlzFFz74g{03U&PDeRq^Zrgbvk35rp1XT;1*}RIq*KXet&JhJ7+&m zUsrC690AwgR=dl6`TC6GJ$E+qOK|Z3wi(@yU_pK>4o_KSk^Ej| z1e2^oWtB^w1%UuHy%bD((&y6^srMkS1E&6ESr6bJ(7!C}0h%A$wn6`TD{tYk_HxwJ zzx*aCtWiVWwXjyC2V#xd7+mAAJP-ptTZUNwVHq$0kK{m)dx-TPmH|VcI@Z})1C;Gq zXJ-vidN8V!o|FgeGbAJc?mo3Lb0po0_ACMJwJ-I3w6#T{qdw=(iJ3!2`r4=GDEOO`T24YK891`e3KG6WW)+&Xkg>IA3wTc}$$oHb_OP!t@}S z9pJ~G<9{pVekVU$%Ke_+Be@^PAJ$!c2_nUcKfHG4>>POy%vVP+Ba1Oxa26Imgi>sV zD{#m~Rb}%k0&ahv|GB8bb%m$4suuiD)54*&c3fS)_+nz_X`HO7p(*Wr)j~;OR z;R50hn=}zU;P}Ihh(E~a0ntVEzxj5#*#{}?wbTQ{!C2ttDrvthHlB*M8oh}uJm#kT zX260oGDB^XGqeO2&^*|Adz$x#B9-1U7kmbOS48<_>y6FLY6YmmLBHvIzj58Gtv}5+ zoHX9{{rA?#H5VQI(eeM@>Ig4M&Wo;REh<5=M60oL|7qVDEeQ>Xu|GLCxg@&W`0>X? zgeF}vs=QwQyNVy&Dbw)ZHI@jvi#G&Zn*H3=+;W@{mXQL+( z(0nxd;8*Y5a#;OBbEzYu{WDgp8lE_VJ^3#v*>o+Q;np>34@qNoS=h(BRP{XQxiQcIgPwWaycAcWT}F z`t;SQHAkGV6TQTmqbM%&I=!YQDe&#{Z}L1zUU)_w=7E2lf5Q6+B2Q93-!{m$Wi$~@ zB+U9}jCcarqyj$hb@x?sO8pDr`eOgWc<8b2@sK|${R{39fA_5;r_$_SVER;{L-^pI zTj~p%{i`naa9!nEEd664w$LsA+!%1Ne~1&34uc3Pj-N6X@zaz3tZ^ZBSy>n6`04h6BzoB%^DE)7)!yZ3T2|cN;aCRD z?Y}#4f-W0bdv=4Lxc%RteS7pFd+!FKe&vouhyauaYvz&7=5Xv)ZjH2TyQ{n^kKf;U zXtb$nqdOP7XdB)CTlz}u#2YUlN#x!8hQ~BA=^xeg+|diuG4FeD7h? zf56%&kw4WuH3seLHOOto!RpqG`&t+KeQrf!pKtMEpHC^^ z+;_4M#XC}o;e=pd-}%l%4|Q|^y?noFaGXfyHcHMSc zG}_%A4!hlU;OzaF_X|OJk0oQ={{H*z_Js@g?R)I8n2Gmph=jU6?RB@b2&Ub!Ar$)b zr`-i?H$3TVO=cegCP~~blr1$~Q?qZMPh#^R@*tv1^oIr_g>hjD9~f{JaQ?Ip{B?R? zZ`@CPK6%2veepQa+ya)zKj?QfnjG1=Ay*^oqtW4!=!TjCR;i-Kb$8Q*tXIiiW4jxU zEKu2>mw!~8z4MDGXY*LuJKqN*I@p;RvBc2}>{Tl5m$kKHRljr1L<}Nuap&2Z?;WkUST+*`HZV^nwb7wSFcRqh zJmXYiDzs%XdGbF_JoU>QWf#dhf1aME!xj1+Wm+qX@e z`-fc54v&kLE)})^@DDvboY*4q;UDG7vMV#=;7o3A{FNV-m91PkUckct*GG{nBl->0 z;t<;9B3it7h3+JWgY$p-t2Zi#5c7UFbYFt=e~#I*N|fAcD3nOtsqb2m>+0VaS=6Nk zF#?~5r{ts28=_0~YK=$WPUYjuv6pkH?2(hsX~i(!q!~JApQ22=$Eg7GJ-P9@o^<~D zd!@&lcHOul0xIR`CQ*0rg7*{1n2_xXQFF-Fs_{jN{C?sfFp1m1PuCEM4^{$4%oz?;Np{X;jpW?&U$v|Ko+qey2HRv4E%F!lxZe1j@E!{NJ?{Ody!SA#zyBsXp37SKk9B8Pj@z#dP9)qndw^c7rTBr&RG<8Mw*Bp4?XJ;rqB{qZH4zGow9z~e1r$`WOb zTJ5bU!qeL_qv^qe+H{9dr3QPWquqm3ozHLbo4ODCOF~|bUM(_X2r=Z0guNWST0|T} zV1EhAaE<_Q41r^|{?xSTKjIid$a`JIUnu(>?}0kmf3V6Ms)#G_yqW%k7~+4=cK%#n zVH^YXvz;=Y@^l7tc;51M%Bqz?#cAh!B~d=rH%}>ZI14zRzgSnAzxWRB3`__G`nS8S zB>!>OvNf~vAKx8#ytsYGjsdIv$J$qwWKg#BeX1mb@|mYR_S}~2P!=ewZpK%B9N8+{|>UE-CP>+GV}3 zGhT+UEDjoGqHh$zinpD+{FhHhX%*PG9qI(`!jxE{wb02 z{sni~-@Pek+Mf<(b@}E6_XeKrx~NXkiNSvTlAep-9sh}Oc`U7MA36C?Z!Gz{_}`@? zsWqm5Aa_OIk_hr>~|fA^qBHmm7bjKHPCa%O+3xn#bO$d!4q@-$t*7 z{5<7TraT3Pl*BCJ96b3Ti1JZtf9gSrS+2Lx52M9+sjdDcfm1X|8lwHWFf!40D#7_n z5`VD&e3t%E#@Nb#o>@tBA?a{_#ei)VCY=4Yu@hQwco>?rjwl;$rS3mpNaTmA*_i2LBJ z2QB{sbFbnIgxUEEALc(lqe8b)hbj&zeTe+QANRTUZ+nj}+J8eEO}wYz-Rh(Ls6Y9D z@NT^hpA}=v{s%xQ_8&0L(mC~A^>O6@=$rXtHkR$qsWXs(DIAH>`;{{_L-eGhOM!n2 z=bzv4+xna<O7GcjL-Wzqz4XUn1^J&XNK_fUUu+@WwG5^qm#N2{=D8}4_#IlEC%sp6G z^iYb+`H@i|LtP`@p2B>MQOY#|FTR|=PAX4UAr7GkrU^OXO;*B-FR|5w=#R1JL1Nj1 zg+&h%%N~j`_h4bsLowzaBvxP#R4OvxnmsP6qg;{s*vx-C$QEeM`x}52Pu+OvR2~<4 zama7tcX2+W=)@0k+Nl}Mg9mZ9ioY59Kh#bAr4=I}h7SFi5j2^< zC%lXC#3|pAa2(fz_LAO`9!hRW93jR`bK)L^zk_K`+=K9kFwHR!**$P1d|VGxMT&da zmU`Z3PaKD3ctwTC-;?>*^D9LDp3J{)Mvf=vPs{x4jG75$M#8WUl8_`sRq%szAM7Dr z0mG@>UP0W)RjwtrHK3aDP?%P$m+g1opEb`P4^2^4y&SqU(q?dbXdJZAzZ8+)R!2Xg zaVAD&G3;t_^)~dv!5yad@F|~}17`Z)faw(Z_2Wj{P}?T63;YoB_k1DW&Ey~@^4Cps zK}qs_wweNZpa_%c-?!?2A5X!bUtB*0f4_x=C%Xj0lWk$5!B~eN+Wf)6SZO})?0nzZ zI6ce0XRjES7(I>18)qyEeiOs-l-k3QQB$8lv>myFR{kPpp5`^9?-u|0Q^pVDl{7DC z{=oPx3rt&BRq8554x2cpDq5v3!>Bl<>5UUd!q)kku1+WiKd z?9Ozb8j|_DY&{mo2fQ@TT^t|i2-g1#^t%NFwbv$5|q#b(P%1TBnrXSk}QkKz=VY zr(#JO{r$Ct(1TMsc>ox&U7jNc)oHEUMhF0 za5<_D(SyfUPL&u{cdBkm_#e{54tIQ?s!I#|*?>*qv56j}{8Vk+L%=2FC%>~fQ&s00 zPK#AYjOR{Yk^XfmkW6AQ8|#JrAm?AWUgSSZ{e!Pwy#B#IRl5EG`7-Fi(mymW_pk~Y zm>|WvDdr%(1^vT6#rnsAq|`r{X2d-R{e!W%2cdtccz*6d>K|CkfF3OUV;OymUZjs; ziEs-&MDVvzklK6*zvW8^d0$I>ZCFvpF&;a#z74d_w~*$;!%zh*N*o%&loBtjKZ#Q& znr^qSSS#Si91!R8YL;fIE8q}TAPp!J@E?kLUV)TWQV%Cj3d|C-DpZb~L5V9sXx;+W z^Xvbl{i0J;tpCf~FIaCt)%rCPKhDcP$W99C-dA1UCo)XyXAF1HY|2kyBL$7C8`=9( zezNUR6+1Jr9X5vwp3iZNd`OK;QGgt)%IPufe@AMq$y_0WxHVSb9@dd80;i+?kH0nyyA)9sM- zre5X_s`AZ>^TVKm)oKK^A#ppcQ?I1?#Iw(e&O^SL(sKzcdQp6M$ENgj^XeOfQ_R&f z?$iZy^$k%nZ-2}AlQOf%{`B=j{}GVu|Af7g>XPgK1pZN2%D<3#LsbW*{1e!cpW^&b zP|8n%pM>?o*>BSR2~PJtBjl%OFLZxv{nP(h`{Tpb@3a{_d!bK4f+X<-&_%F%iB~^` zK)0|z+#4L9aNg>OU?pMz`hKT7?zqQ!uOs4H?_=xqKFQUK#T6pW1c}?tZ!LZz-8b*;8YSLKZREdO8tQ~_hQVaMH5*%nm=FQ zLv+a0S9FZS2o1XB{N=Sn><^KcE$1pK$7&y;ubbJk#H)X#{?0w@*M11UwYUKHFs;3> zWLg;W-IrtRey$hK|28^dVt#h`U$EjgWbzAOWfzHxvyelE_@j;w{w<(g#_E&@4^p6S z1G8Jj$DBT96B$Jp?X4&}W4Q+gN6{I}Juo|k z`(9*dGUg)eXSx2LF}#QSQ(qIc{FD6;C-h@8|35v;|6hUmUp)UjnE%D| zk0Wv%p|T3_EdRqmPpSW5phx;2c=kFmnzB7(&MFs}`Hx4tKfWRKkjb!t;r+Wmek62X zKK=o{7u-3Rr#aUBy^Nf}yrHgO79NQ0+`n_bxIbWJe~vnF^4jB)tMPophoO^6*ZW{eplLs^gTU>|n$yCZfD5ewnLKJK{Ly&gE55gD3& z9hZ=y=}Fl$B17|qv17>4tVliL(SxhpVON*l6>i5Gqd^Z#@HO5|t{Z3z?o;?5tQBzV zf8h0Zas?dEzg9;Va>&Ex&!{&+EzMhK2h{QUKOVzCua|G{F_f zX*ob*1h96(k+CDj0pFkhda7MVuR&M+C%TEgZQkLB$&W+PSHhl@#sAkGv5!8Uw)j8u zU%3XIIX;l-f93cwV>jDdr*`hod|CxB2joI{=%z!8_f8*IP8IL3jLr# zAf7>e33p1`A3THl_E{M388gm{vt(h6H8Od~?a#pf-Y$p&XM2=0bsG>{Vg`xaN98Xo z7Gd{~b%_75?2juw6}mqhSPK6e+Zs#gx#&HKUVIqW&^l_1_Koz8oF5Pu=uwAY{qPPV z5>F}EJpnt22<+AZdU=X>5Rte>$L0#yK_p5^--P>?N;!_en^GHk@_0SOfYVnV60r~A zKY@cSBw`;dOYE4V+ZVsD_19+a*|Gh20vT=k=dasQx2^UodmqX6|097j#-r2f%tSVs z)!%^vX3*2ddEfHa0?8Bl@zvsfnBR*%kSr{+s2Klo{x7rpbI9(05DTPacE5q_{<|(C zBfcoR|ITP>cK@MC?% zU$t(r|B1-XA$SkX{ZF7zi5@w6cwA4D-0@>PeEi_4gF`W7rI=Ujv)vHhtK?rBj_fVl z;@F$-n@_Mfk9j)s7bNEqctIfKZL4nb%lY33+y7elPUQY0hoZL*7t8;)O_`D%M1<~P zU4Nmi1^SeQS@$u-rOcnK`xN8;WjW}8V|3VinC>dy>We}egz-N`C#kbipZD{`OlMmt zXPnem8!Mp)_AOhk9Hof!E~!{J+H)!D_Z$1^&Mj*}EGZMf`stqp|)i`9EkP zdM-Lc$nK{u8*{;O&`_oFt;@OS<|^z4g&F4t?gjPiq-Psvk{KF`ffpiba)N$AbCqct zh&>b&j}}rlDUVh14ki+JE2&D}!9-wd4oEEL0AtY_^9>(?MeEd#)DFf@AMd`y`|Sw* z-Ra}qcf7#<6lT9Vz;hL!7&sYbzY5QVm)rJ$la63aUf_>bQ| zc#f~f{e0zM!GEIrI^7ev#pH)RKkr$@(sL`ULAGc^!M5PiAWKhS-~J%lW_=j+rmkcu7&hi>T6Miy}-Bys{>k0=^^BIJdrBMPxv0w z1F4>Z^yo2tZ&L3f+GhIREX>|}j(+7CKe6|IG4vLRt(9-$O<6171l}^l{39*^em-Sz z+|R-(M6HYaNbM-y|EGD3rH0jqGUgr`E$Sl~++E50pX6UOVgI0ctE!e`Mu{Tq7mEi7 z`=yizaQ?g8!H!!omN~E%_ZB9`-QhE&SDQpap7Fv;=+4y zSOq(L*o*i7VO+ZZ56jNdQjD>y3HFk)>KRuTVXjG8)#f(~>$Hc`$iS2*==50{at#)+ zLT4zL^cET@*hQ`(S)R%^zj#fAcdt{#`uU%(cy!?caAR+N{^eG`_*!NtoxR`Y2-EPthqen2lic4k=yD+ah^jV$#ivhW27T)O)oQR(h~M5Vj`VdKh@ z-T#RGs_b4n+u+iF5ijeV+dHqtCH)t3lUZo4>yF478%QE*J^AJ-nf(@5+8uv`J-m1h z@!RcCdL6xv&DTb-ewlGVUue7#y=NcC;JMW3ZAP1M&-0lPfjQTU^B;K!As6bka-Xd+ zxEk^I0Ad&fy&(GB(k%RricmTR}Um|xjc$)E?^3~;~?L+0o?Wd3vz7efgkBRun z5FMia`y;RXA}RC;Q|5r@Ngl#j_#lP1iU9VxmV_n0ceq6gU?dIWI~67zbgyXhv~!b{+d?aE^fob66J z3oA|(tY?L9H|NZ~!ahg-@QNLlw*rTPzWfSqrXK$A>K_C30=vxxEsS~pD;a;|mGBkV z|BCHjj=xo5Mm(>I3>Nlaar|uyqWPS`!X7Mx5{P>84qzgN!1*hTi{o#+>*MCc`j^+f zk~ObCid`T3_OCa7f>rH&eb!%!PL8EcN)lk-m}UF^(aK`Io}}XT|ZmR8xFStJjL(@!0v3ADlmwc5!=a{-)Q& z*Mz**3VM-0WX11h=MViP^tLbV>Zgq%;lKTto+sm5gK^hJ&?-;nGunh6GLjf+QO-=H z^a-()D1Su$5)>qnSdq8203`*<1CoLy=HJ{yap5(6RuMfEaPU<*oM{wZ2~5SBg2 zdT|d{`_k*l-ef+3Z2kM!ze#5b>hVS(^O*;9>HpI6jrhh0uj$jE1>{T$VtqSd(opUd zi-Hok8Rj`(?fG%I7k2nO#bxrGV?E!Ce=5|d&BOd>p2IZQlSrELoq3MggD$YRejFBv z$f4ouGX-yQ3Cyi0b?g@y8jK}zJ#L-W(?&;PkQnpz`3cgP11@0ZdECR>m;;RY`dQ?P zw;vRHNbvQu#wi+4+?qIudbSq=(3Yj51n;4_f1$+ef8zM%Dc?{mhVzk6fcP8lzBfdS zS$6Xds2sysi9JM_mo5pe;5EnQ4D=^u4?^J#1dEN8PKm;vFy*E(w0l zwHtnfR$`jt9;6-5H0Mrn4uR0&`Fi{W%)QxT0c1V_OMk}2*XOq=ug_~bHFE@Ul3D(* z)1M8r^TGRfbLUtClo}s9oy=v#wPFbszNbLik;iuE4yvmfX>p={HYU(s7PIh}sK!sm z5$9p2JRnz3=KZdbuu~J+=8xuu~ ztMPp{w$G}!gWjq=yaP-SemABV#*LuAc?wp~0k1RZ&$NIIo`Fk1e}3o$r+o0WgMN6= zG`WWscRqXn?)UF`^^rT5T-JUtkdF4C{wsak55IBvY|YNGgC>^wGxaKG?;zVWpFdL% z?UHd{LgfD>P3?iP$o~=C1NwG8`-LO$%)c<33K=-OiuqW+2ew!e=N40dd={hSeCI_AJe!85y9v7F^zlZ zr^-EW1fP4ruD&Yw(8fK8_IR!l_g9^pZOFpL{jwem1>es@ z7K#kZ_IL!dsPL%G$C6qL`;Z!_-7`>U~!BXU}uN$1n@d}Py+4{_U4++4sy z|F`sQj-gj*uD6QM*VH*`DXuHR>^XO9R(&b9QMJ9+y{2$JV`y>(dmxSRdn)==RJIQz zj)9#}3YhSR5#F_1@k64I={%kOt9HFWlZYms8B*A zaR?rvz0~j6n4jOkp}nDA_+dri7{Uq48t3!^cAs#!fd{hx<MP+5BiJV_ zp&LABUH|5ZH>U@B*rUhTk5xWbVh0eg%y+*g??T{a27)tHB@Me^}(t08JjB3M2njNWVQQ zJO}gHdgfib9Mn_USi{;k-#_EWW4vRPAKW5lK0zQBJRLYconl@X!xPlHo7H92tK9e6 zcW8UmFrvt-)OFR@`geJ@IrhNn;vDfbaRvkLqYm$tHqbMvPfuiwX&gs`0GxwyUvIG{RJ#U)h{F-!#fp!f8lKVNlzdA5IkLMw9AJ0p6C6FwC~0BjCa~{ zx4palcb>PghaXmS&>{V7G#AaKewWJ4zz3zDi2cjQ4}TmE?=-L9WE0+po@E6Dh=b$!he zr(^rEf_5gs7`)3_Q*}iJ@5L;!R#jb5<6e-*IGV^{$J{4GHU9+ebN9J_9OVHw3O98|e8=_6dND8x}UyR7otoCmL*St_L1!AL8G37&Y3Yf1`VF$8eYEU*M4)$vnpX zL&~q`V8>4IA1(q{U2x^G>|tP!sF!%GfSuS=_t$e|d$!>loWGqT+nd?;RSSFzt3`VY zCn%Z#=Z_BR-Gl817apID<@NZUDwSgY=WMKDzuy5?f%9cH7Cp#X{>?oUV{Y+57^|=$ z!)x;S&a4$E@V{A@C_zugS`Di`<3+~E#vSx~q<=$C18i_`D z21pL}ky+S|1R|aTl7pSgyj6lnHg2n3S+%^nkpDXiSDjluwqa!VfLy=gorMSX#IXZW zbEV99o0^3^sphd;ho9*@5j`ybgex2Lw8 zTYRp7t18NzHYfIe7404P>nX*!{osa?c%oxuBr5a|#_7aZ0hc>%9yj(Ys8njIcn5&l zxN)Lx>OkzCeUEMv881^4&rcmM!kqKDx_nJx|4`L6*L9tv8S6KNDeXth$Ah&BIVElS zczM_Tx#v%(=OzDxg z?P|oI)@ckb4%VFYO37UyTE}?je$Lx>BadfU>b0coP_96>F#PxbkF6i(tMcln z+T!QM`Z=vIpTKFc`bp;N@%g;^Ngd&D|6eY@RDM@+$;qC3hxr)kuVfxP@8vyg1!kLR zOJYmnR5TMEjrJ1s=V)&tlNcp@e7#6G7J9ZEgZw5!64C{2X7TOVyY3T z+`s?d;e8h?*n(!82?S+C2EM?&`uB-gUt&BYB6wK+8{0g6XnX_ouP|Eh`r#Fx7DcY0 z;q}9apH)c@yjVZXJqQk9t{*mgXon1s(=~Ow+`Wz`V-NNSQ53%7#`U|r#~r^hwR7y( z)p#`2W?XW<>Jj(je|qQW(Nv4^I&|LuKa^a5&hfqKCszNjbrA$YfF0m#b3=^^nU=26}-Iw literal 0 HcmV?d00001 diff --git a/data/sprites/official/stalfos.1.zspr b/data/sprites/official/stalfos.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..d4787a3b0353f4c8060a7240a6a0628d65b23421 GIT binary patch literal 28865 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 LEcrV%->Lb3%+eTY literal 0 HcmV?d00001 diff --git a/data/sprites/official/stick_man.1.zspr b/data/sprites/official/stick_man.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..b891586f4b6563424f9e505b87153344bc0e28b2 GIT binary patch literal 28872 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 N*o2aQwC!O0{{dnh25kTU literal 0 HcmV?d00001 diff --git a/data/sprites/official/superbomb.1.zspr b/data/sprites/official/superbomb.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..1ed38ae3ecb0ea36ef4a1ccf2a51721e51d37629 GIT binary patch literal 28877 zcmeHQe{37o9sl}AY&X5crVZ`2bz>*#k3i{gOP8iJBnGksL}<-m6=PZBRwD}~D_I#` z)Md>DMOvj*Yhi4I5ka(3hNekW9TFWH>y31SKdLQM)rL@ntgdP+@gt_d8g-%G=iT{y z&vw$x_d;E-d8hd9y?5VxKfXWS_xpX{yF74jPtRi?V) zpi(NEUAfpOh*6w=M~y}n=cp1KZamBt%v|PS2JA_^RRA{|(7rv1w~F#`L%0EXiHi3) z8|f+V-8s}dJ(A1Q-*~vc+dg97OH@J5v|d+~lbbJiio$%B`nxHXRv?&h(_Pe{QQmd5 znKt5Sc3Y{9Q65IHKD>%Z1?9bj(e;Bmcc4BD8B-0)!wAOhruVc|CL1lR^$&sTzcutkWGgCB68WBmylm7&%fiM@cjH)>%W-%677F8<(vKsUH*vn zUv&P(+UMyvAM2m!`YkH|^54Gb2*l)H{`p^w{zYd`bo`?2i^7ZQZ=&sAN$_kXH&EY}{pW(kPSUIi@$?&iWea%q0JV0o~dU^{u-E*ta9y@2Ce^cHX%1pZRc>3e>}Xd|8n-@g#Mp2`>{uK{S}87 z)1O53pZvp%**~KFw=OrP2U48>%ziK}Wb!1@Cp64eOZ2GZdzmYMo)hM?EU%;$%w(p0 z21M!CO+`40;pMgCuq6+>O+ReVm^aaB=+PIc6LkUdjrvAo0s0unCzPNB(@Xb5LwO6@ z>3$qcFV!?tO-u2`ve%cwlJ5**Yp{Fjr|YRKiM=vUrJrgl{be3Dx}}(vV!8%8T!4CU zhYdPianUu<<8TQch|w6GfPZ}m`z+6(KB&|xWoIi#)H<7LgMp&!4^_B*6a7GILGxew zZE|>37*8D4?Mk!v!j&WaKoxn)2<38^;!cwsOoyHkr$=U ze6NoL`es(yU-743X(RJdtyYXZ5iga13MNaRz%KZJxJ^4*GXqk$C#W7|QUWX{75?PO zxZyoC^frA28d9_(y9$FwHfjkhC>W^k|4rWk<@VpS9Z=r?Z3SMR3nsZ7 zu)`-$3T9B9u$uLN;UGXY6V@xR!c_cq)2CVW>GSsp>W%$VTRAOaNY(kb3G#6X?L@t? zf2wo=so-B@{Cz;|MFyYTzXW`My!`z2YW3Xw4JU~rt5^SffjEC<=wH}93}0&haAb=8 z!yMsD!3-gt6(<~Y4E0V}=aZ%Tup1P@UvQEeTETyHCU<7twUWL>3xQ_c`bRY*L@td} zHC6ys`%_qXjQyDDGfND(=J>}8)Ft_+h4#`iNIQdnS`PLz{)w<*n$Nh7Ws!_$RFvy^a}Cw=1`qak>v@9* zEA(uN2X%O!2b1ur7=k3grB4o@+abASZ^#=0zdHi{8&-tsLe&fo|qbn$#E$z zVZ7>C?V1x(omA#3b`@(6c7QP0;dVGZo}wZLxr&Nht|FIB(g}OFd`3PXx65Y|B+H4! znP<*W`_RyVzV>9dR7@f}VrtOm8yr+AIye}O4n~!b@6sLr;%hh4K18LtCv9jKU7%y& z(8&_B*_-Ky;F40h6}$@RQxAqs88VfK`8Y{WfVSfFXB0#F!1D)YN+uRF@*7$QDY1bT zpbRlP^-%Uw3J!}I`H+4A$hYY%>KQz`lY(cDk#O8L30pFpyJd&u)0mrx;XG6ws=HxD zz>n|>VmObr822*&55yE6(u&ihIzKQX-bCE1?M2Sfqe%R6?r_s_!(gS z0DjM4+(>=xLkHT2`r6z35<{}Agz;QeTl(-cqF@w`M~VUaSkVT3(|~_BMt>95wra#- z+>I6-BsqNEAR59n*Js>dCkDt9l+gk;82Sn%8I{v9C%xU2%@(@2P)}a zcQbzI_9@`s0M0K3FV_3~Luct$j-<)bw>B{acLxdH%7YzvdfWTz+NF-{qCR&9K>8V7o1W4JSbp3}@-@hhVok zQ9rT%F3!LCrGHWW5g)&p{vq1_^wV!{>wo_FS8V;{*KaHO2S0u*`hzIEDE)}T&+vcR z`&U`=_ltdhDcc^%t%RpPE9-Z<{IRnC$FHBc!;AKxhZp5v@%h^bxp}p#m zywz^!bIC$QIT~&c*88;gVTcqZ+!$O|w$L>z{rOj8&XgZI|6X4I-}6r-I(jLgCKMcq zuXvt5$8P#ryzf6m1N1D#|G0F~HSWAltk-<;QfG5vwz90$X`im)wtAN`b`roYph2vn*? z09wMqus-g@A;o?1t~U{d(~gLYy$F)%0xe|qKS%j5DnEJqJ0JWro%+A1{a^)tTKQ+K z4e#sdsjsiCt$x<_h>_RMA1K)G&{JREg$q6Vck1y?Mxljs7X4jj{t>0$e8ZO_wyBp^ zK;AzJdH*~x;I#Yg)$<$I-|km>8NL)qf(W8lWyGhRLcM|=`N-g&E<`LLhR1fZt;V*y zAsh(W>T$JjQ)lM`9rIKr9PMtZE8f|uv?`s-?s5B=Q|ZCg{o8749+=nRu-RSZTk9g* zYk2;#qQCO+*65#KznJ};oBuL>{VP29ee`?Cdlo}iN%zD5uXHOdEXF_F5L_&~U7(94 z+WV_}u=n2u+_Se=onv%Re+N||V7(qHKq_@?s`0e7_VpPBz-1f?*{m1m zUs3(}>V{7t=BmBO_=3@H6y3vwn-xzzqD%49v{H%namb2J)*v^XsoQ{UuZX$kl$BZungF zpZs9mXjNSQEo}W;-2M>Le?;kD6kb&SFZ}imPOx?s9e?5TU%vTYtbLyTt@v+5@n?%AAf(at#N(Rx{!C*(i0D#TC^B%ub;$mjNYXq@)6(L9~E7w9N9V`#W%mO{GIDg znEkstuSEIBod4PQj#d$w^Iu%#bT}R6{14*`GJ|B!e`Jt-D@kZWyi{$)yp&Pk{JJ=n!#>6YUg53WA;tJWSx&zt?9ntom{J0hF?-~7Ug ze=L7Td#Ltfjs6eQD|9T-i0$$&pv%da0+ z{HN!g36D-aZf$74T=~jfI`!!J(O|-NPHOj$C&&KvS5_WB)UtKJ$eZ(DiWwt~{&oBn z7qPrK|6SS>O#DR#W2isxPI&LS=|NB{yPN3g8xL-5S=+SU$dASn@#Du{OtrV*(~@(m zKiE2Aw10kn!uubG34b=Q|HeNyr^COe9@={vBVdWa*T;4oJDJSS!nc7u_}qsVI4qaS zC9lilW`~yo-nk`nOCr>*9TNSG{-t~ziwt_=cO2~4?;LxhZp(AN(FVCeUXD1K+1hA{ zmdi^P+_Df~giGc})$`GRM_U!+t7uE#{n@rBJA1(Us@9(de}lgY@QWlp)~?E5>91cL zpt;HXXm~Vuo_6`9UoSrfwq z2y|dFU1qb2er%l8tU*PX-7lNx=jTgiHM+CPLK#VRbO{-=*@$@v^>`yY`VcB)B+aDh zdB0P)s;e7a|E%wQuJ7}9Liexg-dne-?mhRM-#PcrpT1}9O@R-8w(rh&6H?E+dx&}| zz`v1hraNgXZKhjEr}xlCAjU-R!1reSe~)wQR{VC3-nseCdniQVU~@xz_!?=}MYG2loIQIRm&5JU(Oe2{S-Jd_sXWG$jAMb~*m6xccS#5X0Nr^0*92y zJHsW_P7dOO>yc@kq7M2anj4G+`TC5LmO9vQtfBOqUZ3#v#2u)#4tL;S=MI2z2SnZ- zX!IuBfrFhp(5xxA1AgvYLxdUw8{~siT^s<8Y-pTlVQk`kSftSo?WcJco50qN{>Nl4 z&7TOZmWJf7wASEANqsG0QHs{)M$CSiLf7K@z~)+O#Olvau_MEE*Qd*_AO2OYzeKyP zHQA&#M7Uu{OJmo@;&N2-@ImS8^pEuN_2mKEIsf(ClewpIujG4mfzMvQi8=!dXo;3= zY($PPwrtYgq`g^N8cWEGU%%~wu@(JoiB{_G2mWh$oBR?!|Ouq{?f<@uz&p_jq&MR zwT8B&EYlP1ehrMbw^nNyDQOzrzsIkE@wwclz@`9SUEIM_+6T3bxcd%S!Pvs}AKFG2 zjHmmD`o$gGFTX5L!`+v(6#50uzkBkJi`rylKkcF(;D2cUMoUpE(Ik}|EUurRi_}6< zoEO)x;C#}-Ptm!z-b+i{yz8ISzNp=;Ey4Aho$LS9*dE-5r|(^VyZmox0bhU6xjw3N zl0?z48lK`o0;cAmDtTw8&M!kLtJJ`z$Rjnq-iA0Z{AhG&NPyoA2{ z%1D_qwA#terBBdkY+ODdile2GP$aZ6@cvoi05F9EU7>BW1rE~$9iUIrr`-Hus?Ztj zES+<)PHVLT@y|7txXU5w)T5u#URS=R3XB%WE7RW}N%ng>|s&< z%XQ^1+B@C+xC4}bA=T{Q9=yBb$~T&Nn-fm{9=yA!<=2(Xs^;LZ6qT2yE?Re*cbQy& zPzuXgI;pQPyBtiEj;&YrtNYcm!sm(RX1;t37H#W%-$-!^Q+X^6&yVt^q@SA2urVaXWe} zZmYPHD24I7C1(UiNu?$rd>mY!m(kMbi6gphnz;CSeVoR~CR?{0oNP))veccrIekYg zjq~x)v_OhB>G#a*b?|N~8)ZBXFO_Mlq150*x9`ty@=oIW#kZE~@TG^|kSaCjiw~)F zcyIHeh9Y%ptqPA0qVwu&QV(Udgo~FAUK*&-xV$RW3YdUtA0Oq3>CEcNJj>zOJp50r3qhHj?5*2#O(gT4NX@h6Yh^xrVGYs*r8{|%F>bvpf* z>+@11)XWoXHASc?Tm@+F96D^&;tZ92h+mOVL!kBm`f$+0610~u8HWuYC+O+R%7+3G zF$uYuSPIpqiutTjhv|)4i0fb8{%Bo&wpG$=3)nR5JWrDbXoUJ{y|TjT#3tU%AjViP zEq3JrkxX&9SKeJM4vh1)_s@Eq->(Q`S`Dhs4TPXKB%r??HhObh0pHgQ>O$!QCXe1u z?aH{0!l|tT>&P`hdg1uyg&SGh3n$1scTl zUHn6{FONH@mS17+vtpK9E#E99gV}Irt5?1$+!0(DUe+3S%cpU#X-H7-`eWKW%XEG&osoNi6-kY# z&^ANP)8qS>BXS@N%ukQ#_z2&9$$ff6b4q;oG56^?mvsKI(ErxSz199^A;%!bu%APw zsThHN;vSfBp^m=Wt!CA@TVAKF@^*Q#(&h>_g%;_rTHhZzaZD6Y(EBFoThrZtr z-M3rKC~+kb&7rq(&)=dfR@>ATr@TVTQ{NwXemE+}9kIuy&!t{ZUnq^1y!K!Qr(rr! zZ9gOsp43a#_OlMsMk}51#${LsNz$7&zdTE@&#}trBq70Guhu6d*n}(ZoY4gdcC=cb zSMIm-&3D&Atr*oJiM#87)#TwR(4IEC`j@!-eS~?D_N&=MvAJ7clS(^TKc(o&+m#-@ z=;Bec*BmuQX+rvOoTWsW=9sL>C8P^eyMPnXWF#5DSaq+|qH2*?K)F^ghL$CY_eJG!aUN)EYUfMYNDygUfc=tk9|W2EEVeZIUgS397{VT>QSKOfsvk zNo|+=i0@!K+P_#Sq#ktfgqyL0Hb`cG2kCpcYhbM>9+vKE<%xgzP}VJaCc z!wt8UkVfV(WQvD>tBrRq4%;^n4Y$kJX&G(h6>R?kN7|!HV_9uE#%F~-Y;-8?@+0&p zeH`qq0)zE67sr3L?>QcS%saT@%C?(h#9&BKg%zXlBUVZhWZ zc_dug?(z@oz&;FWrFG@?f`3>C_Q6gLB@a0KgN@O373R>0%Rd0ykz~dHXcr-of|XmU*`DFFjlIXLSSY#cH~V?#a!;Jg^ZsM@MLb!MewoR>J*t zU}nAs+Rfs?+|c&9jAzHMnHGyIQs%_gI`YeaY}-ida`Rx-@FO#zEfkC|K)#It^YiMA z?7XJ<=5D#PmT?&Ox=r7z-=Ysq=iHXDMBgyA^3sGh9r}<#d3uS?N(-bPOIM6>eZS3= zotA7!zygT`galcp%?!*4L<1gv$kL)^9hUw?=hh^d-q|KAV$oB4;dWB(WH*ao7@aa1nXunk1fQa$dTnT@J} zw$PhtJ{TUNoW2m+dpxuh(u(ovf#+|Vczoh;*%;GSrBc)v``)4_ua6#%8zrp(Jhvek zZVFt1*+`_6+)mNJ%ql+C|MbT3;j%HJ=ba22QwLV9-<2_J7Y}MZ+RP?33@a|Ih9YDq zdy+F%;D%^~@!KgX@9DSmdGtHeFeow2l2?f5zGvtog~OzF@gny z=wN3A3kH#y14W9-i(Bs0{|p}oM`G&Yjt`_ar8cERX$uliqK_L78Nev*;L=0m50&|$ zqRmwf(#2H{k}=whXD2vF7vtFp4wA7*upS^EJj7adL$INWgY*-*gIsz7nS$UhUY95R z?D!Rc&Q^N>xE9i+b;_(gFiHQE6PUH1^-8V&)dLO8*S}alSeE+1v7^HxKkEmb+P|kS zq>FFbKIgUE&RBP3F0{K0-Kp=?E4jNcLu6?MJ||Wm-?fg88AEz`)q1(0y_mmQKc=tK zhw|kdOR#h-6Hls`gQzpo8Hp?DWCm;=W0aTL)Tk8KN_N@$F-=%^|*M%HM-C{$R_d)P4F6Xo#;Xk>K{$3;Bs%ebNQv0lL`J z0M7W+@($$@X#>u`kowS^O;>HYYDMcx%FfuNzlZKl-M#Scg)6lzt)!w>)W1H^cXKcF z$qD02yGpqVeSP%a!@b7!#!=&Gvy|&Im~&ld3@fI^|k)qou=k)VxlrxET6FLo(NL1ltqN;UnC;klfxc%&9Sg8bY`s9< zQa1JnnH7?!sTR-+i>R3O%2m8s?@jflv+-mk;^cTgG}#-{ zv*YbyfiJz|QgA{!A0Le!bI$*d;Qrv(qyV-qPw$uwr79c9-o!$oxMf(?60H4 z$B!*vGI4lZ7UeIbN7I$VN2QF5-Ddq4_+krgebrteVms>Ts{O+aWDBk~u?0tXqO)ke zapkD7Ow<=m)o8WxJaHb|In+M_F!9n!th(qL17C0Zr-#=OZZCYvfV%D0u04`v_36Z}r#L||p; zGZ9<;W|xk+(ZK9DpLVR?w(e}dh4;zN@QnGj!)JFLghx>LSMG(D+}*e{==oRf)?SYt zlqV(lH}V&aqV|BcOh0eDn7e<^V{(zcO)t<%>Z6K&_pCqDKQsqg;uNqP!DeaG!cD2W zQ_Hjt89E~vu3l}g{3xBLz49K1XSv51hpgEL%eI8in6^hQJIwl;&>D#uhLXBhoy4aY z+9@%^ApA>Zd&0b!x>QQUgntS0)|{k&PMpuCx88cAtBRxm;zj)h+ z!snF_0RN``)BjuL+vOCruaxHbZ`)W+Jzd%AlU@J2VKOOA4O>*O^t^ZQ%3V}Cd~|k&rck&mzYQBx+*_` z63&00ycrjVzt2gUrp@aG0QCK*P~9-)c>YsQa})m4?_FOpKApcP1=H+5eKL0}pTl#G zp)$Wbp74CXyqLX+xcoQhMDCx=a`4NmTEb`L$(Q0%JE4#1)}1vek^S zAvqZ5zwya~@h6*4uAbB{1X8kw2pkI=4dT9eK$2Ig|*4Nkk=#*Fh7U zAvH{S5e(OKIy5-23Ub+)_QNQ5>{vgTO(O zrOfh|zn{6XdY<%{l`<`X`+L?OxYS&Z7C!2p*Q8h|8gRXleC+*e_x)A0W%&=?^OR3n zJ(dO2sG36Yc>ci;S0R3cLgAX{Uwg*?%~oTQm^~M-t~u0%ewnP|(jZ0`^tTep;NXc? z4az{w;4>#rlnrFtvnuP4isLU#NA%>?jT*4KVDk|WE4{`ds{^I7{sZ~9OZjix;SX-%0$FEF>Gtlt8dQH;t@l)r!XIvFXgc}24H;b3i z%`)#LD`uwne}gB=pGnG=#I4!f^o@~pNC^b1m#O9}e54#aer4g|jgK1npI3g#YBkk- zQ%HfS<_K*xwJtqlUMQVuEj(;_ZW|M-($7f?b74KaYq{i~3Lw?nqy9PXmtlCnZhKfN z#OW>;X>JN}y6kEA`9&tf%Zw4Ub}+Mg4Dm13qy?i5?)YKdHP+hqz~qQ`7K*9{5i?S+ zUHx?>L<>(1Jew2O zV$b7f?6%A|cT}kBo1si|&~X~!LXOi&WOD!2v|{;mdns%*hrEq{jiqY$2wh8fn4{|Vd|?t%sT%~a6%Ylr;MDrzWj0AO3E5Od|7T`MvT8q~Bmo zGZZxKZRjOGFBR3v$eM*&=!xvzB-$7m4;2=60So`!$LPFK#v>ik*}IAMQlAmGQi!i$ z?=yf{8I3z zO(z4BK$GCkKTO|x==AlKb!BJZf4K7DzCY-CB7QI?FsgJDY#eQ#2k`A+^nWkGT9j+2 zR|jLBh=!Se%ZAoO_Nxb@z!3%fTb2s25B?|gPYb$1jLPPDt6ghGug2IJh*@Pip`Wu~ zFM&}--_BA}pB>L4?n2-$>e4fD-RUSjc-n{{kq)QJU>(Tu>w(dcGHD6VJwXe#Yo{)q zwg|c}$1egWu1zdWEb0*Pi=y?+{)za-DlUom#R479y|nh^+Br}xsCL7#(3wCbfC0{y z^APNv_XOTbv(ZJ^az2JP*B2;Km*|9OG)F+emzDP321h`_e>eP_%D<${?MM8b1W!6R z6JWI8!I3C9l`)pQ8uvv5AA&^%@4M~ezG+!G8{rs&H^*BQh3-@5MV8{dWwfPJ6Ts0} z9Bd!+M=zzB|2}I^wa(zZW#lvSnE%eI%bQn6J`sA&BiU@Qt;jb3cIJpM9Sjlb-p z`{?U?Dtq^$JV!5{fNg!6P7&kZG$m;kyYgfb>gi9nU#>cl$un&RXYk*J>*ftJp!)f^!42p-sLecvbL!kggo9={birrJX9`my%@0iK5gEMFYG%bSNCy{l09 z?!?FHgR6HkWU-^$D0-?hKS`8!c>Me*3_& zihb#sSEDB!=>sLezRr9hn62Wy&B2;*KLxwBogSctnCE%CF}w9|@=51BW8eM+uzMNhv(pO*Hz^07=8=rPO% zJbSZz1iuyM$k8>ScR90VNj(71g~M+-xDh?1kuKWj%@-X73NsJfYLno%PB>Ux#ezpU zqpWE1;$J!b>?K67zF6)59KrfsbK!ud*D%Aq9!s%v@IIZbmM6RmZiC-G(FPxj8fr1O!NU@mt@`+D^=IO* z%QQbG^0WUgfmoDL7kjnT$1ggBly?dm5XW#=Au!jGh$Nc1|F>bT<`j#xHn+h>NxHbt z+B|UHEW=(%Ip{pG z)fybkeU58b+BiGDESvy;{USVAGXg7R)BwzV&a1&h3GQ=V4UE0<>&3scfZr4CXYHS; z&;#}Y48*V-@4E}$r(v-9B^PH9b0#D}$jQGL@js#lfio%8V3myV*x_^mIgZ$Stc~EY z?vcw5<~}EC5HV2P=R^%628#O}*HGSdIDK4C6Zbi;;c584%D^0>$?28aVy(`4>@qdc|@wJO4m;*I(3qjNS@AW3C@>?{yr{q z1mi4+)mPVo zXS*|8&A(5rpMU!?dN()w^Y5a8FYCDG5{sl;Jbu4` zXTFC@h8I8k1^63Ew9)YReRwORTK_;ZHA(SMO?e&sp<{uF_+R)3dSaFz|BIO{m_+=r z=U+e!c_>y>em7#bAiXT_`r~xg=rR?{yM9sap3C;V*HQ9n&s?_eJ?!`<%!&A^_yUdP zBlDMW^J@6tyBy5^k-p|y{UsKu)n77$y<@$DzW%b>o;tRAaMagd)I>A;|EI$r#z|9K zn;rj#iwkyPuxyUYp8S*TRf9)heR=ZFDdhfx9#rjrd`O|`HTp9=zvw}=`MC$xVw9*G zzg$CoO6iy3D&+aExk1FM^Fd&EYGqFi!jYIZW)Ig~KZemi&#qR0ACXWKw7f!Rmd7~mRM0*LvO*{kNz40ssD{5fVHH!EY8&R;xT!CPbgV?LtZrk^?o-Gp%> zEVKtt|7ed~rhh>C)aoC5ta|+e+>u*@=(=sHSA)Q5-O(PZ`o}SgwTHb`{e%0Qs6pr- z+~-6MLjORYLk+lpSN|BZxrTd;16V!4`p10w9Q_@=mF~fO{|Mwh#yl+-R4nd}qdj`T zY&Wmv3|fE|6&F`mpYpg^~2wo;`M)?{i4Gm$Ls&R z^?M5V_l5c^BVB_cKju!#(oa%rF#b$p@8vLBumXLVF;8P{VR!L!SPzdPc#pP!Ffvwnx456eIAd2*== z{RKU1@Gp@n^akxTJjCC43R*qR*I~Yk+VgD1eHL*6H_;!{bzt0X!%WXIJAt?Dy;+eT zIv;X?rFDx#C!|I4Rj|~NJM7P%@W+MPF?)Bp-g)TX-t4RJ>KmdxmTY4M09|la-w>Sy z*C^}{@#;rd{)E{65O|3FkEXA`v-FTnUw=OoJ){&d|9SSyh+9N&*4MS)S^rei{^85o5C3c3 z`klth*6&oz`t>`|J@EYe^*ig8HIdZ~-GRAuRWJd4cdNWp+7Y=E?<(uy<*D=5qJas- z|2&>6fD>DwozOQ}PcVbvVE2XMD+NE*JA4S728ZD%9z%0j-Qus-M z-|MgsQJy8h{~w7Z$Zk3${j;loU?k$+{zWP~Sd9NgbY72-@coMM-$f_%`8H9G2Jkrs z?m@)w<{*C{f#mx5-5J_7`t#;1PI+hjj+TW5@ng{mMV**Q{q=`@4VEC{`3BK5&SM48 z5dqPHDCEgmL^Hh;`!Y<`?v(3jHoJbO5$&;b_AByQ|Usx>? z$YS+mlH32Jn8zZSzESTxULc{OJ!*?q=WC@0uHhZ<5xi+^HKXjXgG24=wsyw6 zK7LFuz>{>rInPnVW2v+u+ZWKO>KSYY*FSxn4eic^q@}X)sa`IhdcBOw*_xACY+Mw?mV|@*gKjEbn{v zmiCT$7;)7EO?Yh5#9Id&?R~}|58e)K=9Ba!w4f)c5-jsMX)->kOhRTn2N@oLc5_=pJ>Ha+LKXJhr*OtToav1#gPu(3b zDs%#E=w}AojnM3r;)<>M5t>=z_)HO@S+Or7LURJ_wTRHH*pucYR`aO+A~tg1kImM{uL`eM%~^9nhk7|sFMEo~lm!3Vlp(rq4h!nmkk zJbFHUoPRp$Df5<(EKq!mcklLqB2}J~q*WN(3lY<@7jt<9JqPej~T6zgBx$%{V}Q3SAL0^%%>pb4XF?-}qg z&wpiFWedz>DiVxJwfmp&7^>a>WJ2rJSGn?yM<8qO%zyCQzgogNROYcIM+>Kdv|U*Q zD|i~N&w3L)kLGtES6Pa)e%*z+cbm~`6by0wg1lQgseWV1dY8ZJk=gpW(0r3?f5f## z`bGO<{!UZmSKyO+A=N#w!pzZ7j4cn=9T4dn$iYG#ch_EPY6jP@-MyiN`>ox*LAF`a zY=rOT4*gGBw$GV-7kf75Me=HH=ASIxKEJ#wO5dlf^cVC#`mDC^(G$QU;kO)a*pVFA z*R!YR#k^<_uXg&+$^ZK3{<~+f1@A}qZ(2xr&j#2M5#8U5=j~#fqx)AIb3>T_T9V|x zXhfW*VSgm5I6kjSyL9?u)LZ{@+A$32k3m2lpklAT?>uvbA?u#r`&kmfueuMUp`b9czp33#=_oiS$qz>g@$BfdI z+dJUN|D{?+UX5MHeEFZ1chZCOQ_GeA{Rgx!({~VQ>dF5y{168T(f#^j$dL}bVFlxj z*X*$_HIqjlxZGZucQpIbu8CY(E9LjmR%5+^)w5%72qdc;e`V9VHvB*+9S7#*w?JUO1p@!#_M`l-x_^+XFL^RxG~Fj> zr(`4D`UheKXna^d);yDzQbF&IF(TTLub3Af`S;jahpi^+w>B965H800Ts)h)Lem}j zuRAkVS0ED-@;~F?!_@1r=brrUMf~r>d0+mk9RC9US3^*&Fi;To%>D+<`%XOZ%0*z_ z!2ek!{9oTY)%n~B#HLv!{9moX_CPi?H?~4@Trbq2PD3bdCYE(E*aN#{*pM0`fjzKg zqo<1T&O*NTAG)ezy}M!;Mc}zJN8$ZSd*#92XKFC7ee-m0&Z33i`lcQiZfW> zP%8rtmn85f{_X{r#joV*<5!+&H(>+R#ILjh8~*C7`+v>&m7m=|(9{3D75JY1zs_BO z&+ZIcQD?kw*udLuH??cwYUdXjGtHsSftnDDCXhX#0*AlVg{r(aV&3b;#gik zai8_=Z=Oeat(kwGf0^~L4kK1hn&~SbkcLs}2-g?D^j(*HtkW&DKeD&#aM5Wybhg*3 z9a*9u(?#psrdM2o1_Hf4bvCz&76j%+7EcxX=RMhSw{{J@lhyU}L9CuPrdlth#QOQ4 zBL4MJ>+4tCAHd4n`u+dvSKv2b{O-tI zp%3s5J}!T8x3oUg8-hKq3I1Zjy4t+UtXSR%epz2`cA2l}huwF(TDl>0FCqpOV+`@z z?8W@IHIxW&EGpwg`YCgpIZVSw$$4~L(EApc1t5Xj8`}fWyBr-Pf*jM-1pJpv;6Y5_ z{S)+rUGy*=HC{C_g97)UWEl~lSu^Qily9srKMi`LUw#*TguZQDG%pMbJ)(r`Z%66G zu+SrVMf5>chHT;eOHe}_`VV)$gcj2Thbne3IctyEtKdT!hO{?v-b>72@2T1UCyy~_ zgM;$Uvv<8+>k^X!4q|sH%+mGa2YUe4^;fXmL9e{9VavLYMFfk7#rH4rzi|z(^!xc) z2R~7T+f7;i>fTep5Tukjjs|2%f_tK&a|!2bQS z9fvRm zrn2gpBYX#L*`_gR}ol z!QvAYYsRYnH{kiu3ps+t^S_P8juS_)FoVVZx8{Z>#LFhD@hiMKR|#!=6Y(qS+3Je={>z(~f3NXhUS-^6mUQ2Lxf7#*oufgE@yk9R z)DX-z&UNCC*yn@s6S|g`$2k7zJH{Uxy=E!p>G2m}w`ENBfQs>V9Q=FM(IJGW{|cg) zQ;rTH@bQuQ_@OgcpNQSN3@?7@g~)0N<*VcO8`4fLuX^K`_sl~Lrsu!oJs(kn>G|(| z@f)wc=HLJL{S)~86ZriT_R&*4y>Gek=Zm2N)}07A0@b4Wp(~=(s`IG~+8)8!a5cHMle(=YeG*Vc#pdOgn7VfgEv=jY{- zu#>R&TYTs0AuOl*R8_~^!9>5OwFWv5dkY0 zvnlorJ7f3S9`0!1xsO*MbMy|;j0ooM5d8l#NAD2)SM2|1`#)zGzQ}E%9k*M+LI33$1E-=G9V3>jJD0F7GAYXJLA% zt*TDQU_!A9r;0gt{IvDR7rgwey;oxI6-tzIP(~!kRn`2?irsLw+QEeW|3lj=FML?% z{04yk!%DOo6%`oQs7&92D{T;xYWL?(F4-ESxDU>iAOw+iIy-;%A`4S!159AJ* z9wx*-=1t!uq3k zQ89G4B+jR(048}gY*CTehwvg*%$ME#>_a#KYovl}_`9e5Xa4>|TOb)|3$&qEi1C*Q zv<4CZtWHKWA&@9^wH9Dr6nfsb#FO12Cq$rTy$3O@MM9VgW;jrqV&w}1rJO#nq~{4oSHa% zUMT|~PLIaUXfNr}!(9%JQ3uv9`1=U;%)U<9)qCv@L(p0&fg!$8zrAv(|L%V5L|bGtGCveXc#w zp%jCgwC!@*>2nF}zJ6b@S1ST`jz>L!AuY`)J>zyl>;%saf9Ne=)yLI_m7(7#UJa`|Zhm&HBIp z@b4e=(F5}5cV94H!Wa|l|Gr1BfqOp=%-;82=~&(Vm%aGQ6MTJd|I131eTxLa#u*i? z!SwdO#4S`)E|<@HAV)dxXZ1UXuMt-t!16B=V z1u1%(NPP_prcbZI>`ywgYY$oO`J%CP*Z7`N-oxTBOZ_oiV@24*U~_1joOR`i@In^p zWL}&XUdRt=uj-S@LrGpew%9!}nZyUXBY9YrFO_)p*b(=qH8>E%MK@wuisz;U4I2>~8;)#%ltdp`o*(U1Cn zSg07so&85QI)DF!uUW?Unu345#B2O`e*RTbdy^*5Xo!b8SYn~TbTB#7o0-31HU|5% zTW9U-hosUYkm5Y~em9@6M~5CR1{YO1s?;k`Ao0YTIGIn~uY9$A-mm>G}v&j8N^h3G z#2SVgT>XK$uVZpqskqnEsUQzwr>=vp{;%NqG*MDZYTosuTC?45ch~~+RSU79SgEQ{ zFfKuYdFR7~{d?Va%*Vja$2gWmoLAvdYN6TqRI%C;y%I550<-^lj{f4-Q+-uzOUIs= ze`1|m0AG(N`(vM za}Af_D&)Td(oD(f`XAooU&X4vY6i^LlX=Um!_aRk$xR(PU9P>2qf;R+zzrB$pG~qWKe}r(;J` zJffndguUp@#OSGb*jbT(6}0NBgL6YSG++(8a-%XgHaB)vY)=}*4c;5vY z(qfS89@Onm|KC>PjbBKKY;xrO+_E{+J~qB{g{>> z@v)}Qv(u)Je>Z=1M~c{mk#~v~xB3mlERSOkFy0H26Yn26N~6Z(xdXY!b9Z1?%9y^^4%X zc?B{e_?;@8)xY`k#u1F(G>-7tc8PF!rDoF|cC&aBBmv0p>e!*AJtH!|A>G z0*Up*P7UcYa5hq;V(LuKt{#?dybVzfk#Bpol8do3Ayt&6(Uz!a&nUE)Q?v}ba|rAd z`W^mfviPsvU+#GCg1?)(FTPm0bi*TWZ~N2RQr}wj-Ua_V_NnOQXa525#(DYF)SrKL z#!dS__=$LYnSb`d_f6jYM>l15j^8@@f%Q8N7QebK-*e%nZK3}IwgQ{3 literal 0 HcmV?d00001 diff --git a/data/sprites/official/toadette_captain.1.zspr b/data/sprites/official/toadette_captain.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..e69f74a71c7001d61270f8684a9b3bd237aeedd6 GIT binary patch literal 28885 zcmd^o4}4VBo$v3?+)Qp@NG=d@0wHsS2x&z#{8s^NK)6W-QB(}wZ0E%BN?rAsmns@`f1UnWb{Qq8zrPE3}LwUeSi1N zote;TpR%9Nc0cdjPiE$O&OPUzd++b}JHOxW{LaZ=es1}#(!$3Mtob}4HGRH`C`JSX%wXh@rCD*yOi?V6 zq+WWSHh}uJC^d4K$4d`Z?`&$QFqGRXu9+~|>!l0yMOsFMbcfodHS2MDtMp9aaIvMn zSN!^#{qZ>cANm=NRiK3k1wjjDsTnq7dRVX3f`R;e`ê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 literal 0 HcmV?d00001 diff --git a/data/sprites/official/two_faced.1.zspr b/data/sprites/official/two_faced.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..d504c321ad11dce8a110deec91ac8488152363c7 GIT binary patch literal 28873 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%0A?-oGA1L0eHw!#y#LOAcEBto@ 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 I^|x66Kh0F=tpET3 literal 0 HcmV?d00001 diff --git a/data/sprites/official/wario.1.zspr b/data/sprites/official/wario.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..f1a5aab77422e63913302c42649bc53c042733c1 GIT binary patch literal 28861 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 JA9^VIe*kfk$sYg! literal 0 HcmV?d00001 diff --git a/data/sprites/official/zebraunicorn.1.zspr b/data/sprites/official/zebraunicorn.1.zspr new file mode 100644 index 0000000000000000000000000000000000000000..c06130ff7a6320c5afb6c6ffe7c193aa3934377b GIT binary patch literal 28883 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 Date: Thu, 11 Jul 2019 00:12:09 -0400 Subject: [PATCH 07/52] Reinstate the state cache system for playthough generation Fixed the issues in can_beat_game that required me to turn it off. --- BaseClasses.py | 18 +++++------------- Main.py | 3 +-- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 01f7168d0c..289063e83f 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -261,32 +261,24 @@ 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] - 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: sphere = [] # 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: - if state.can_reach(location): - if location.item.name == 'Triforce': - triforces_collected[location.item.player] = True - if all(triforces_collected.values()): - return True - elif location.item.name in ['Triforce Piece', 'Power Star']: - treasure_pieces_collected[location.item.player] += 1 - 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 + if location.can_reach(state): sphere.append(location) if not sphere: - # ran out of places and did not find triforce yet, quit + # ran out of places and did not finish yet, quit return False for location in sphere: prog_locations.remove(location) state.collect(location.item, True, location) + if self.has_beaten_game(state): + return True + return False def option_identifier(self, maxbytes, player): diff --git a/Main.py b/Main.py index fe06a64ddd..6042f36503 100644 --- a/Main.py +++ b/Main.py @@ -327,8 +327,7 @@ def create_playthrough(world): old_item = location.item location.item = None state.remove(old_item) - ##if world.can_beat_game(state_cache[num]): - if world.can_beat_game(): + if world.can_beat_game(state_cache[num]): to_delete.append(location) else: # still required, got to keep it around From 759c1a568679d7b1e8f83f3a52149713a42b8ec7 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Thu, 11 Jul 2019 00:18:30 -0400 Subject: [PATCH 08/52] Optimize simplified caching system --- BaseClasses.py | 46 +++++++++++++++++++++++----------------------- Main.py | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 289063e83f..fac890f0f6 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -227,12 +227,12 @@ class World(object): def get_reachable_locations(self, state=None, player=None): if state is None: state = self.state - return [location for location in self.get_locations() if (player is None or location.player == player) and state.can_reach(location)] + return [location for location in self.get_locations() if (player is None or location.player == player) and location.can_reach(state)] def get_placeable_locations(self, state=None, player=None): if state is None: state = self.state - 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)] + return [location for location in self.get_locations() if (player is None or location.player == player) and location.item is None and location.can_reach(state)] def unlocks_new_location(self, item): temp_state = self.state.copy() @@ -316,32 +316,32 @@ class CollectionState(object): def __init__(self, parent): self.prog_items = [] self.world = parent - self.reachable_regions = set() + self.reachable_regions = {player: set() for player in range(1, parent.players + 1)} self.events = [] self.path = {} self.locations_checked = set() - self.stale = True + self.stale = {player: True for player in range(1, parent.players + 1)} - def update_reachable_regions(self): - self.stale=False + def update_reachable_regions(self, player): + self.stale[player] = False + rrp = self.reachable_regions[player] new_regions = True - reachable_regions_count = len(self.reachable_regions) + reachable_regions_count = len(rrp) while new_regions: - possible = [region for region in self.world.regions if region not in self.reachable_regions] + possible = [region for region in self.world.regions if region not in rrp and region.player == player] for candidate in possible: if candidate.can_reach_private(self): - self.reachable_regions.add(candidate) - new_regions = len(self.reachable_regions) > reachable_regions_count - reachable_regions_count = len(self.reachable_regions) + rrp.add(candidate) + new_regions = len(rrp) > reachable_regions_count + reachable_regions_count = len(rrp) def copy(self): ret = CollectionState(self.world) ret.prog_items = copy.copy(self.prog_items) - ret.reachable_regions = copy.copy(self.reachable_regions) + ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)} ret.events = copy.copy(self.events) ret.path = copy.copy(self.path) ret.locations_checked = copy.copy(self.locations_checked) - ret.stale = True return ret def can_reach(self, spot, resolution_hint=None, player=None): @@ -366,7 +366,7 @@ class CollectionState(object): while new_locations: 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)] + reachable_events = [location for location in locations if location.event and (not key_only or location.item.key) and location.can_reach(self)] for event in reachable_events: if (event.name, event.player) not in self.events: self.events.append((event.name, event.player)) @@ -537,7 +537,7 @@ class CollectionState(object): self.prog_items.append((item.name, item.player)) changed = True - self.stale = True + self.stale[item.player] = True if changed: if not event: @@ -573,8 +573,8 @@ class CollectionState(object): return # invalidate caches, nothing can be trusted anymore now - self.reachable_regions = set() - self.stale = True + self.reachable_regions[item.player] = set() + self.stale[item.player] = True def __getattr__(self, item): if item.startswith('can_reach_'): @@ -616,13 +616,13 @@ class Region(object): self.player = player def can_reach(self, state): - if state.stale: - state.update_reachable_regions() - return self in state.reachable_regions + if state.stale[self.player]: + state.update_reachable_regions(self.player) + return self in state.reachable_regions[self.player] def can_reach_private(self, state): for entrance in self.entrances: - if state.can_reach(entrance): + if entrance.can_reach(state): if not self in state.path: state.path[self] = (self.name, state.path.get(entrance, None)) return True @@ -658,7 +658,7 @@ class Entrance(object): self.player = player def can_reach(self, state): - if state.can_reach(self.parent_region) and self.access_rule(state): + if self.parent_region.can_reach(state) and self.access_rule(state): if not self in state.path: state.path[self] = (self.name, state.path.get(self.parent_region, (self.parent_region.name, None))) return True @@ -746,7 +746,7 @@ class Location(object): 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))) def can_reach(self, state): - if state.can_reach(self.parent_region) and self.access_rule(state): + if self.parent_region.can_reach(state) and self.access_rule(state): return True return False diff --git a/Main.py b/Main.py index 6042f36503..3cd7a78f69 100644 --- a/Main.py +++ b/Main.py @@ -246,7 +246,7 @@ def copy_world(world): # copy progress items in state ret.state.prog_items = list(world.state.prog_items) - ret.state.stale = True + ret.state.stale = {player: True for player in range(1, world.players + 1)} for player in range(1, world.players + 1): set_rules(ret, player) From 67a5b7e105a80c5ff91da6ee950f32983f1a0506 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Thu, 11 Jul 2019 17:33:27 -0400 Subject: [PATCH 09/52] Don't require the Gui unless we are actually using it --- EntranceRandomizer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index da16e2a6e6..d87358e64c 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -6,7 +6,6 @@ import random import textwrap import sys -from Gui import guiMain from Main import main from Utils import is_bundled, close_console, output_path @@ -232,6 +231,7 @@ def start(): # probably wants the gui. Users of the bundled build who want the command line # interface shouuld specify at least one option, possibly setting a value to a # default if they like all the defaults + from Gui import guiMain close_console() guiMain() sys.exit(0) @@ -252,6 +252,7 @@ def start(): logging.basicConfig(format='%(message)s', level=loglevel) if args.gui: + from Gui import guiMain guiMain(args) elif args.count is not None: seed = args.seed From aab696fa9ceb89e076446457a21d877efd20ef07 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Thu, 11 Jul 2019 17:46:57 -0400 Subject: [PATCH 10/52] Remove logic_hash It is no longer used for anything. --- Main.py | 20 +------------------- Plando.py | 12 +----------- Rom.py | 2 +- Utils.py | 8 -------- 4 files changed, 3 insertions(+), 39 deletions(-) diff --git a/Main.py b/Main.py index 3cd7a78f69..c2152e552d 100644 --- a/Main.py +++ b/Main.py @@ -19,24 +19,6 @@ from Utils import output_path __version__ = '0.6.2' -logic_hash = [134, 166, 181, 191, 228, 89, 188, 200, 5, 157, 217, 139, 180, 198, 106, 104, - 88, 223, 138, 28, 54, 18, 216, 129, 248, 19, 109, 220, 159, 75, 238, 57, - 231, 183, 143, 167, 114, 176, 82, 169, 179, 94, 115, 193, 252, 222, 52, 245, - 33, 208, 39, 122, 177, 136, 29, 161, 210, 165, 6, 125, 146, 212, 101, 185, - 65, 247, 253, 85, 171, 147, 71, 148, 203, 202, 230, 1, 13, 64, 254, 141, - 32, 93, 152, 4, 92, 16, 195, 204, 246, 201, 11, 7, 189, 97, 9, 91, - 237, 215, 163, 131, 142, 34, 111, 196, 120, 127, 168, 211, 227, 61, 187, 110, - 190, 162, 59, 80, 225, 186, 37, 154, 76, 72, 27, 17, 79, 206, 207, 243, - 184, 197, 153, 48, 119, 99, 2, 151, 51, 67, 121, 175, 38, 224, 87, 242, - 45, 22, 155, 244, 209, 117, 214, 213, 194, 126, 236, 73, 133, 70, 49, 140, - 229, 108, 156, 124, 105, 226, 44, 23, 112, 102, 173, 219, 14, 116, 58, 103, - 55, 10, 95, 251, 84, 118, 160, 78, 63, 250, 31, 41, 35, 255, 170, 25, - 66, 172, 98, 249, 68, 8, 113, 21, 46, 24, 137, 149, 81, 130, 42, 164, - 50, 12, 158, 15, 47, 182, 30, 40, 36, 83, 77, 205, 20, 241, 3, 132, - 0, 60, 96, 62, 74, 178, 53, 56, 135, 174, 145, 86, 107, 233, 218, 221, - 43, 150, 100, 69, 235, 26, 234, 192, 199, 144, 232, 128, 239, 123, 240, 90] - - def main(args, seed=None): start = time.clock() @@ -144,7 +126,7 @@ def main(args, seed=None): else: rom = LocalRom(args.rom) - patch_rom(world, player, rom, bytearray(logic_hash)) + patch_rom(world, player, rom) enemizer_patch = [] if use_enemizer: diff --git a/Plando.py b/Plando.py index 0bb3dd1ad7..290e212b54 100755 --- a/Plando.py +++ b/Plando.py @@ -19,16 +19,6 @@ from Main import create_playthrough __version__ = '0.2-dev' -logic_hash = [182, 244, 144, 92, 149, 200, 93, 183, 124, 169, 226, 46, 111, 163, 5, 193, 13, 112, 125, 101, 128, 84, 31, 67, 107, 94, 184, 100, 189, 18, 8, 171, - 142, 57, 173, 38, 37, 211, 253, 131, 98, 239, 167, 116, 32, 186, 70, 148, 66, 151, 143, 86, 59, 83, 16, 51, 240, 152, 60, 242, 190, 117, 76, 122, - 15, 221, 62, 39, 174, 177, 223, 34, 150, 50, 178, 238, 95, 219, 10, 162, 222, 0, 165, 202, 74, 36, 206, 209, 251, 105, 175, 135, 121, 88, 214, 247, - 154, 161, 71, 19, 85, 157, 40, 96, 225, 27, 230, 49, 231, 207, 64, 35, 249, 134, 132, 108, 63, 24, 4, 127, 255, 14, 145, 23, 81, 216, 113, 90, 194, - 110, 65, 229, 43, 1, 11, 168, 147, 103, 156, 77, 80, 220, 28, 227, 213, 198, 172, 79, 75, 140, 44, 146, 188, 17, 6, 102, 56, 235, 166, 89, 218, 246, - 99, 78, 187, 126, 119, 196, 69, 137, 181, 55, 20, 215, 199, 130, 9, 45, 58, 185, 91, 33, 197, 72, 115, 195, 114, 29, 30, 233, 141, 129, 155, 159, 47, - 224, 236, 21, 234, 191, 136, 104, 87, 106, 26, 73, 250, 248, 228, 48, 53, 243, 237, 241, 61, 180, 12, 208, 245, 232, 192, 2, 7, 170, 123, 176, 160, 201, - 153, 217, 252, 158, 25, 205, 22, 133, 254, 138, 203, 118, 210, 204, 82, 97, 52, 164, 68, 139, 120, 109, 54, 3, 41, 179, 212, 42] - - def main(args): start_time = time.clock() @@ -84,7 +74,7 @@ def main(args): sprite = None rom = LocalRom(args.rom) - patch_rom(world, 1, rom, logic_hash, args.heartbeep, args.heartcolor, sprite) + patch_rom(world, 1, rom, args.heartbeep, args.heartcolor, sprite) for textname, texttype, text in text_patches: if texttype == 'text': diff --git a/Rom.py b/Rom.py index 61105b5b10..f1583b9c92 100644 --- a/Rom.py +++ b/Rom.py @@ -412,7 +412,7 @@ class Sprite(object): # split into palettes of 15 colors return array_chunk(palette_as_colors, 15) -def patch_rom(world, player, rom, hashtable): +def patch_rom(world, player, rom): random.seed(world.rom_seeds[player]) # patch items for location in world.get_locations(): diff --git a/Utils.py b/Utils.py index 0063ed4a18..de9fd0c555 100644 --- a/Utils.py +++ b/Utils.py @@ -87,14 +87,6 @@ def close_console(): except Exception: pass -def new_logic_array(): - import random - l = list(range(256)) - random.SystemRandom().shuffle(l) - chunks = [l[i:i + 16] for i in range(0, len(l), 16)] - lines = [", ".join([str(j) for j in i]) for i in chunks] - print("logic_hash = ["+",\n ".join(lines)+"]") - def make_new_base2current(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', new_rom='working.sfc'): from collections import OrderedDict import json From c4570b732d28e44667638df91c0b2713ef51c4be Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Thu, 11 Jul 2019 18:48:21 -0400 Subject: [PATCH 11/52] Remove option_identifier It is no longer needed because we set the hash based on an actual hash of the rom contents, so it is not based on the title field anymore. --- BaseClasses.py | 30 ------------------------------ Rom.py | 5 +++-- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index fac890f0f6..616600c781 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -281,36 +281,6 @@ class World(object): return False - def option_identifier(self, maxbytes, player): - id_value = 0 - id_value_max = 1 - - def markbool(value): - nonlocal id_value, id_value_max - id_value += id_value_max * bool(value) - id_value_max *= 2 - def marksequence(options, value): - nonlocal id_value, id_value_max - id_value += id_value_max * options.index(value) - id_value_max *= len(options) - markbool(self.logic == 'noglitches') - marksequence(['standard', 'open', 'swordless'], self.mode) - markbool(self.place_dungeon_items) - marksequence(['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], self.goal) - marksequence(['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple'], self.shuffle) - marksequence(['easy', 'normal', 'hard', 'expert', 'insane'], self.difficulty) - marksequence(['none', 'display', 'timed', 'timed-ohko', 'timed-countdown', 'ohko'], self.timer) - marksequence(['on', 'off', 'random'], self.progressive) - marksequence(['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'], self.algorithm) - markbool(self.check_beatable_only) - markbool(self.shuffle_ganon) - markbool(self.keysanity) - markbool(self.retro) - marksequence(range(1, 256), self.players) - marksequence(range(1, self.players + 1), player) - assert id_value_max < (1 << (maxbytes * 8)) - return id_value - class CollectionState(object): def __init__(self, parent): diff --git a/Rom.py b/Rom.py index f1583b9c92..b16b88b6e0 100644 --- a/Rom.py +++ b/Rom.py @@ -974,8 +974,9 @@ def patch_rom(world, player, rom): # set rom name # 21 bytes - rom.name = bytearray('ER062%09d' % world.seed, 'utf8') + world.option_identifier(7, player).to_bytes(7, 'big') - assert(len(rom.name) == 21) + from Main import __version__ + rom.name = bytearray('ER_{0}_{1:09}\0'.format(__version__,world.seed), 'utf8') + assert len(rom.name) <= 21 rom.write_bytes(0x7FC0, rom.name) # Write title screen Code From dcca15eda745bc4efab16aa5c711b4a37eb920dd Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 13 Jul 2019 18:11:43 -0400 Subject: [PATCH 12/52] Avoid changing spoiler file for single world --- BaseClasses.py | 55 ++++++++++++++++++++++++++++++++++++++------------ Dungeons.py | 1 + 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 616600c781..f41ed2b92b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -610,7 +610,10 @@ class Region(object): return str(self.__unicode__()) def __unicode__(self): - return '%s (Player %d)' % (self.name, self.player) + if self.world and self.world.players == 1: + return self.name + else: + return '%s (Player %d)' % (self.name, self.player) class Entrance(object): @@ -646,7 +649,10 @@ class Entrance(object): return str(self.__unicode__()) def __unicode__(self): - return '%s (Player %d)' % (self.name, self.player) + if self.parent_region and self.parent_region.world and self.parent_region.world.players == 1: + return self.name + else: + return '%s (Player %d)' % (self.name, self.player) class Dungeon(object): @@ -659,6 +665,7 @@ class Dungeon(object): self.dungeon_items = dungeon_items self.bosses = dict() self.player = player + self.world = None @property def boss(self): @@ -683,7 +690,10 @@ class Dungeon(object): return str(self.__unicode__()) def __unicode__(self): - return '%s (Player %d)' % (self.name, self.player) + if self.world and self.world.players==1: + return self.name + else: + return '%s (Player %d)' % (self.name, self.player) class Boss(object): def __init__(self, name, enemizer_name, defeat_rule, player): @@ -724,7 +734,10 @@ class Location(object): return str(self.__unicode__()) def __unicode__(self): - return '%s (Player %d)' % (self.name, self.player) + if self.parent_region and self.parent_region.world and self.parent_region.world.players == 1: + return self.name + else: + return '%s (Player %d)' % (self.name, self.player) class Item(object): @@ -765,7 +778,10 @@ class Item(object): return str(self.__unicode__()) def __unicode__(self): - return '%s (Player %d)' % (self.name, self.player) + if self.location and self.location.parent_region and self.location.parent_region.world and self.location.parent_region.world.players == 1: + return self.name + else: + return '%s (Player %d)' % (self.name, self.player) # have 6 address that need to be filled @@ -848,13 +864,20 @@ class Spoiler(object): self.bosses = OrderedDict() def set_entrance(self, entrance, exit, direction, player): - self.entrances[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)]) + if self.world.players == 1: + self.entrances[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)]) + else: + self.entrances[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)]) def parse_data(self): 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] + if self.world.players == 1: + self.medallions['Misery Mire'] = self.world.required_medallions[1][0] + self.medallions['Turtle Rock'] = self.world.required_medallions[1][1] + else: + 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() listed_locations = set() @@ -913,6 +936,8 @@ class Spoiler(object): self.bosses[str(player)]["Ganons Tower"] = "Agahnim 2" self.bosses[str(player)]["Ganon"] = "Ganon" + if self.world.players == 1: + self.bosses = self.bosses["1"] from Main import __version__ as ERVersion self.metadata = {'version': ERVersion, @@ -966,11 +991,15 @@ class Spoiler(object): outfile.write('Players: %d' % self.metadata['players']) if self.entrances: outfile.write('\n\nEntrances:\n\n') - 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'.join(['%s%s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players >1 else '', entry['entrance'], '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', entry['exit']) for entry in self.entrances.values()])) outfile.write('\n\nMedallions\n') - for player in range(1, self.world.players + 1): - 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])) + if self.world.players == 1: + outfile.write('\nMisery Mire Medallion: %s' % (self.medallions['Misery Mire'])) + outfile.write('\nTurtle Rock Medallion: %s' % (self.medallions['Turtle Rock'])) + else: + for player in range(1, self.world.players + 1): + 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'.join(['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in grouping.items()])) outfile.write('\n\nShops:\n\n') diff --git a/Dungeons.py b/Dungeons.py index bed737302a..f3e13dd4a9 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -12,6 +12,7 @@ def create_dungeons(world, player): dungeon.boss = BossFactory(default_boss, player) for region in dungeon.regions: world.get_region(region, player).dungeon = dungeon + dungeon.world = world return dungeon ES = make_dungeon('Hyrule Castle', None, ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'], None, [ItemFactory('Small Key (Escape)', player)], [ItemFactory('Map (Escape)', player)]) From 6d5a0a004debc31ddb19690b5620cae8c4bb79dc Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 13 Jul 2019 18:17:16 -0400 Subject: [PATCH 13/52] Use a proper multiset for progression items This can cut generation times in half in some cases --- BaseClasses.py | 59 +-- Main.py | 2 +- _vendor/collections_extended/CONTRIBUTERS | 5 + _vendor/collections_extended/LICENSE | 191 ++++++++ _vendor/collections_extended/__init__.py | 55 +++ _vendor/collections_extended/_compat.py | 53 +++ _vendor/collections_extended/_util.py | 16 + _vendor/collections_extended/bags.py | 527 +++++++++++++++++++++ _vendor/collections_extended/bijection.py | 94 ++++ _vendor/collections_extended/range_map.py | 384 +++++++++++++++ _vendor/collections_extended/setlists.py | 552 ++++++++++++++++++++++ 11 files changed, 1909 insertions(+), 29 deletions(-) create mode 100644 _vendor/collections_extended/CONTRIBUTERS create mode 100644 _vendor/collections_extended/LICENSE create mode 100644 _vendor/collections_extended/__init__.py create mode 100644 _vendor/collections_extended/_compat.py create mode 100644 _vendor/collections_extended/_util.py create mode 100644 _vendor/collections_extended/bags.py create mode 100644 _vendor/collections_extended/bijection.py create mode 100644 _vendor/collections_extended/range_map.py create mode 100644 _vendor/collections_extended/setlists.py diff --git a/BaseClasses.py b/BaseClasses.py index f41ed2b92b..29b33a885d 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3,6 +3,7 @@ from enum import Enum, unique import logging import json from collections import OrderedDict +from _vendor.collections_extended import bag, setlist from Utils import int16_as_bytes class World(object): @@ -135,34 +136,34 @@ class World(object): if ret.has('Golden Sword', item.player): pass elif ret.has('Tempered Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 4: - ret.prog_items.append(('Golden Sword', item.player)) + ret.prog_items.add(('Golden Sword', item.player)) elif ret.has('Master Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 3: - ret.prog_items.append(('Tempered Sword', item.player)) + ret.prog_items.add(('Tempered Sword', item.player)) elif ret.has('Fighter Sword', item.player) and self.difficulty_requirements.progressive_sword_limit >= 2: - ret.prog_items.append(('Master Sword', item.player)) + ret.prog_items.add(('Master Sword', item.player)) elif self.difficulty_requirements.progressive_sword_limit >= 1: - ret.prog_items.append(('Fighter Sword', item.player)) + ret.prog_items.add(('Fighter Sword', item.player)) elif 'Glove' in item.name: if ret.has('Titans Mitts', item.player): pass elif ret.has('Power Glove', item.player): - ret.prog_items.append(('Titans Mitts', item.player)) + ret.prog_items.add(('Titans Mitts', item.player)) else: - ret.prog_items.append(('Power Glove', item.player)) + ret.prog_items.add(('Power Glove', item.player)) elif 'Shield' in item.name: if ret.has('Mirror Shield', item.player): pass elif ret.has('Red Shield', item.player) and self.difficulty_requirements.progressive_shield_limit >= 3: - ret.prog_items.append(('Mirror Shield', item.player)) + ret.prog_items.add(('Mirror Shield', item.player)) elif ret.has('Blue Shield', item.player) and self.difficulty_requirements.progressive_shield_limit >= 2: - ret.prog_items.append(('Red Shield', item.player)) + ret.prog_items.add(('Red Shield', item.player)) elif self.difficulty_requirements.progressive_shield_limit >= 1: - ret.prog_items.append(('Blue Shield', item.player)) + ret.prog_items.add(('Blue Shield', item.player)) elif item.name.startswith('Bottle'): if ret.bottle_count(item.player) < self.difficulty_requirements.progressive_bottle_limit: - ret.prog_items.append((item.name, item.player)) + ret.prog_items.add((item.name, item.player)) elif item.advancement or item.key: - ret.prog_items.append((item.name, item.player)) + ret.prog_items.add((item.name, item.player)) for item in self.itempool: soft_collect(item) @@ -284,7 +285,7 @@ class World(object): class CollectionState(object): def __init__(self, parent): - self.prog_items = [] + self.prog_items = bag() self.world = parent self.reachable_regions = {player: set() for player in range(1, parent.players + 1)} self.events = [] @@ -293,12 +294,13 @@ class CollectionState(object): self.stale = {player: True for player in range(1, parent.players + 1)} def update_reachable_regions(self, player): + player_regions = [region for region in self.world.regions if region.player == player] self.stale[player] = False rrp = self.reachable_regions[player] new_regions = True reachable_regions_count = len(rrp) while new_regions: - possible = [region for region in self.world.regions if region not in rrp and region.player == player] + possible = [region for region in player_regions if region not in rrp] for candidate in possible: if candidate.can_reach_private(self): rrp.add(candidate) @@ -307,7 +309,7 @@ class CollectionState(object): def copy(self): ret = CollectionState(self.world) - ret.prog_items = copy.copy(self.prog_items) + ret.prog_items = self.prog_items.copy() ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)} ret.events = copy.copy(self.events) ret.path = copy.copy(self.path) @@ -343,17 +345,18 @@ class CollectionState(object): self.collect(event.item, True, event) new_locations = len(reachable_events) > checked_locations checked_locations = len(reachable_events) + def has(self, item, player, count=1): if count == 1: return (item, player) in self.prog_items - return self.item_count(item, player) >= count + return self.prog_items.count((item, player)) >= count def has_key(self, item, player, count=1): if self.world.retro: return self.can_buy_unlimited('Small Key (Universal)', player) if count == 1: return (item, player) in self.prog_items - return self.item_count(item, player) >= count + return self.prog_items.count((item, player)) >= count def can_buy_unlimited(self, item, player): for shop in self.world.shops: @@ -362,7 +365,7 @@ class CollectionState(object): return False def item_count(self, item, player): - return len([pritem for pritem in self.prog_items if pritem == (item, player)]) + return self.prog_items.count((item, player)) def can_lift_rocks(self, player): return self.has('Power Glove', player) or self.has('Titans Mitts', player) @@ -467,44 +470,44 @@ class CollectionState(object): if self.has('Golden Sword', item.player): pass elif self.has('Tempered Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 4: - self.prog_items.append(('Golden Sword', item.player)) + self.prog_items.add(('Golden Sword', item.player)) changed = True elif self.has('Master Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 3: - self.prog_items.append(('Tempered Sword', item.player)) + self.prog_items.add(('Tempered Sword', item.player)) changed = True elif self.has('Fighter Sword', item.player) and self.world.difficulty_requirements.progressive_sword_limit >= 2: - self.prog_items.append(('Master Sword', item.player)) + self.prog_items.add(('Master Sword', item.player)) changed = True elif self.world.difficulty_requirements.progressive_sword_limit >= 1: - self.prog_items.append(('Fighter Sword', item.player)) + self.prog_items.add(('Fighter Sword', item.player)) changed = True elif 'Glove' in item.name: if self.has('Titans Mitts', item.player): pass elif self.has('Power Glove', item.player): - self.prog_items.append(('Titans Mitts', item.player)) + self.prog_items.add(('Titans Mitts', item.player)) changed = True else: - self.prog_items.append(('Power Glove', item.player)) + self.prog_items.add(('Power Glove', item.player)) changed = True elif 'Shield' in item.name: if self.has('Mirror Shield', item.player): pass elif self.has('Red Shield', item.player) and self.world.difficulty_requirements.progressive_shield_limit >= 3: - self.prog_items.append(('Mirror Shield', item.player)) + self.prog_items.add(('Mirror Shield', item.player)) changed = True elif self.has('Blue Shield', item.player) and self.world.difficulty_requirements.progressive_shield_limit >= 2: - self.prog_items.append(('Red Shield', item.player)) + self.prog_items.add(('Red Shield', item.player)) changed = True elif self.world.difficulty_requirements.progressive_shield_limit >= 1: - self.prog_items.append(('Blue Shield', item.player)) + self.prog_items.add(('Blue Shield', item.player)) changed = True elif item.name.startswith('Bottle'): if self.bottle_count(item.player) < self.world.difficulty_requirements.progressive_bottle_limit: - self.prog_items.append((item.name, item.player)) + self.prog_items.add((item.name, item.player)) changed = True elif event or item.advancement: - self.prog_items.append((item.name, item.player)) + self.prog_items.add((item.name, item.player)) changed = True self.stale[item.player] = True diff --git a/Main.py b/Main.py index c2152e552d..bef644a992 100644 --- a/Main.py +++ b/Main.py @@ -227,7 +227,7 @@ def copy_world(world): ret.itempool.append(Item(item.name, item.advancement, item.priority, item.type, player = item.player)) # copy progress items in state - ret.state.prog_items = list(world.state.prog_items) + ret.state.prog_items = world.state.prog_items.copy() ret.state.stale = {player: True for player in range(1, world.players + 1)} for player in range(1, world.players + 1): diff --git a/_vendor/collections_extended/CONTRIBUTERS b/_vendor/collections_extended/CONTRIBUTERS new file mode 100644 index 0000000000..333a02bfe7 --- /dev/null +++ b/_vendor/collections_extended/CONTRIBUTERS @@ -0,0 +1,5 @@ +Mike Lenzen https://github.com/mlenzen +Caleb Levy https://github.com/caleblevy +Marein Könings https://github.com/MareinK +Jad Kik https://github.com/jadkik +Kuba Marek https://github.com/bluecube \ No newline at end of file diff --git a/_vendor/collections_extended/LICENSE b/_vendor/collections_extended/LICENSE new file mode 100644 index 0000000000..8405e89a0b --- /dev/null +++ b/_vendor/collections_extended/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/_vendor/collections_extended/__init__.py b/_vendor/collections_extended/__init__.py new file mode 100644 index 0000000000..039fee030e --- /dev/null +++ b/_vendor/collections_extended/__init__.py @@ -0,0 +1,55 @@ +"""collections_extended contains a few extra basic data structures.""" +from ._compat import Collection +from .bags import bag, frozenbag +from .setlists import setlist, frozensetlist +from .bijection import bijection +from .range_map import RangeMap, MappedRange + +__version__ = '1.0.2' + +__all__ = ( + 'collection', + 'setlist', + 'frozensetlist', + 'bag', + 'frozenbag', + 'bijection', + 'RangeMap', + 'MappedRange', + 'Collection', + ) + + +def collection(iterable=None, mutable=True, ordered=False, unique=False): + """Return a Collection with the specified properties. + + Args: + iterable (Iterable): collection to instantiate new collection from. + mutable (bool): Whether or not the new collection is mutable. + ordered (bool): Whether or not the new collection is ordered. + unique (bool): Whether or not the new collection contains only unique values. + """ + if iterable is None: + iterable = tuple() + if unique: + if ordered: + if mutable: + return setlist(iterable) + else: + return frozensetlist(iterable) + else: + if mutable: + return set(iterable) + else: + return frozenset(iterable) + else: + if ordered: + if mutable: + return list(iterable) + else: + return tuple(iterable) + else: + if mutable: + return bag(iterable) + else: + return frozenbag(iterable) diff --git a/_vendor/collections_extended/_compat.py b/_vendor/collections_extended/_compat.py new file mode 100644 index 0000000000..bbf0fbd951 --- /dev/null +++ b/_vendor/collections_extended/_compat.py @@ -0,0 +1,53 @@ +"""Python 2/3 compatibility helpers.""" +import sys + +is_py2 = sys.version_info[0] == 2 + +if is_py2: + def keys_set(d): + """Return a set of passed dictionary's keys.""" + return set(d.keys()) +else: + keys_set = dict.keys + + +if sys.version_info < (3, 6): + from collections import Sized, Iterable, Container + + def _check_methods(C, *methods): + mro = C.__mro__ + for method in methods: + for B in mro: + if method in B.__dict__: + if B.__dict__[method] is None: + return NotImplemented + break + else: + return NotImplemented + return True + + class Collection(Sized, Iterable, Container): + """Backport from Python3.6.""" + + __slots__ = tuple() + + @classmethod + def __subclasshook__(cls, C): + if cls is Collection: + return _check_methods(C, "__len__", "__iter__", "__contains__") + return NotImplemented + +else: + from collections.abc import Collection + + +def handle_rich_comp_not_implemented(): + """Correctly handle unimplemented rich comparisons. + + In Python 3, return NotImplemented. + In Python 2, raise a TypeError. + """ + if is_py2: + raise TypeError() + else: + return NotImplemented diff --git a/_vendor/collections_extended/_util.py b/_vendor/collections_extended/_util.py new file mode 100644 index 0000000000..58a83f452b --- /dev/null +++ b/_vendor/collections_extended/_util.py @@ -0,0 +1,16 @@ +"""util functions for collections_extended.""" + + +def hash_iterable(it): + """Perform a O(1) memory hash of an iterable of arbitrary length. + + hash(tuple(it)) creates a temporary tuple containing all values from it + which could be a problem if it is large. + + See discussion at: + https://groups.google.com/forum/#!msg/python-ideas/XcuC01a8SYs/e-doB9TbDwAJ + """ + hash_value = hash(type(it)) + for value in it: + hash_value = hash((hash_value, value)) + return hash_value diff --git a/_vendor/collections_extended/bags.py b/_vendor/collections_extended/bags.py new file mode 100644 index 0000000000..cce132fe42 --- /dev/null +++ b/_vendor/collections_extended/bags.py @@ -0,0 +1,527 @@ +"""Bag class definitions.""" +import heapq +from operator import itemgetter +from collections import Set, MutableSet, Hashable + +from . import _compat + + +class _basebag(Set): + """Base class for bag classes. + + Base class for bag and frozenbag. Is not mutable and not hashable, so there's + no reason to use this instead of either bag or frozenbag. + """ + + # Basic object methods + + def __init__(self, iterable=None): + """Create a new basebag. + + If iterable isn't given, is None or is empty then the bag starts empty. + Otherwise each element from iterable will be added to the bag + however many times it appears. + + This runs in O(len(iterable)) + """ + self._dict = dict() + self._size = 0 + if iterable: + if isinstance(iterable, _basebag): + for elem, count in iterable._dict.items(): + self._dict[elem] = count + self._size += count + else: + for value in iterable: + self._dict[value] = self._dict.get(value, 0) + 1 + self._size += 1 + + def __repr__(self): + if self._size == 0: + return '{0}()'.format(self.__class__.__name__) + else: + repr_format = '{class_name}({values!r})' + return repr_format.format( + class_name=self.__class__.__name__, + values=tuple(self), + ) + + def __str__(self): + if self._size == 0: + return '{class_name}()'.format(class_name=self.__class__.__name__) + else: + format_single = '{elem!r}' + format_mult = '{elem!r}^{mult}' + strings = [] + for elem, mult in self._dict.items(): + if mult > 1: + strings.append(format_mult.format(elem=elem, mult=mult)) + else: + strings.append(format_single.format(elem=elem)) + return '{%s}' % ', '.join(strings) + + # New public methods (not overriding/implementing anything) + + def num_unique_elements(self): + """Return the number of unique elements. + + This runs in O(1) time + """ + return len(self._dict) + + def unique_elements(self): + """Return a view of unique elements in this bag. + + In Python 3: + This runs in O(1) time and returns a view of the unique elements + In Python 2: + This runs in O(n) and returns set of the current elements. + """ + return _compat.keys_set(self._dict) + + def count(self, value): + """Return the number of value present in this bag. + + If value is not in the bag no Error is raised, instead 0 is returned. + + This runs in O(1) time + + Args: + value: The element of self to get the count of + Returns: + int: The count of value in self + """ + return self._dict.get(value, 0) + + def nlargest(self, n=None): + """List the n most common elements and their counts. + + List is from the most + common to the least. If n is None, the list all element counts. + + Run time should be O(m log m) where m is len(self) + Args: + n (int): The number of elements to return + """ + if n is None: + return sorted(self._dict.items(), key=itemgetter(1), reverse=True) + else: + return heapq.nlargest(n, self._dict.items(), key=itemgetter(1)) + + @classmethod + def _from_iterable(cls, it): + return cls(it) + + @classmethod + def from_mapping(cls, mapping): + """Create a bag from a dict of elem->count. + + Each key in the dict is added if the value is > 0. + """ + out = cls() + for elem, count in mapping.items(): + if count > 0: + out._dict[elem] = count + out._size += count + return out + + def copy(self): + """Create a shallow copy of self. + + This runs in O(len(self.num_unique_elements())) + """ + return self.from_mapping(self._dict) + + # implementing Sized methods + + def __len__(self): + """Return the cardinality of the bag. + + This runs in O(1) + """ + return self._size + + # implementing Container methods + + def __contains__(self, value): + """Return the multiplicity of the element. + + This runs in O(1) + """ + return self._dict.get(value, 0) + + # implementing Iterable methods + + def __iter__(self): + """Iterate through all elements. + + Multiple copies will be returned if they exist. + """ + for value, count in self._dict.items(): + for i in range(count): + yield(value) + + # Comparison methods + + def _is_subset(self, other): + """Check that every element in self has a count <= in other. + + Args: + other (Set) + """ + if isinstance(other, _basebag): + for elem, count in self._dict.items(): + if not count <= other._dict.get(elem, 0): + return False + else: + for elem in self: + if self._dict.get(elem, 0) > 1 or elem not in other: + return False + return True + + def _is_superset(self, other): + """Check that every element in self has a count >= in other. + + Args: + other (Set) + """ + if isinstance(other, _basebag): + for elem, count in other._dict.items(): + if not self._dict.get(elem, 0) >= count: + return False + else: + for elem in other: + if elem not in self: + return False + return True + + def __le__(self, other): + if not isinstance(other, Set): + return _compat.handle_rich_comp_not_implemented() + return len(self) <= len(other) and self._is_subset(other) + + def __lt__(self, other): + if not isinstance(other, Set): + return _compat.handle_rich_comp_not_implemented() + return len(self) < len(other) and self._is_subset(other) + + def __gt__(self, other): + if not isinstance(other, Set): + return _compat.handle_rich_comp_not_implemented() + return len(self) > len(other) and self._is_superset(other) + + def __ge__(self, other): + if not isinstance(other, Set): + return _compat.handle_rich_comp_not_implemented() + return len(self) >= len(other) and self._is_superset(other) + + def __eq__(self, other): + if not isinstance(other, Set): + return False + if isinstance(other, _basebag): + return self._dict == other._dict + if not len(self) == len(other): + return False + for elem in other: + if self._dict.get(elem, 0) != 1: + return False + return True + + def __ne__(self, other): + return not (self == other) + + # Operations - &, |, +, -, ^, * and isdisjoint + + def __and__(self, other): + """Intersection is the minimum of corresponding counts. + + This runs in O(l + n) where: + n is self.num_unique_elements() + if other is a bag: + l = 1 + else: + l = len(other) + """ + if not isinstance(other, _basebag): + other = self._from_iterable(other) + values = dict() + for elem in self._dict: + values[elem] = min(other._dict.get(elem, 0), self._dict.get(elem, 0)) + return self.from_mapping(values) + + def isdisjoint(self, other): + """Return if this bag is disjoint with the passed collection. + + This runs in O(len(other)) + + TODO move isdisjoint somewhere more appropriate + """ + for value in other: + if value in self: + return False + return True + + def __or__(self, other): + """Union is the maximum of all elements. + + This runs in O(m + n) where: + n is self.num_unique_elements() + if other is a bag: + m = other.num_unique_elements() + else: + m = len(other) + """ + if not isinstance(other, _basebag): + other = self._from_iterable(other) + values = dict() + for elem in self.unique_elements() | other.unique_elements(): + values[elem] = max(self._dict.get(elem, 0), other._dict.get(elem, 0)) + return self.from_mapping(values) + + def __add__(self, other): + """Return a new bag also containing all the elements of other. + + self + other = self & other + self | other + + This runs in O(m + n) where: + n is self.num_unique_elements() + m is len(other) + Args: + other (Iterable): elements to add to self + """ + out = self.copy() + for value in other: + out._dict[value] = out._dict.get(value, 0) + 1 + out._size += 1 + return out + + def __sub__(self, other): + """Difference between the sets. + + For normal sets this is all x s.t. x in self and x not in other. + For bags this is count(x) = max(0, self.count(x)-other.count(x)) + + This runs in O(m + n) where: + n is self.num_unique_elements() + m is len(other) + Args: + other (Iterable): elements to remove + """ + out = self.copy() + for value in other: + old_count = out._dict.get(value, 0) + if old_count == 1: + del out._dict[value] + out._size -= 1 + elif old_count > 1: + out._dict[value] = old_count - 1 + out._size -= 1 + return out + + def __mul__(self, other): + """Cartesian product of the two sets. + + other can be any iterable. + Both self and other must contain elements that can be added together. + + This should run in O(m*n+l) where: + m is the number of unique elements in self + n is the number of unique elements in other + if other is a bag: + l is 0 + else: + l is the len(other) + The +l will only really matter when other is an iterable with MANY + repeated elements. + For example: {'a'^2} * 'bbbbbbbbbbbbbbbbbbbbbbbbbb' + The algorithm will be dominated by counting the 'b's + """ + if not isinstance(other, _basebag): + other = self._from_iterable(other) + values = dict() + for elem, count in self._dict.items(): + for other_elem, other_count in other._dict.items(): + new_elem = elem + other_elem + new_count = count * other_count + values[new_elem] = new_count + return self.from_mapping(values) + + def __xor__(self, other): + """Symmetric difference between the sets. + + other can be any iterable. + + This runs in O(m + n) where: + m = len(self) + n = len(other) + """ + return (self - other) | (other - self) + + +class bag(_basebag, MutableSet): + """bag is a mutable unhashable bag.""" + + def pop(self): + """Remove and return an element of self.""" + # TODO can this be done more efficiently (no need to create an iterator)? + it = iter(self) + try: + value = next(it) + except StopIteration: + raise KeyError + self.discard(value) + return value + + def add(self, elem): + """Add elem to self.""" + self._dict[elem] = self._dict.get(elem, 0) + 1 + self._size += 1 + + def discard(self, elem): + """Remove elem from this bag, silent if it isn't present.""" + try: + self.remove(elem) + except ValueError: + pass + + def remove(self, elem): + """Remove elem from this bag, raising a ValueError if it isn't present. + + Args: + elem: object to remove from self + Raises: + ValueError: if the elem isn't present + """ + old_count = self._dict.get(elem, 0) + if old_count == 0: + raise ValueError + elif old_count == 1: + del self._dict[elem] + else: + self._dict[elem] -= 1 + self._size -= 1 + + def discard_all(self, other): + """Discard all of the elems from other.""" + if not isinstance(other, _basebag): + other = self._from_iterable(other) + for elem, other_count in other._dict.items(): + old_count = self._dict.get(elem, 0) + new_count = old_count - other_count + if new_count >= 0: + if new_count == 0: + if elem in self: + del self._dict[elem] + else: + self._dict[elem] = new_count + self._size += new_count - old_count + + def remove_all(self, other): + """Remove all of the elems from other. + + Raises a ValueError if the multiplicity of any elem in other is greater + than in self. + """ + if not self._is_superset(other): + raise ValueError + self.discard_all(other) + + def clear(self): + """Remove all elements from this bag.""" + self._dict = dict() + self._size = 0 + + # In-place operations + + def __ior__(self, other): + """Set multiplicity of each element to the maximum of the two collections. + + if isinstance(other, _basebag): + This runs in O(other.num_unique_elements()) + else: + This runs in O(len(other)) + """ + if not isinstance(other, _basebag): + other = self._from_iterable(other) + for elem, other_count in other._dict.items(): + old_count = self._dict.get(elem, 0) + new_count = max(other_count, old_count) + self._dict[elem] = new_count + self._size += new_count - old_count + return self + + def __iand__(self, other): + """Set multiplicity of each element to the minimum of the two collections. + + if isinstance(other, _basebag): + This runs in O(other.num_unique_elements()) + else: + This runs in O(len(other)) + """ + if not isinstance(other, _basebag): + other = self._from_iterable(other) + for elem, old_count in set(self._dict.items()): + other_count = other._dict.get(elem, 0) + new_count = min(other_count, old_count) + if new_count == 0: + del self._dict[elem] + else: + self._dict[elem] = new_count + self._size += new_count - old_count + return self + + def __ixor__(self, other): + """Set self to the symmetric difference between the sets. + + if isinstance(other, _basebag): + This runs in O(other.num_unique_elements()) + else: + This runs in O(len(other)) + """ + if not isinstance(other, _basebag): + other = self._from_iterable(other) + other_minus_self = other - self + self -= other + self |= other_minus_self + return self + + def __isub__(self, other): + """Discard the elements of other from self. + + if isinstance(it, _basebag): + This runs in O(it.num_unique_elements()) + else: + This runs in O(len(it)) + """ + self.discard_all(other) + return self + + def __iadd__(self, other): + """Add all of the elements of other to self. + + if isinstance(it, _basebag): + This runs in O(it.num_unique_elements()) + else: + This runs in O(len(it)) + """ + if not isinstance(other, _basebag): + other = self._from_iterable(other) + for elem, other_count in other._dict.items(): + self._dict[elem] = self._dict.get(elem, 0) + other_count + self._size += other_count + return self + + +class frozenbag(_basebag, Hashable): + """frozenbag is an immutable, hashable bab.""" + + def __hash__(self): + """Compute the hash value of a frozenbag. + + This was copied directly from _collections_abc.Set._hash in Python3 which + is identical to _abcoll.Set._hash + We can't call it directly because Python2 raises a TypeError. + """ + if not hasattr(self, '_hash_value'): + self._hash_value = self._hash() + return self._hash_value diff --git a/_vendor/collections_extended/bijection.py b/_vendor/collections_extended/bijection.py new file mode 100644 index 0000000000..f9641de4d1 --- /dev/null +++ b/_vendor/collections_extended/bijection.py @@ -0,0 +1,94 @@ +"""Class definition for bijection.""" + +from collections import MutableMapping, Mapping + + +class bijection(MutableMapping): + """A one-to-one onto mapping, a dict with unique values.""" + + def __init__(self, iterable=None, **kwarg): + """Create a bijection from an iterable. + + Matches dict.__init__. + """ + self._data = {} + self.__inverse = self.__new__(bijection) + self.__inverse._data = {} + self.__inverse.__inverse = self + if iterable is not None: + if isinstance(iterable, Mapping): + for key, value in iterable.items(): + self[key] = value + else: + for pair in iterable: + self[pair[0]] = pair[1] + for key, value in kwarg.items(): + self[key] = value + + def __repr__(self): + if len(self._data) == 0: + return '{0}()'.format(self.__class__.__name__) + else: + repr_format = '{class_name}({values!r})' + return repr_format.format( + class_name=self.__class__.__name__, + values=self._data, + ) + + @property + def inverse(self): + """Return the inverse of this bijection.""" + return self.__inverse + + # Required for MutableMapping + def __len__(self): + return len(self._data) + + # Required for MutableMapping + def __getitem__(self, key): + return self._data[key] + + # Required for MutableMapping + def __setitem__(self, key, value): + if key in self: + del self.inverse._data[self[key]] + if value in self.inverse: + del self._data[self.inverse[value]] + self._data[key] = value + self.inverse._data[value] = key + + # Required for MutableMapping + def __delitem__(self, key): + value = self._data.pop(key) + del self.inverse._data[value] + + # Required for MutableMapping + def __iter__(self): + return iter(self._data) + + def __contains__(self, key): + return key in self._data + + def clear(self): + """Remove everything from this bijection.""" + self._data.clear() + self.inverse._data.clear() + + def copy(self): + """Return a copy of this bijection.""" + return bijection(self) + + def items(self): + """See Mapping.items.""" + return self._data.items() + + def keys(self): + """See Mapping.keys.""" + return self._data.keys() + + def values(self): + """See Mapping.values.""" + return self.inverse.keys() + + def __eq__(self, other): + return isinstance(other, bijection) and self._data == other._data diff --git a/_vendor/collections_extended/range_map.py b/_vendor/collections_extended/range_map.py new file mode 100644 index 0000000000..19a612388b --- /dev/null +++ b/_vendor/collections_extended/range_map.py @@ -0,0 +1,384 @@ +"""RangeMap class definition.""" +from bisect import bisect_left, bisect_right +from collections import namedtuple, Mapping, MappingView, Set + + +# Used to mark unmapped ranges +_empty = object() + +MappedRange = namedtuple('MappedRange', ('start', 'stop', 'value')) + + +class KeysView(MappingView, Set): + """A view of the keys that mark the starts of subranges. + + Since iterating over all the keys is impossible, the KeysView only + contains the keys that start each subrange. + """ + + __slots__ = () + + @classmethod + def _from_iterable(self, it): + return set(it) + + def __contains__(self, key): + loc = self._mapping._bisect_left(key) + return self._mapping._keys[loc] == key and \ + self._mapping._values[loc] is not _empty + + def __iter__(self): + for item in self._mapping.ranges(): + yield item.start + + +class ItemsView(MappingView, Set): + """A view of the items that mark the starts of subranges. + + Since iterating over all the keys is impossible, the ItemsView only + contains the items that start each subrange. + """ + + __slots__ = () + + @classmethod + def _from_iterable(self, it): + return set(it) + + def __contains__(self, item): + key, value = item + loc = self._mapping._bisect_left(key) + return self._mapping._keys[loc] == key and \ + self._mapping._values[loc] == value + + def __iter__(self): + for mapped_range in self._mapping.ranges(): + yield (mapped_range.start, mapped_range.value) + + +class ValuesView(MappingView): + """A view on the values of a Mapping.""" + + __slots__ = () + + def __contains__(self, value): + return value in self._mapping._values + + def __iter__(self): + for value in self._mapping._values: + if value is not _empty: + yield value + + +def _check_start_stop(start, stop): + """Check that start and stop are valid - orderable and in the right order. + + Raises: + ValueError: if stop <= start + TypeError: if unorderable + """ + if start is not None and stop is not None and stop <= start: + raise ValueError('stop must be > start') + + +def _check_key_slice(key): + if not isinstance(key, slice): + raise TypeError('Can only set and delete slices') + if key.step is not None: + raise ValueError('Cannot set or delete slices with steps') + + +class RangeMap(Mapping): + """Map ranges of orderable elements to values.""" + + def __init__(self, iterable=None, **kwargs): + """Create a RangeMap. + + A mapping or other iterable can be passed to initialize the RangeMap. + If mapping is passed, it is interpreted as a mapping from range start + indices to values. + If an iterable is passed, each element will define a range in the + RangeMap and should be formatted (start, stop, value). + + default_value is a an optional keyword argument that will initialize the + entire RangeMap to that value. Any missing ranges will be mapped to that + value. However, if ranges are subsequently deleted they will be removed + and *not* mapped to the default_value. + + Args: + iterable: A Mapping or an Iterable to initialize from. + default_value: If passed, the return value for all keys less than the + least key in mapping or missing ranges in iterable. If no mapping + or iterable, the return value for all keys. + """ + default_value = kwargs.pop('default_value', _empty) + if kwargs: + raise TypeError('Unknown keyword arguments: %s' % ', '.join(kwargs.keys())) + self._keys = [None] + self._values = [default_value] + if iterable: + if isinstance(iterable, Mapping): + self._init_from_mapping(iterable) + else: + self._init_from_iterable(iterable) + + @classmethod + def from_mapping(cls, mapping): + """Create a RangeMap from a mapping of interval starts to values.""" + obj = cls() + obj._init_from_mapping(mapping) + return obj + + def _init_from_mapping(self, mapping): + for key, value in sorted(mapping.items()): + self.set(value, key) + + @classmethod + def from_iterable(cls, iterable): + """Create a RangeMap from an iterable of tuples defining each range. + + Each element of the iterable is a tuple (start, stop, value). + """ + obj = cls() + obj._init_from_iterable(iterable) + return obj + + def _init_from_iterable(self, iterable): + for start, stop, value in iterable: + self.set(value, start=start, stop=stop) + + def __str__(self): + range_format = '({range.start}, {range.stop}): {range.value}' + values = ', '.join([range_format.format(range=r) for r in self.ranges()]) + return 'RangeMap(%s)' % values + + def __repr__(self): + range_format = '({range.start!r}, {range.stop!r}, {range.value!r})' + values = ', '.join([range_format.format(range=r) for r in self.ranges()]) + return 'RangeMap([%s])' % values + + def _bisect_left(self, key): + """Return the index of the key or the last key < key.""" + if key is None: + return 0 + else: + return bisect_left(self._keys, key, lo=1) + + def _bisect_right(self, key): + """Return the index of the first key > key.""" + if key is None: + return 1 + else: + return bisect_right(self._keys, key, lo=1) + + def ranges(self, start=None, stop=None): + """Generate MappedRanges for all mapped ranges. + + Yields: + MappedRange + """ + _check_start_stop(start, stop) + start_loc = self._bisect_right(start) + if stop is None: + stop_loc = len(self._keys) + else: + stop_loc = self._bisect_left(stop) + start_val = self._values[start_loc - 1] + candidate_keys = [start] + self._keys[start_loc:stop_loc] + [stop] + candidate_values = [start_val] + self._values[start_loc:stop_loc] + for i, value in enumerate(candidate_values): + if value is not _empty: + start_key = candidate_keys[i] + stop_key = candidate_keys[i + 1] + yield MappedRange(start_key, stop_key, value) + + def __contains__(self, value): + try: + self.__getitem(value) is not _empty + except KeyError: + return False + else: + return True + + def __iter__(self): + for key, value in zip(self._keys, self._values): + if value is not _empty: + yield key + + def __bool__(self): + if len(self._keys) > 1: + return True + else: + return self._values[0] != _empty + + __nonzero__ = __bool__ + + def __getitem(self, key): + """Get the value for a key (not a slice).""" + loc = self._bisect_right(key) - 1 + value = self._values[loc] + if value is _empty: + raise KeyError(key) + else: + return value + + def get(self, key, restval=None): + """Get the value of the range containing key, otherwise return restval.""" + try: + return self.__getitem(key) + except KeyError: + return restval + + def get_range(self, start=None, stop=None): + """Return a RangeMap for the range start to stop. + + Returns: + A RangeMap + """ + return self.from_iterable(self.ranges(start, stop)) + + def set(self, value, start=None, stop=None): + """Set the range from start to stop to value.""" + _check_start_stop(start, stop) + # start_index, stop_index will denote the sections we are replacing + start_index = self._bisect_left(start) + if start is not None: # start_index == 0 + prev_value = self._values[start_index - 1] + if prev_value == value: + # We're setting a range where the left range has the same + # value, so create one big range + start_index -= 1 + start = self._keys[start_index] + if stop is None: + new_keys = [start] + new_values = [value] + stop_index = len(self._keys) + else: + stop_index = self._bisect_right(stop) + stop_value = self._values[stop_index - 1] + stop_key = self._keys[stop_index - 1] + if stop_key == stop and stop_value == value: + new_keys = [start] + new_values = [value] + else: + new_keys = [start, stop] + new_values = [value, stop_value] + self._keys[start_index:stop_index] = new_keys + self._values[start_index:stop_index] = new_values + + def delete(self, start=None, stop=None): + """Delete the range from start to stop from self. + + Raises: + KeyError: If part of the passed range isn't mapped. + """ + _check_start_stop(start, stop) + start_loc = self._bisect_right(start) - 1 + if stop is None: + stop_loc = len(self._keys) + else: + stop_loc = self._bisect_left(stop) + for value in self._values[start_loc:stop_loc]: + if value is _empty: + raise KeyError((start, stop)) + # this is inefficient, we've already found the sub ranges + self.set(_empty, start=start, stop=stop) + + def empty(self, start=None, stop=None): + """Empty the range from start to stop. + + Like delete, but no Error is raised if the entire range isn't mapped. + """ + self.set(_empty, start=start, stop=stop) + + def clear(self): + """Remove all elements.""" + self._keys = [None] + self._values = [_empty] + + @property + def start(self): + """Get the start key of the first range. + + None if RangeMap is empty or unbounded to the left. + """ + if self._values[0] is _empty: + try: + return self._keys[1] + except IndexError: + # This is empty or everything is mapped to a single value + return None + else: + # This is unbounded to the left + return self._keys[0] + + @property + def end(self): + """Get the stop key of the last range. + + None if RangeMap is empty or unbounded to the right. + """ + if self._values[-1] is _empty: + return self._keys[-1] + else: + # This is unbounded to the right + return None + + def __eq__(self, other): + if isinstance(other, RangeMap): + return ( + self._keys == other._keys and + self._values == other._values + ) + else: + return False + + def __getitem__(self, key): + try: + _check_key_slice(key) + except TypeError: + return self.__getitem(key) + else: + return self.get_range(key.start, key.stop) + + def __setitem__(self, key, value): + _check_key_slice(key) + self.set(value, key.start, key.stop) + + def __delitem__(self, key): + _check_key_slice(key) + self.delete(key.start, key.stop) + + def __len__(self): + count = 0 + for v in self._values: + if v is not _empty: + count += 1 + return count + + def keys(self): + """Return a view of the keys.""" + return KeysView(self) + + def values(self): + """Return a view of the values.""" + return ValuesView(self) + + def items(self): + """Return a view of the item pairs.""" + return ItemsView(self) + + # Python2 - override slice methods + def __setslice__(self, i, j, value): + """Implement __setslice__ to override behavior in Python 2. + + This is required because empty slices pass integers in python2 as opposed + to None in python 3. + """ + raise SyntaxError('Assigning slices doesn\t work in Python 2, use set') + + def __delslice__(self, i, j): + raise SyntaxError('Deleting slices doesn\t work in Python 2, use delete') + + def __getslice__(self, i, j): + raise SyntaxError('Getting slices doesn\t work in Python 2, use get_range.') diff --git a/_vendor/collections_extended/setlists.py b/_vendor/collections_extended/setlists.py new file mode 100644 index 0000000000..2976077c73 --- /dev/null +++ b/_vendor/collections_extended/setlists.py @@ -0,0 +1,552 @@ +"""Setlist class definitions.""" +import random as random_ + +from collections import ( + Sequence, + Set, + MutableSequence, + MutableSet, + Hashable, + ) + +from . import _util + + +class _basesetlist(Sequence, Set): + """A setlist is an ordered Collection of unique elements. + + _basesetlist is the superclass of setlist and frozensetlist. It is immutable + and unhashable. + """ + + def __init__(self, iterable=None, raise_on_duplicate=False): + """Create a setlist. + + Args: + iterable (Iterable): Values to initialize the setlist with. + """ + self._list = list() + self._dict = dict() + if iterable: + if raise_on_duplicate: + self._extend(iterable) + else: + self._update(iterable) + + def __repr__(self): + if len(self) == 0: + return '{0}()'.format(self.__class__.__name__) + else: + repr_format = '{class_name}({values!r})' + return repr_format.format( + class_name=self.__class__.__name__, + values=tuple(self), + ) + + # Convenience methods + def _fix_neg_index(self, index): + if index < 0: + index += len(self) + if index < 0: + raise IndexError('index is out of range') + return index + + def _fix_end_index(self, index): + if index is None: + return len(self) + else: + return self._fix_neg_index(index) + + def _append(self, value): + # Checking value in self will check that value is Hashable + if value in self: + raise ValueError('Value "%s" already present' % str(value)) + else: + self._dict[value] = len(self) + self._list.append(value) + + def _extend(self, values): + new_values = set() + for value in values: + if value in new_values: + raise ValueError('New values contain duplicates') + elif value in self: + raise ValueError('New values contain elements already present in self') + else: + new_values.add(value) + for value in values: + self._dict[value] = len(self) + self._list.append(value) + + def _add(self, item): + if item not in self: + self._dict[item] = len(self) + self._list.append(item) + + def _update(self, values): + for value in values: + if value not in self: + self._dict[value] = len(self) + self._list.append(value) + + @classmethod + def _from_iterable(cls, it, **kwargs): + return cls(it, **kwargs) + + # Implement Container + def __contains__(self, value): + return value in self._dict + + # Iterable we get by inheriting from Sequence + + # Implement Sized + def __len__(self): + return len(self._list) + + # Implement Sequence + def __getitem__(self, index): + if isinstance(index, slice): + return self._from_iterable(self._list[index]) + return self._list[index] + + def count(self, value): + """Return the number of occurences of value in self. + + This runs in O(1) + + Args: + value: The value to count + Returns: + int: 1 if the value is in the setlist, otherwise 0 + """ + if value in self: + return 1 + else: + return 0 + + def index(self, value, start=0, end=None): + """Return the index of value between start and end. + + By default, the entire setlist is searched. + + This runs in O(1) + + Args: + value: The value to find the index of + start (int): The index to start searching at (defaults to 0) + end (int): The index to stop searching at (defaults to the end of the list) + Returns: + int: The index of the value + Raises: + ValueError: If the value is not in the list or outside of start - end + IndexError: If start or end are out of range + """ + try: + index = self._dict[value] + except KeyError: + raise ValueError + else: + start = self._fix_neg_index(start) + end = self._fix_end_index(end) + if start <= index and index < end: + return index + else: + raise ValueError + + @classmethod + def _check_type(cls, other, operand_name): + if not isinstance(other, _basesetlist): + message = ( + "unsupported operand type(s) for {operand_name}: " + "'{self_type}' and '{other_type}'").format( + operand_name=operand_name, + self_type=cls, + other_type=type(other), + ) + raise TypeError(message) + + def __add__(self, other): + self._check_type(other, '+') + out = self.copy() + out._extend(other) + return out + + # Implement Set + + def issubset(self, other): + return self <= other + + def issuperset(self, other): + return self >= other + + def union(self, other): + out = self.copy() + out.update(other) + return out + + def intersection(self, other): + other = set(other) + return self._from_iterable(item for item in self if item in other) + + def difference(self, other): + other = set(other) + return self._from_iterable(item for item in self if item not in other) + + def symmetric_difference(self, other): + return self.union(other) - self.intersection(other) + + def __sub__(self, other): + self._check_type(other, '-') + return self.difference(other) + + def __and__(self, other): + self._check_type(other, '&') + return self.intersection(other) + + def __or__(self, other): + self._check_type(other, '|') + return self.union(other) + + def __xor__(self, other): + self._check_type(other, '^') + return self.symmetric_difference(other) + + # Comparison + + def __eq__(self, other): + if not isinstance(other, _basesetlist): + return False + if not len(self) == len(other): + return False + for self_elem, other_elem in zip(self, other): + if self_elem != other_elem: + return False + return True + + def __ne__(self, other): + return not (self == other) + + # New methods + + def sub_index(self, sub, start=0, end=None): + """Return the index of a subsequence. + + This runs in O(len(sub)) + + Args: + sub (Sequence): An Iterable to search for + Returns: + int: The index of the first element of sub + Raises: + ValueError: If sub isn't a subsequence + TypeError: If sub isn't iterable + IndexError: If start or end are out of range + """ + start_index = self.index(sub[0], start, end) + end = self._fix_end_index(end) + if start_index + len(sub) > end: + raise ValueError + for i in range(1, len(sub)): + if sub[i] != self[start_index + i]: + raise ValueError + return start_index + + def copy(self): + return self.__class__(self) + + +class setlist(_basesetlist, MutableSequence, MutableSet): + """A mutable (unhashable) setlist.""" + + def __str__(self): + return '{[%s}]' % ', '.join(repr(v) for v in self) + + # Helper methods + def _delete_all(self, elems_to_delete, raise_errors): + indices_to_delete = set() + for elem in elems_to_delete: + try: + elem_index = self._dict[elem] + except KeyError: + if raise_errors: + raise ValueError('Passed values contain elements not in self') + else: + if elem_index in indices_to_delete: + if raise_errors: + raise ValueError('Passed vales contain duplicates') + indices_to_delete.add(elem_index) + self._delete_values_by_index(indices_to_delete) + + def _delete_values_by_index(self, indices_to_delete): + deleted_count = 0 + for i, elem in enumerate(self._list): + if i in indices_to_delete: + deleted_count += 1 + del self._dict[elem] + else: + new_index = i - deleted_count + self._list[new_index] = elem + self._dict[elem] = new_index + # Now remove deleted_count items from the end of the list + if deleted_count: + self._list = self._list[:-deleted_count] + + # Set/Sequence agnostic + def pop(self, index=-1): + """Remove and return the item at index.""" + value = self._list.pop(index) + del self._dict[value] + return value + + def clear(self): + """Remove all elements from self.""" + self._dict = dict() + self._list = list() + + # Implement MutableSequence + def __setitem__(self, index, value): + if isinstance(index, slice): + old_values = self[index] + for v in value: + if v in self and v not in old_values: + raise ValueError + self._list[index] = value + self._dict = {} + for i, v in enumerate(self._list): + self._dict[v] = i + else: + index = self._fix_neg_index(index) + old_value = self._list[index] + if value in self: + if value == old_value: + return + else: + raise ValueError + del self._dict[old_value] + self._list[index] = value + self._dict[value] = index + + def __delitem__(self, index): + if isinstance(index, slice): + indices_to_delete = set(self.index(e) for e in self._list[index]) + self._delete_values_by_index(indices_to_delete) + else: + index = self._fix_neg_index(index) + value = self._list[index] + del self._dict[value] + for elem in self._list[index + 1:]: + self._dict[elem] -= 1 + del self._list[index] + + def insert(self, index, value): + """Insert value at index. + + Args: + index (int): Index to insert value at + value: Value to insert + Raises: + ValueError: If value already in self + IndexError: If start or end are out of range + """ + if value in self: + raise ValueError + index = self._fix_neg_index(index) + self._dict[value] = index + for elem in self._list[index:]: + self._dict[elem] += 1 + self._list.insert(index, value) + + def append(self, value): + """Append value to the end. + + Args: + value: Value to append + Raises: + ValueError: If value alread in self + TypeError: If value isn't hashable + """ + self._append(value) + + def extend(self, values): + """Append all values to the end. + + If any of the values are present, ValueError will + be raised and none of the values will be appended. + + Args: + values (Iterable): Values to append + Raises: + ValueError: If any values are already present or there are duplicates + in the passed values. + TypeError: If any of the values aren't hashable. + """ + self._extend(values) + + def __iadd__(self, values): + """Add all values to the end of self. + + Args: + values (Iterable): Values to append + Raises: + ValueError: If any values are already present + """ + self._check_type(values, '+=') + self.extend(values) + return self + + def remove(self, value): + """Remove value from self. + + Args: + value: Element to remove from self + Raises: + ValueError: if element is already present + """ + try: + index = self._dict[value] + except KeyError: + raise ValueError('Value "%s" is not present.') + else: + del self[index] + + def remove_all(self, elems_to_delete): + """Remove all elements from elems_to_delete, raises ValueErrors. + + See Also: + discard_all + Args: + elems_to_delete (Iterable): Elements to remove. + Raises: + ValueError: If the count of any element is greater in + elems_to_delete than self. + TypeError: If any of the values aren't hashable. + """ + self._delete_all(elems_to_delete, raise_errors=True) + + # Implement MutableSet + + def add(self, item): + """Add an item. + + Note: + This does not raise a ValueError for an already present value like + append does. This is to match the behavior of set.add + Args: + item: Item to add + Raises: + TypeError: If item isn't hashable. + """ + self._add(item) + + def update(self, values): + """Add all values to the end. + + If any of the values are present, silently ignore + them (as opposed to extend which raises an Error). + + See also: + extend + Args: + values (Iterable): Values to add + Raises: + TypeError: If any of the values are unhashable. + """ + self._update(values) + + def discard_all(self, elems_to_delete): + """Discard all the elements from elems_to_delete. + + This is much faster than removing them one by one. + This runs in O(len(self) + len(elems_to_delete)) + + Args: + elems_to_delete (Iterable): Elements to discard. + Raises: + TypeError: If any of the values aren't hashable. + """ + self._delete_all(elems_to_delete, raise_errors=False) + + def discard(self, value): + """Discard an item. + + Note: + This does not raise a ValueError for a missing value like remove does. + This is to match the behavior of set.discard + """ + try: + self.remove(value) + except ValueError: + pass + + def difference_update(self, other): + """Update self to include only the differene with other.""" + other = set(other) + indices_to_delete = set() + for i, elem in enumerate(self): + if elem in other: + indices_to_delete.add(i) + if indices_to_delete: + self._delete_values_by_index(indices_to_delete) + + def intersection_update(self, other): + """Update self to include only the intersection with other.""" + other = set(other) + indices_to_delete = set() + for i, elem in enumerate(self): + if elem not in other: + indices_to_delete.add(i) + if indices_to_delete: + self._delete_values_by_index(indices_to_delete) + + def symmetric_difference_update(self, other): + """Update self to include only the symmetric difference with other.""" + other = setlist(other) + indices_to_delete = set() + for i, item in enumerate(self): + if item in other: + indices_to_delete.add(i) + for item in other: + self.add(item) + self._delete_values_by_index(indices_to_delete) + + def __isub__(self, other): + self._check_type(other, '-=') + self.difference_update(other) + return self + + def __iand__(self, other): + self._check_type(other, '&=') + self.intersection_update(other) + return self + + def __ior__(self, other): + self._check_type(other, '|=') + self.update(other) + return self + + def __ixor__(self, other): + self._check_type(other, '^=') + self.symmetric_difference_update(other) + return self + + # New methods + def shuffle(self, random=None): + """Shuffle all of the elements in self randomly.""" + random_.shuffle(self._list, random=random) + for i, elem in enumerate(self._list): + self._dict[elem] = i + + def sort(self, *args, **kwargs): + """Sort this setlist in place.""" + self._list.sort(*args, **kwargs) + for index, value in enumerate(self._list): + self._dict[value] = index + + +class frozensetlist(_basesetlist, Hashable): + """An immutable (hashable) setlist.""" + + def __hash__(self): + if not hasattr(self, '_hash_value'): + self._hash_value = _util.hash_iterable(self) + return self._hash_value From 18f12750509a732506cb8cea1112e432ff0ffffd Mon Sep 17 00:00:00 2001 From: cassidoxa <43386495+cassidoxa@users.noreply.github.com> Date: Sat, 27 Jul 2019 09:13:13 -0400 Subject: [PATCH 14/52] Inverted Mode (#5) Merging in Cassidoxa's inverted mode. I've still not fully reviewed the logic used in this mode, so it should be considered experimental, pending an in depth review by either myself or AA. --- BaseClasses.py | 30 +- Dungeons.py | 9 +- EntranceRandomizer.py | 6 +- EntranceShuffle.py | 1628 +++++++++++++++++++++++++++++++++++++++-- Gui.py | 2 +- InvertedRegions.py | 642 ++++++++++++++++ ItemList.py | 4 +- Main.py | 42 +- README.md | 12 +- Rom.py | 278 ++++++- Rules.py | 680 ++++++++++++++++- Text.py | 3 +- 12 files changed, 3223 insertions(+), 113 deletions(-) create mode 100644 InvertedRegions.py diff --git a/BaseClasses.py b/BaseClasses.py index 29b33a885d..2466a3b763 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -431,7 +431,7 @@ class CollectionState(object): self.has('Bug Catching Net', player) and (self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))) and cave.can_reach(self) and - (cave.is_light_world or self.has_Pearl(player)) + self.is_not_bunny(cave, player) ) def has_sword(self, player): @@ -455,6 +455,22 @@ class CollectionState(object): def has_fire_source(self, player): return self.has('Fire Rod', player) or self.has('Lamp', player) + def can_flute(self, player): + lw = self.world.get_region('Light World', player) + return self.has('Ocarina', player) and lw.can_reach(self) and self.is_not_bunny(lw, player) + + def can_melt_things(self, player): + return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player)) + + def can_avoid_lasers(self, player): + return self.has('Mirror Shield', player) or self.has('Cane of Byrna', player) or self.has('Cape', player) + + def is_not_bunny(self, region, player): + if self.has_Pearl(player): + return True + + return region.is_light_world if self.world.mode != 'inverted' else region.is_dark_world + def has_misery_mire_medallion(self, player): return self.has(self.world.required_medallions[player][0], player) @@ -933,9 +949,15 @@ class Spoiler(object): self.bosses[str(player)]["Ice Palace"] = self.world.get_dungeon("Ice Palace", player).boss.name self.bosses[str(player)]["Misery Mire"] = self.world.get_dungeon("Misery Mire", player).boss.name self.bosses[str(player)]["Turtle Rock"] = self.world.get_dungeon("Turtle Rock", player).boss.name - self.bosses[str(player)]["Ganons Tower Basement"] = self.world.get_dungeon('Ganons Tower', player).bosses['bottom'].name - self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower', player).bosses['middle'].name - self.bosses[str(player)]["Ganons Tower Top"] = self.world.get_dungeon('Ganons Tower', player).bosses['top'].name + if self.world.mode != 'inverted': + self.bosses[str(player)]["Ganons Tower Basement"] = self.world.get_dungeon('Ganons Tower', player).bosses['bottom'].name + self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower', player).bosses['middle'].name + self.bosses[str(player)]["Ganons Tower Top"] = self.world.get_dungeon('Ganons Tower', player).bosses['top'].name + else: + self.bosses[str(player)]["Ganons Tower Basement"] = self.world.get_dungeon('Inverted Ganons Tower', player).bosses['bottom'].name + self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Inverted Ganons Tower', player).bosses['middle'].name + self.bosses[str(player)]["Ganons Tower Top"] = self.world.get_dungeon('Inverted Ganons Tower', player).bosses['top'].name + self.bosses[str(player)]["Ganons Tower"] = "Agahnim 2" self.bosses[str(player)]["Ganon"] = "Ganon" diff --git a/Dungeons.py b/Dungeons.py index f3e13dd4a9..12592af748 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -19,7 +19,6 @@ def create_dungeons(world, player): 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)', 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)', 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, 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)', 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)', 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)', player), ItemFactory(['Small Key (Skull Woods)'] * 2, player), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player)) @@ -27,7 +26,13 @@ def create_dungeons(world, 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)', 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)', 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)', 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)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player)) + + if world.mode != 'inverted': + AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, 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)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player)) + else: + AT = make_dungeon('Inverted Agahnims Tower', 'Agahnim', ['Inverted Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), []) + GT = make_dungeon('Inverted Ganons Tower', 'Agahnim2', ['Inverted 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', player) GT.bosses['middle'] = BossFactory('Lanmolas', player) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index d87358e64c..4812742653 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -28,7 +28,7 @@ def start(): No Logic: Distribute items without regard for item requirements. ''') - parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless'], + parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless', 'inverted'], help='''\ Select game mode. (default: %(default)s) Open: World starts with Zelda rescued. @@ -42,6 +42,10 @@ def start(): hammer. Misery Mire and Turtle Rock can be opened without a sword. Hammer damages Ganon. Ether and Bombos Tablet can be activated with Hammer (and Book). + Inverted: Starting locations are Dark Sanctuary in West Dark + World or at Link's House, which is shuffled freely. + Requires the moon pearl to be Link in the Light World + instead of a bunny. ''') parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], help='''\ diff --git a/EntranceShuffle.py b/EntranceShuffle.py index da4602442a..5718dd864a 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1059,6 +1059,697 @@ def link_entrances(world, player): if world.get_entrance('Ganons Tower', player).connected_region.name != 'Ganons Tower (Entrance)': world.ganonstower_vanilla[player] = False +def link_inverted_entrances(world, player): + # Link's house shuffled freely, Houlihan set in mandatory_connections + + Dungeon_Exits = Inverted_Dungeon_Exits_Base.copy() + Cave_Exits = Cave_Exits_Base.copy() + Old_Man_House = Old_Man_House_Base.copy() + Cave_Three_Exits = Cave_Three_Exits_Base.copy() + + unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits) + + # setup mandatory connections + for exitname, regionname in inverted_mandatory_connections: + connect_simple(world, exitname, regionname, player) + + # if we do not shuffle, set default connections + if world.shuffle == 'vanilla': + for exitname, regionname in inverted_default_connections: + connect_simple(world, exitname, regionname, player) + for exitname, regionname in inverted_default_dungeon_connections: + connect_simple(world, exitname, regionname, player) + elif world.shuffle == 'dungeonssimple': + for exitname, regionname in inverted_default_connections: + connect_simple(world, exitname, regionname, player) + + simple_shuffle_dungeons(world, player) + elif world.shuffle == 'dungeonsfull': + for exitname, regionname in inverted_default_connections: + connect_simple(world, exitname, regionname, player) + + skull_woods_shuffle(world, player) + + dungeon_exits = list(Dungeon_Exits) + lw_entrances = list(Inverted_LW_Dungeon_Entrances) + lw_dungeon_entrances_must_exit = list(Inverted_LW_Dungeon_Entrances_Must_Exit) + dw_entrances = list(Inverted_DW_Dungeon_Entrances) + + # randomize which desert ledge door is a must-exit + if random.randint(0, 1) == 0: + lw_dungeon_entrances_must_exit.append('Desert Palace Entrance (North)') + dp_must_exit = 'Desert Palace Entrance (North)' + lw_entrances.append('Desert Palace Entrance (West)') + else: + lw_dungeon_entrances_must_exit.append('Desert Palace Entrance (West)') + dp_must_exit = 'Desert Palace Entrance (West)' + lw_entrances.append('Desert Palace Entrance (North)') + + dungeon_exits.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) + lw_entrances.append('Hyrule Castle Entrance (South)') + + if not world.shuffle_ganon: + connect_two_way(world, 'Inverted Ganons Tower', 'Inverted Ganons Tower Exit', player) + hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'] + else: + lw_entrances.append('Inverted Ganons Tower') + dungeon_exits.append('Inverted Ganons Tower Exit') + hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Inverted Ganons Tower'] + + # shuffle aga door first. If it's on HC ledge, remaining HC ledge door must be must-exit + all_entrances_aga = lw_entrances + dw_entrances + aga_doors = [i for i in all_entrances_aga] + random.shuffle(aga_doors) + aga_door = aga_doors.pop() + + if aga_door in hc_ledge_entrances: + lw_entrances.remove(aga_door) + hc_ledge_entrances.remove(aga_door) + + random.shuffle(hc_ledge_entrances) + hc_ledge_must_exit = hc_ledge_entrances.pop() + lw_entrances.remove(hc_ledge_must_exit) + lw_dungeon_entrances_must_exit.append(hc_ledge_must_exit) + + if aga_door in lw_entrances: + lw_entrances.remove(aga_door) + elif aga_door in dw_entrances: + dw_entrances.remove(aga_door) + + connect_two_way(world, aga_door, 'Inverted Agahnims Tower Exit', player) + dungeon_exits.remove('Inverted Agahnims Tower Exit') + + all_dungeon_entrances = dw_entrances + lw_entrances + connect_mandatory_exits(world, all_dungeon_entrances, dungeon_exits, lw_dungeon_entrances_must_exit, player, dp_must_exit) + + remaining_dw_entrances = [i for i in all_dungeon_entrances if i in dw_entrances] + remaining_lw_entrances = [i for i in all_dungeon_entrances if i in lw_entrances] + connect_caves(world, remaining_lw_entrances, remaining_dw_entrances, dungeon_exits, player) + + elif world.shuffle == 'simple': + simple_shuffle_dungeons(world, player) + + old_man_entrances = list(Inverted_Old_Man_Entrances) + caves = list(Cave_Exits) + three_exit_caves = list(Cave_Three_Exits) + + single_doors = list(Single_Cave_Doors) + bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors) + blacksmith_doors = list(Inverted_Blacksmith_Single_Cave_Doors) + door_targets = list(Inverted_Single_Cave_Targets) + + # we shuffle all 2 entrance caves as pairs as a start + # start with the ones that need to be directed + two_door_caves = list(Inverted_Two_Door_Caves_Directional) + random.shuffle(two_door_caves) + random.shuffle(caves) + while two_door_caves: + entrance1, entrance2 = two_door_caves.pop() + exit1, exit2 = caves.pop() + connect_two_way(world, entrance1, exit1, player) + connect_two_way(world, entrance2, exit2, player) + + # now the remaining pairs + two_door_caves = list(Inverted_Two_Door_Caves) + random.shuffle(two_door_caves) + while two_door_caves: + entrance1, entrance2 = two_door_caves.pop() + exit1, exit2 = caves.pop() + connect_two_way(world, entrance1, exit1, player) + connect_two_way(world, entrance2, exit2, player) + + # place links house + links_house = random.choice(list(bomb_shop_doors + blacksmith_doors)) + connect_two_way(world, links_house, 'Inverted Links House Exit', player) + if links_house in bomb_shop_doors: + bomb_shop_doors.remove(links_house) + if links_house in blacksmith_doors: + blacksmith_doors.remove(links_house) + + # place dark sanc + sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in bomb_shop_doors] + sanc_door = random.choice(sanc_doors) + bomb_shop_doors.remove(sanc_door) + connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player) + + lw_dm_entrances = ['Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)', 'Old Man House (Bottom)', + 'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Spiral Cave (Bottom)', 'Old Man Cave (East)', + 'Death Mountain Return Cave (East)', 'Spiral Cave', 'Old Man House (Top)', 'Spectacle Rock Cave', + 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)'] + + # place old man, bumper cave bottom to DDM entrances not in east bottom + + random.shuffle(old_man_entrances) + old_man_exit = old_man_entrances.pop() + connect_two_way(world, 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player) + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) + if old_man_exit == 'Spike Cave': + bomb_shop_doors.remove('Spike Cave') + bomb_shop_doors.extend(old_man_entrances) + + # add old man house to ensure it is always somewhere on light death mountain + caves.extend(list(Old_Man_House)) + caves.extend(list(three_exit_caves)) + + # connect rest + connect_caves(world, lw_dm_entrances, [], caves, player) + + # scramble holes + scramble_inverted_holes(world, player) + + # place blacksmith, has limited options + blacksmith_doors = [door for door in blacksmith_doors[:]] + random.shuffle(blacksmith_doors) + blacksmith_hut = blacksmith_doors.pop() + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) + bomb_shop_doors.extend(blacksmith_doors) + + # place bomb shop, has limited options + bomb_shop_doors = [door for door in bomb_shop_doors[:]] + random.shuffle(bomb_shop_doors) + bomb_shop = bomb_shop_doors.pop() + connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) + single_doors.extend(bomb_shop_doors) + + # tavern back door cannot be shuffled yet + connect_doors(world, ['Tavern North'], ['Tavern'], player) + + # place remaining doors + connect_doors(world, single_doors, door_targets, player) + + elif world.shuffle == 'restricted': + simple_shuffle_dungeons(world, player) + + lw_entrances = list(Inverted_LW_Entrances + Inverted_LW_Single_Cave_Doors) + dw_entrances = list(Inverted_DW_Entrances + Inverted_DW_Single_Cave_Doors + Inverted_Old_Man_Entrances) + lw_must_exits = list(Inverted_LW_Entrances_Must_Exit) + old_man_entrances = list(Inverted_Old_Man_Entrances) + caves = list(Cave_Exits + Cave_Three_Exits + Old_Man_House) + single_doors = list(Single_Cave_Doors) + bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors) + blacksmith_doors = list(Inverted_Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + door_targets = list(Inverted_Single_Cave_Targets) + + # place links house + links_house = random.choice(list(lw_entrances + dw_entrances + lw_must_exits)) + connect_two_way(world, links_house, 'Inverted Links House Exit', player) + if links_house in lw_entrances: + lw_entrances.remove(links_house) + elif links_house in dw_entrances: + dw_entrances.remove(links_house) + elif links_house in lw_must_exits: + lw_must_exits.remove(links_house) + + # place dark sanc + sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances] + sanc_door = random.choice(sanc_doors) + dw_entrances.remove(sanc_door) + connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player) + + # tavern back door cannot be shuffled yet + connect_doors(world, ['Tavern North'], ['Tavern'], player) + + # place must exits + connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) + + # place old man, has limited options + # exit has to come from specific set of doors, the entrance is free to move about + old_man_entrances = [door for door in old_man_entrances if door in dw_entrances] + random.shuffle(old_man_entrances) + old_man_exit = old_man_entrances.pop() + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) + dw_entrances.remove(old_man_exit) + + # place blacksmith, has limited options + all_entrances = lw_entrances + dw_entrances + # cannot place it anywhere already taken (or that are otherwise not eligible for placement) + blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] + random.shuffle(blacksmith_doors) + blacksmith_hut = blacksmith_doors.pop() + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) + if blacksmith_hut in lw_entrances: + lw_entrances.remove(blacksmith_hut) + if blacksmith_hut in dw_entrances: + dw_entrances.remove(blacksmith_hut) + bomb_shop_doors.extend(blacksmith_doors) + + # place bomb shop, has limited options + all_entrances = lw_entrances + dw_entrances + # cannot place it anywhere already taken (or that are otherwise not eligible for placement) + bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] + random.shuffle(bomb_shop_doors) + bomb_shop = bomb_shop_doors.pop() + connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) + if bomb_shop in lw_entrances: + lw_entrances.remove(bomb_shop) + if bomb_shop in dw_entrances: + dw_entrances.remove(bomb_shop) + + # place the old man cave's entrance somewhere in the dark world + random.shuffle(dw_entrances) + old_man_entrance = dw_entrances.pop() + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + + # now scramble the rest + connect_caves(world, lw_entrances, dw_entrances, caves, player) + + # scramble holes + scramble_inverted_holes(world, player) + + doors = lw_entrances + dw_entrances + # place remaining doors + connect_doors(world, doors, door_targets, player) + elif world.shuffle == 'full': + skull_woods_shuffle(world, player) + + lw_entrances = list(Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Single_Cave_Doors) + dw_entrances = list(Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_DW_Single_Cave_Doors + Inverted_Old_Man_Entrances) + lw_must_exits = list(Inverted_LW_Dungeon_Entrances_Must_Exit + Inverted_LW_Entrances_Must_Exit) + old_man_entrances = list(Inverted_Old_Man_Entrances + Old_Man_Entrances + ['Inverted Agahnims Tower', 'Tower of Hera']) + caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits) # don't need to consider three exit caves, have one exit caves to avoid parity issues + bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors) + blacksmith_doors = list(Inverted_Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + door_targets = list(Inverted_Single_Cave_Targets) + old_man_house = list(Old_Man_House) + + # randomize which desert ledge door is a must-exit + if random.randint(0, 1) == 0: + lw_must_exits.append('Desert Palace Entrance (North)') + dp_must_exit = 'Desert Palace Entrance (North)' + lw_entrances.append('Desert Palace Entrance (West)') + else: + lw_must_exits.append('Desert Palace Entrance (West)') + dp_must_exit = 'Desert Palace Entrance (West)' + lw_entrances.append('Desert Palace Entrance (North)') + + # tavern back door cannot be shuffled yet + connect_doors(world, ['Tavern North'], ['Tavern'], player) + + caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) + lw_entrances.append('Hyrule Castle Entrance (South)') + + + if not world.shuffle_ganon: + connect_two_way(world, 'Inverted Ganons Tower', 'Inverted Ganons Tower Exit', player) + hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'] + else: + lw_entrances.append('Inverted Ganons Tower') + caves.append('Inverted Ganons Tower Exit') + hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Inverted Ganons Tower'] + + # shuffle aga door first. if it's on hc ledge, then one other hc ledge door has to be must_exit + all_entrances_aga = lw_entrances + dw_entrances + aga_doors = [i for i in all_entrances_aga] + random.shuffle(aga_doors) + aga_door = aga_doors.pop() + + if aga_door in hc_ledge_entrances: + lw_entrances.remove(aga_door) + hc_ledge_entrances.remove(aga_door) + + random.shuffle(hc_ledge_entrances) + hc_ledge_must_exit = hc_ledge_entrances.pop() + lw_entrances.remove(hc_ledge_must_exit) + lw_must_exits.append(hc_ledge_must_exit) + + if aga_door in lw_entrances: + lw_entrances.remove(aga_door) + elif aga_door in dw_entrances: + dw_entrances.remove(aga_door) + + connect_two_way(world, aga_door, 'Inverted Agahnims Tower Exit', player) + caves.remove('Inverted Agahnims Tower Exit') + + # place links house + links_house_doors = [door for door in lw_entrances + dw_entrances + lw_must_exits] + links_house = random.choice(links_house_doors) + connect_two_way(world, links_house, 'Inverted Links House Exit', player) + if links_house in lw_entrances: + lw_entrances.remove(links_house) + if links_house in dw_entrances: + dw_entrances.remove(links_house) + if links_house in lw_must_exits: + lw_must_exits.remove(links_house) + + # place dark sanc + sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances] + sanc_door = random.choice(sanc_doors) + dw_entrances.remove(sanc_door) + connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player) + + # place old man house + # no dw must exits in inverted, but we randomize whether cave is in light or dark world + if random.randint(0, 1) == 0: + caves += old_man_house + connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player, dp_must_exit) + try: + caves.remove(old_man_house[0]) + except ValueError: + pass + else: #if the cave wasn't placed we get here + connect_caves(world, lw_entrances, [], old_man_house, player) + else: + connect_caves(world, dw_entrances, [], old_man_house, player) + connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player, dp_must_exit) + + # place old man, has limited options + # exit has to come from specific set of doors, the entrance is free to move about + old_man_entrances = [door for door in old_man_entrances if door in dw_entrances + lw_entrances] + random.shuffle(old_man_entrances) + old_man_exit = old_man_entrances.pop() + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) + if old_man_exit in dw_entrances: + dw_entrances.remove(old_man_exit) + old_man_world = 'dark' + elif old_man_exit in lw_entrances: + lw_entrances.remove(old_man_exit) + old_man_world = 'light' + + # place blacksmith, has limited options + all_entrances = lw_entrances + dw_entrances + # cannot place it anywhere already taken (or that are otherwise not eligible for placement) + blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] + random.shuffle(blacksmith_doors) + blacksmith_hut = blacksmith_doors.pop() + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) + if blacksmith_hut in lw_entrances: + lw_entrances.remove(blacksmith_hut) + if blacksmith_hut in dw_entrances: + dw_entrances.remove(blacksmith_hut) + bomb_shop_doors.extend(blacksmith_doors) + + # place bomb shop, has limited options + all_entrances = lw_entrances + dw_entrances + # cannot place it anywhere already taken (or that are otherwise not eligible for placement) + bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] + random.shuffle(bomb_shop_doors) + bomb_shop = bomb_shop_doors.pop() + connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) + if bomb_shop in lw_entrances: + lw_entrances.remove(bomb_shop) + if bomb_shop in dw_entrances: + dw_entrances.remove(bomb_shop) + + # place the old man cave's entrance somewhere in the same world he'll exit from + if old_man_world == 'light': + random.shuffle(lw_entrances) + old_man_entrance = lw_entrances.pop() + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + elif old_man_world == 'dark': + random.shuffle(dw_entrances) + old_man_entrance = dw_entrances.pop() + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + + # now scramble the rest + connect_caves(world, lw_entrances, dw_entrances, caves, player) + + # scramble holes + scramble_inverted_holes(world, player) + + doors = lw_entrances + dw_entrances + + # place remaining doors + connect_doors(world, doors, door_targets, player) + elif world.shuffle == 'crossed': + skull_woods_shuffle(world, player) + + entrances = list(Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Single_Cave_Doors + Inverted_Old_Man_Entrances + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_DW_Single_Cave_Doors) + must_exits = list(Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit) + + old_man_entrances = list(Inverted_Old_Man_Entrances + Old_Man_Entrances + ['Inverted Agahnims Tower', 'Tower of Hera']) + caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits + Old_Man_House) # don't need to consider three exit caves, have one exit caves to avoid parity issues + bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors) + blacksmith_doors = list(Inverted_Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + door_targets = list(Inverted_Single_Cave_Targets) + + # randomize which desert ledge door is a must-exit + if random.randint(0, 1) == 0: + must_exits.append('Desert Palace Entrance (North)') + dp_must_exit = 'Desert Palace Entrance (North)' + entrances.append('Desert Palace Entrance (West)') + else: + must_exits.append('Desert Palace Entrance (West)') + dp_must_exit = 'Desert Palace Entrance (West)' + entrances.append('Desert Palace Entrance (North)') + + caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) + entrances.append('Hyrule Castle Entrance (South)') + + if not world.shuffle_ganon: + connect_two_way(world, 'Inverted Ganons Tower', 'Inverted Ganons Tower Exit', player) + hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'] + else: + entrances.append('Inverted Ganons Tower') + caves.append('Inverted Ganons Tower Exit') + hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Inverted Ganons Tower'] + + # shuffle aga door. if it's on hc ledge, then one other hc ledge door has to be must_exit + aga_door = random.choice(list(entrances)) + + if aga_door in hc_ledge_entrances: + hc_ledge_entrances.remove(aga_door) + + random.shuffle(hc_ledge_entrances) + hc_ledge_must_exit = hc_ledge_entrances.pop() + entrances.remove(hc_ledge_must_exit) + must_exits.append(hc_ledge_must_exit) + + entrances.remove(aga_door) + connect_two_way(world, aga_door, 'Inverted Agahnims Tower Exit', player) + caves.remove('Inverted Agahnims Tower Exit') + + + # place links house + links_house = random.choice(list(entrances + must_exits)) + connect_two_way(world, links_house, 'Inverted Links House Exit', player) + if links_house in entrances: + entrances.remove(links_house) + elif links_house in must_exits: + must_exits.remove(links_house) + + # place dark sanc + sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances] + sanc_door = random.choice(sanc_doors) + entrances.remove(sanc_door) + connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player) + + # tavern back door cannot be shuffled yet + connect_doors(world, ['Tavern North'], ['Tavern'], player) + + + #place must-exit caves + connect_mandatory_exits(world, entrances, caves, must_exits, player, dp_must_exit) + + + # place old man, has limited options + # exit has to come from specific set of doors, the entrance is free to move about + old_man_entrances = [door for door in old_man_entrances if door in entrances] + random.shuffle(old_man_entrances) + old_man_exit = old_man_entrances.pop() + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) + entrances.remove(old_man_exit) + + # place blacksmith, has limited options + # cannot place it anywhere already taken (or that are otherwise not eligible for placement) + blacksmith_doors = [door for door in blacksmith_doors if door in entrances] + random.shuffle(blacksmith_doors) + blacksmith_hut = blacksmith_doors.pop() + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) + entrances.remove(blacksmith_hut) + + # place bomb shop, has limited options + + # cannot place it anywhere already taken (or that are otherwise not eligible for placement) + bomb_shop_doors = [door for door in bomb_shop_doors if door in entrances] + random.shuffle(bomb_shop_doors) + bomb_shop = bomb_shop_doors.pop() + connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) + entrances.remove(bomb_shop) + + # place the old man cave's entrance somewhere + random.shuffle(entrances) + old_man_entrance = entrances.pop() + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + + # now scramble the rest + connect_caves(world, entrances, [], caves, player) + + # scramble holes + scramble_inverted_holes(world, player) + + # place remaining doors + connect_doors(world, entrances, door_targets, player) + elif world.shuffle == 'insanity': + # beware ye who enter here + + entrances = Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_Old_Man_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)'] + entrances_must_exits = Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit + + doors = Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Secret Entrance Stairs'] + Inverted_Old_Man_Entrances +\ + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] +\ + Inverted_LW_Single_Cave_Doors + Inverted_DW_Single_Cave_Doors + ['Desert Palace Entrance (West)', 'Desert Palace Entrance (North)'] + + # randomize which desert ledge door is a must-exit + if random.randint(0, 1) == 0: + entrances_must_exits.append('Desert Palace Entrance (North)') + entrances.append('Desert Palace Entrance (West)') + else: + entrances_must_exits.append('Desert Palace Entrance (West)') + entrances.append('Desert Palace Entrance (North)') + + # TODO: there are other possible entrances we could support here by way of exiting from a connector, + # and rentering to find bomb shop. However appended list here is all those that we currently have + # bomb shop logic for. + # Specifically we could potentially add: 'Dark Death Mountain Ledge (East)' and doors associated with pits + bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors + ['Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Entrance', 'Bumper Cave (Top)', 'Hookshot Cave Back Entrance']) + blacksmith_doors = list(Inverted_Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + door_targets = list(Inverted_Single_Cave_Targets) + + random.shuffle(doors) + + old_man_entrances = list(Inverted_Old_Man_Entrances + Old_Man_Entrances) + ['Tower of Hera', 'Inverted Agahnims Tower'] + + caves = Cave_Exits + Dungeon_Exits + Cave_Three_Exits + ['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', + 'Kakariko Well Exit', 'Bat Cave Exit', 'North Fairy Cave Exit', 'Lost Woods Hideout Exit', 'Lumberjack Tree Exit', 'Sanctuary Exit'] + + + # shuffle up holes + hole_entrances = ['Kakariko Well Drop', 'Bat Cave Drop', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave', + 'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] + + hole_targets = ['Kakariko Well (top)', 'Bat Cave (right)', 'North Fairy Cave', 'Lost Woods Hideout (top)', 'Lumberjack Tree (top)', 'Sewer Drop', 'Skull Woods Second Section (Drop)', + 'Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)'] + + # tavern back door cannot be shuffled yet + connect_doors(world, ['Tavern North'], ['Tavern'], player) + + hole_entrances.append('Hyrule Castle Secret Entrance Drop') + hole_targets.append('Hyrule Castle Secret Entrance') + entrances.append('Hyrule Castle Secret Entrance Stairs') + caves.append('Hyrule Castle Secret Entrance Exit') + + if not world.shuffle_ganon: + connect_two_way(world, 'Inverted Ganons Tower', 'Inverted Ganons Tower Exit', player) + connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player) + else: + entrances.append('Inverted Ganons Tower') + caves.extend(['Inverted Ganons Tower Exit', 'Pyramid Exit']) + hole_entrances.append('Inverted Pyramid Hole') + hole_targets.append('Pyramid') + doors.extend(['Inverted Ganons Tower', 'Inverted Pyramid Entrance']) + + random.shuffle(hole_entrances) + random.shuffle(hole_targets) + random.shuffle(entrances) + + # fill up holes + for hole in hole_entrances: + connect_entrance(world, hole, hole_targets.pop(), player) + + doors.append('Hyrule Castle Entrance (South)') + caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) + + # place links house and dark sanc + links_house = random.choice(list(entrances + entrances_must_exits)) + connect_two_way(world, links_house, 'Inverted Links House Exit', player) + if links_house in entrances: + entrances.remove(links_house) + elif links_house in entrances_must_exits: + entrances_must_exits.remove(links_house) + doors.remove(links_house) + + sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances] + sanc_door = random.choice(sanc_doors) + entrances.remove(sanc_door) + doors.remove(sanc_door) + connect_doors(world, [sanc_door], ['Inverted Dark Sanctuary'], player) + + # now let's deal with mandatory reachable stuff + def extract_reachable_exit(cavelist): + random.shuffle(cavelist) + candidate = None + for cave in cavelist: + if isinstance(cave, tuple) and len(cave) > 1: + # special handling: TRock has two entries that we should consider entrance only + # ToDo this should be handled in a more sensible manner + if cave[0] in ['Turtle Rock Exit (Front)', 'Spectacle Rock Cave Exit (Peak)'] and len(cave) == 2: + continue + candidate = cave + break + if candidate is None: + raise RuntimeError('No suitable cave.') + cavelist.remove(candidate) + return candidate + + def connect_reachable_exit(entrance, caves, doors): + cave = extract_reachable_exit(caves) + + exit = cave[-1] + cave = cave[:-1] + connect_exit(world, exit, entrance, player) + connect_entrance(world, doors.pop(), exit, player) + # rest of cave now is forced to be in this world + caves.append(cave) + + # connect mandatory exits + for entrance in entrances_must_exits: + connect_reachable_exit(entrance, caves, doors) + + # place old man, has limited options + # exit has to come from specific set of doors, the entrance is free to move about + old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances] + random.shuffle(old_man_entrances) + old_man_exit = old_man_entrances.pop() + entrances.remove(old_man_exit) + + connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player) + connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)', player) + caves.append('Old Man Cave Exit (West)') + + # place blacksmith, has limited options + blacksmith_doors = [door for door in blacksmith_doors if door in doors] + random.shuffle(blacksmith_doors) + blacksmith_hut = blacksmith_doors.pop() + connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) + doors.remove(blacksmith_hut) + + # place dam and pyramid fairy, have limited options + bomb_shop_doors = [door for door in bomb_shop_doors if door in doors] + random.shuffle(bomb_shop_doors) + bomb_shop = bomb_shop_doors.pop() + connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) + doors.remove(bomb_shop) + + # handle remaining caves + for cave in caves: + if isinstance(cave, str): + cave = (cave,) + + for exit in cave: + connect_exit(world, exit, entrances.pop(), player) + connect_entrance(world, doors.pop(), exit, player) + + # place remaining doors + connect_doors(world, doors, door_targets, player) + else: + raise NotImplementedError('Shuffling not supported yet') + + # patch swamp drain + if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Palace (Entrance)': + world.swamp_patch_required[player] = True + + # check for potion shop location + if world.get_entrance('Potion Shop', player).connected_region.name != 'Potion Shop': + world.powder_patch_required[player] = True + + # check for ganon location + if world.get_entrance('Inverted Pyramid Hole', player).connected_region.name != 'Hyrule Castle Ledge': + world.ganon_at_pyramid[player] = False + + # check for Ganon's Tower location + if world.get_entrance('Inverted Ganons Tower', player).connected_region.name != 'Ganons Tower (Entrance)': + world.ganonstower_vanilla[player] = False def connect_simple(world, exitname, regionname, player): world.get_entrance(exitname, player).connect(world.get_region(regionname, player)) @@ -1084,7 +1775,6 @@ def connect_entrance(world, entrancename, exitname, player): entrance.connect(region, addresses, target) world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance', player) - def connect_exit(world, exitname, entrancename, player): entrance = world.get_entrance(entrancename, player) exit = world.get_entrance(exitname, player) @@ -1158,6 +1848,47 @@ def scramble_holes(world, player): connect_entrance(world, drop, target, player) +def scramble_inverted_holes(world, player): + hole_entrances = [('Kakariko Well Cave', 'Kakariko Well Drop'), + ('Bat Cave Cave', 'Bat Cave Drop'), + ('North Fairy Cave', 'North Fairy Cave Drop'), + ('Lost Woods Hideout Stump', 'Lost Woods Hideout Drop'), + ('Lumberjack Tree Cave', 'Lumberjack Tree Tree'), + ('Sanctuary', 'Sanctuary Grave')] + + hole_targets = [('Kakariko Well Exit', 'Kakariko Well (top)'), + ('Bat Cave Exit', 'Bat Cave (right)'), + ('North Fairy Cave Exit', 'North Fairy Cave'), + ('Lost Woods Hideout Exit', 'Lost Woods Hideout (top)'), + ('Lumberjack Tree Exit', 'Lumberjack Tree (top)')] + + if not world.shuffle_ganon: + connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player) + else: + hole_targets.append(('Pyramid Exit', 'Pyramid')) + + + hole_entrances.append(('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Drop')) + hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) + + # do not shuffle sanctuary into pyramid hole unless shuffle is crossed + if world.shuffle == 'crossed': + hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) + if world.shuffle_ganon: + random.shuffle(hole_targets) + exit, target = hole_targets.pop() + connect_two_way(world, 'Inverted Pyramid Entrance', exit, player) + connect_entrance(world, 'Inverted Pyramid Hole', target, player) + if world.shuffle != 'crossed': + hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) + + random.shuffle(hole_targets) + for entrance, drop in hole_entrances: + exit, target = hole_targets.pop() + connect_two_way(world, entrance, exit, player) + connect_entrance(world, drop, target, player) + def connect_random(world, exitlist, targetlist, player, two_way=False): targetlist = list(targetlist) random.shuffle(targetlist) @@ -1169,7 +1900,7 @@ def connect_random(world, exitlist, targetlist, player, two_way=False): connect_entrance(world, exit, target, player) -def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): +def connect_mandatory_exits(world, entrances, caves, must_be_exits, player, dp_must_exit=None): """This works inplace""" random.shuffle(entrances) random.shuffle(caves) @@ -1190,8 +1921,12 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): connect_two_way(world, exit, cave[-1], player) if len(cave) == 2: entrance = entrances.pop() - # ToDo Better solution, this is a hot fix. Do not connect both sides of trock ledge only to each other - if entrance == 'Dark Death Mountain Ledge (West)': + # ToDo Better solution, this is a hot fix. Do not connect both sides of trock/desert ledge only to each other + if world.mode != 'inverted' and entrance == 'Dark Death Mountain Ledge (West)': + new_entrance = entrances.pop() + entrances.append(entrance) + entrance = new_entrance + if world.mode == 'inverted' and entrance == dp_must_exit: new_entrance = entrances.pop() entrances.append(entrance) entrance = new_entrance @@ -1262,77 +1997,131 @@ def simple_shuffle_dungeons(world, player): dungeon_entrances = ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace'] dungeon_exits = ['Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit', 'Skull Woods Final Section Exit', 'Palace of Darkness Exit', 'Ice Palace Exit', 'Misery Mire Exit', 'Swamp Palace Exit'] - if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) + if world.mode != 'inverted': + if not world.shuffle_ganon: + connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) + else: + dungeon_entrances.append('Ganons Tower') + dungeon_exits.append('Ganons Tower Exit') else: - dungeon_entrances.append('Ganons Tower') - dungeon_exits.append('Ganons Tower Exit') + dungeon_entrances.append('Inverted Agahnims Tower') + dungeon_exits.append('Inverted Agahnims Tower Exit') # shuffle up single entrance dungeons connect_random(world, dungeon_entrances, dungeon_exits, player, True) # mix up 4 door dungeons multi_dungeons = ['Desert', 'Turtle Rock'] - if world.mode == 'open': + if world.mode == 'open' or (world.mode == 'inverted' and world.shuffle_ganon): multi_dungeons.append('Hyrule Castle') random.shuffle(multi_dungeons) dp_target = multi_dungeons[0] tr_target = multi_dungeons[1] - if world.mode != 'open': + if world.mode not in ['open', 'inverted'] or (world.mode == 'inverted' and world.shuffle_ganon is False): # place hyrule castle as intended hc_target = 'Hyrule Castle' else: hc_target = multi_dungeons[2] # ToDo improve this? - if hc_target == 'Hyrule Castle': - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) - connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)', player) - connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)', player) - connect_two_way(world, 'Agahnims Tower', 'Agahnims Tower Exit', player) - elif hc_target == 'Desert': - connect_two_way(world, 'Desert Palace Entrance (South)', 'Hyrule Castle Exit (South)', player) - connect_two_way(world, 'Desert Palace Entrance (East)', 'Hyrule Castle Exit (East)', player) - connect_two_way(world, 'Desert Palace Entrance (West)', 'Hyrule Castle Exit (West)', player) - connect_two_way(world, 'Desert Palace Entrance (North)', 'Agahnims Tower Exit', player) - elif hc_target == 'Turtle Rock': - connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)', player) - connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Hyrule Castle Exit (East)', player) - connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)', player) - connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Agahnims Tower Exit', player) - if dp_target == 'Hyrule Castle': - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Desert Palace Exit (South)', player) - connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Desert Palace Exit (East)', player) - connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Desert Palace Exit (West)', player) - connect_two_way(world, 'Agahnims Tower', 'Desert Palace Exit (North)', player) - elif dp_target == 'Desert': - connect_two_way(world, 'Desert Palace Entrance (South)', 'Desert Palace Exit (South)', player) - connect_two_way(world, 'Desert Palace Entrance (East)', 'Desert Palace Exit (East)', player) - connect_two_way(world, 'Desert Palace Entrance (West)', 'Desert Palace Exit (West)', player) - connect_two_way(world, 'Desert Palace Entrance (North)', 'Desert Palace Exit (North)', player) - elif dp_target == 'Turtle Rock': - connect_two_way(world, 'Turtle Rock', 'Desert Palace Exit (South)', player) - connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Desert Palace Exit (East)', player) - connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Desert Palace Exit (West)', player) - connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Desert Palace Exit (North)', player) + if world.mode != 'inverted': + if hc_target == 'Hyrule Castle': + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Agahnims Tower', 'Agahnims Tower Exit', player) + elif hc_target == 'Desert': + connect_two_way(world, 'Desert Palace Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Agahnims Tower Exit', player) + elif hc_target == 'Turtle Rock': + connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Agahnims Tower Exit', player) - if tr_target == 'Hyrule Castle': - connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Turtle Rock Exit (Front)', player) - connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Turtle Rock Ledge Exit (East)', player) - connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) - connect_two_way(world, 'Agahnims Tower', 'Turtle Rock Isolated Ledge Exit', player) - elif tr_target == 'Desert': - connect_two_way(world, 'Desert Palace Entrance (South)', 'Turtle Rock Exit (Front)', player) - connect_two_way(world, 'Desert Palace Entrance (North)', 'Turtle Rock Ledge Exit (East)', player) - connect_two_way(world, 'Desert Palace Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) - connect_two_way(world, 'Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Exit', player) - elif tr_target == 'Turtle Rock': - connect_two_way(world, 'Turtle Rock', 'Turtle Rock Exit (Front)', player) - connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Isolated Ledge Exit', player) - connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)', player) - connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)', player) + if dp_target == 'Hyrule Castle': + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Agahnims Tower', 'Desert Palace Exit (North)', player) + elif dp_target == 'Desert': + connect_two_way(world, 'Desert Palace Entrance (South)', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Desert Palace Exit (North)', player) + elif dp_target == 'Turtle Rock': + connect_two_way(world, 'Turtle Rock', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Desert Palace Exit (North)', player) + + if tr_target == 'Hyrule Castle': + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Turtle Rock Ledge Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Agahnims Tower', 'Turtle Rock Isolated Ledge Exit', player) + elif tr_target == 'Desert': + connect_two_way(world, 'Desert Palace Entrance (South)', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Turtle Rock Ledge Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Exit', player) + elif tr_target == 'Turtle Rock': + connect_two_way(world, 'Turtle Rock', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Isolated Ledge Exit', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)', player) + else: + if hc_target == 'Hyrule Castle': + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Inverted Ganons Tower', 'Inverted Ganons Tower Exit', player) + elif hc_target == 'Desert': + connect_two_way(world, 'Desert Palace Entrance (South)', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Hyrule Castle Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Inverted Ganons Tower Exit', player) + elif hc_target == 'Turtle Rock': + connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Inverted Ganons Tower Exit', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Hyrule Castle Exit (East)', player) + + if dp_target == 'Hyrule Castle': + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Inverted Ganons Tower', 'Desert Palace Exit (North)', player) + elif dp_target == 'Desert': + connect_two_way(world, 'Desert Palace Entrance (South)', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Desert Palace Exit (North)', player) + elif dp_target == 'Turtle Rock': + connect_two_way(world, 'Turtle Rock', 'Desert Palace Exit (South)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Desert Palace Exit (East)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Desert Palace Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Desert Palace Exit (North)', player) + + if tr_target == 'Hyrule Castle': + connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Turtle Rock Ledge Exit (East)', player) + connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Inverted Ganons Tower', 'Turtle Rock Isolated Ledge Exit', player) + elif tr_target == 'Desert': + connect_two_way(world, 'Desert Palace Entrance (South)', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Desert Palace Entrance (North)', 'Turtle Rock Ledge Exit (East)', player) + connect_two_way(world, 'Desert Palace Entrance (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Exit', player) + elif tr_target == 'Turtle Rock': + connect_two_way(world, 'Turtle Rock', 'Turtle Rock Exit (Front)', player) + connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Isolated Ledge Exit', player) + connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)', player) + connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)', player) def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits): def shuffle_lists_in_list(ls): @@ -1731,6 +2520,308 @@ Single_Cave_Targets = ['Blinds Hideout', 'Kakariko Gamble Game', 'Dam'] +Inverted_LW_Dungeon_Entrances = ['Desert Palace Entrance (South)', + 'Eastern Palace', + 'Tower of Hera', + 'Hyrule Castle Entrance (West)', + 'Hyrule Castle Entrance (East)'] + +Inverted_DW_Dungeon_Entrances = ['Thieves Town', + 'Skull Woods Final Section', + 'Ice Palace', + 'Misery Mire', + 'Palace of Darkness', + 'Swamp Palace', + 'Turtle Rock', + 'Dark Death Mountain Ledge (West)', + 'Dark Death Mountain Ledge (East)', + 'Turtle Rock Isolated Ledge Entrance', + 'Inverted Agahnims Tower'] + +Inverted_LW_Dungeon_Entrances_Must_Exit = ['Desert Palace Entrance (East)'] + +Inverted_Dungeon_Exits_Base = [['Desert Palace Exit (South)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)'], + 'Desert Palace Exit (North)', + 'Eastern Palace Exit', + 'Tower of Hera Exit', + 'Thieves Town Exit', + 'Skull Woods Final Section Exit', + 'Ice Palace Exit', + 'Misery Mire Exit', + 'Palace of Darkness Exit', + 'Swamp Palace Exit', + 'Inverted Agahnims Tower Exit', + ['Turtle Rock Ledge Exit (East)', + 'Turtle Rock Exit (Front)', 'Turtle Rock Ledge Exit (West)', 'Turtle Rock Isolated Ledge Exit']] + +Inverted_LW_Entrances_Must_Exit = ['Death Mountain Return Cave (West)', + 'Two Brothers House (West)'] + +Inverted_Two_Door_Caves_Directional = [('Old Man Cave (West)', 'Death Mountain Return Cave (West)'), + ('Two Brothers House (East)', 'Two Brothers House (West)')] + + +Inverted_Two_Door_Caves = [('Elder House (East)', 'Elder House (West)'), + ('Superbunny Cave (Bottom)', 'Superbunny Cave (Top)'), + ('Hookshot Cave', 'Hookshot Cave Back Entrance')] + + + +Inverted_Old_Man_Entrances = ['Dark Death Mountain Fairy', + 'Spike Cave'] + +Inverted_LW_Entrances = ['Elder House (East)', + 'Elder House (West)', + 'Two Brothers House (East)', + 'Old Man Cave (East)', + 'Old Man Cave (West)', + 'Old Man House (Bottom)', + 'Old Man House (Top)', + 'Death Mountain Return Cave (East)', + 'Paradox Cave (Bottom)', + 'Paradox Cave (Middle)', + 'Paradox Cave (Top)', + 'Spectacle Rock Cave', + 'Spectacle Rock Cave Peak', + 'Spectacle Rock Cave (Bottom)', + 'Fairy Ascension Cave (Bottom)', + 'Fairy Ascension Cave (Top)', + 'Spiral Cave', + 'Spiral Cave (Bottom)'] + + +Inverted_DW_Entrances = ['Bumper Cave (Bottom)', + 'Superbunny Cave (Top)', + 'Superbunny Cave (Bottom)', + 'Hookshot Cave', + 'Hookshot Cave Back Entrance'] + +Inverted_Bomb_Shop_Multi_Cave_Doors = ['Hyrule Castle Entrance (South)', + 'Misery Mire', + 'Thieves Town', + 'Bumper Cave (Bottom)', + 'Swamp Palace', + 'Hyrule Castle Secret Entrance Stairs', + 'Skull Woods First Section Door', + 'Skull Woods Second Section Door (East)', + 'Skull Woods Second Section Door (West)', + 'Skull Woods Final Section', + 'Ice Palace', + 'Turtle Rock', + 'Dark Death Mountain Ledge (West)', + 'Dark Death Mountain Ledge (East)', + 'Superbunny Cave (Top)', + 'Superbunny Cave (Bottom)', + 'Hookshot Cave', + 'Inverted Agahnims Tower', + 'Desert Palace Entrance (South)', + 'Tower of Hera', + 'Two Brothers House (West)', + 'Old Man Cave (East)', + 'Old Man House (Bottom)', + 'Old Man House (Top)', + 'Death Mountain Return Cave (East)', + 'Death Mountain Return Cave (West)', + 'Spectacle Rock Cave Peak', + 'Spectacle Rock Cave', + 'Spectacle Rock Cave (Bottom)', + 'Paradox Cave (Bottom)', + 'Paradox Cave (Middle)', + 'Paradox Cave (Top)', + 'Fairy Ascension Cave (Bottom)', + 'Fairy Ascension Cave (Top)', + 'Spiral Cave', + 'Spiral Cave (Bottom)', + 'Palace of Darkness', + 'Hyrule Castle Entrance (West)', + 'Hyrule Castle Entrance (East)', + 'Inverted Ganons Tower', + 'Desert Palace Entrance (West)', + 'Desert Palace Entrance (North)'] + +Inverted_Blacksmith_Multi_Cave_Doors = [] # same as non-inverted + +Inverted_LW_Single_Cave_Doors = LW_Single_Cave_Doors + ['Inverted Big Bomb Shop'] + +Inverted_DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', + 'Inverted Dark Sanctuary', + 'Inverted Links House', + 'Dark Lake Hylia Fairy', + 'C-Shaped House', + 'Bumper Cave (Top)', + 'Dark Lake Hylia Shop', + 'Dark World Shop', + 'Red Shield Shop', + 'Mire Shed', + 'East Dark World Hint', + 'Dark Desert Hint', + 'Palace of Darkness Hint', + 'Dark Lake Hylia Ledge Spike Cave', + 'Cave Shop (Dark Death Mountain)', + 'Dark World Potion Shop', + 'Pyramid Fairy', + 'Archery Game', + 'Dark World Lumberjack Shop', + 'Hype Cave', + 'Brewery', + 'Dark Lake Hylia Ledge Hint', + 'Chest Game', + 'Dark Desert Fairy', + 'Dark Lake Hylia Ledge Fairy', + 'Fortune Teller (Dark)', + 'Dark World Hammer Peg Cave'] + + +Inverted_Bomb_Shop_Single_Cave_Doors = ['Waterfall of Wishing', + 'Capacity Upgrade', + 'Bonk Rock Cave', + 'Graveyard Cave', + 'Checkerboard Cave', + 'Cave 45', + 'Kings Grave', + 'Bonk Fairy (Light)', + 'Hookshot Fairy', + 'East Dark World Hint', + 'Palace of Darkness Hint', + 'Dark Lake Hylia Fairy', + 'Dark Lake Hylia Ledge Fairy', + 'Dark Lake Hylia Ledge Spike Cave', + 'Dark Lake Hylia Ledge Hint', + 'Hype Cave', + 'Bonk Fairy (Dark)', + 'Brewery', + 'C-Shaped House', + 'Chest Game', + 'Dark World Hammer Peg Cave', + 'Red Shield Shop', + 'Inverted Dark Sanctuary', + 'Fortune Teller (Dark)', + 'Dark World Shop', + 'Dark World Lumberjack Shop', + 'Dark World Potion Shop', + 'Archery Game', + 'Mire Shed', + 'Dark Desert Hint', + 'Dark Desert Fairy', + 'Spike Cave', + 'Cave Shop (Dark Death Mountain)', + 'Bumper Cave (Top)', + 'Mimic Cave', + 'Dark Lake Hylia Shop', + 'Inverted Links House'] + +Inverted_Blacksmith_Single_Cave_Doors = ['Blinds Hideout', + 'Lake Hylia Fairy', + 'Light Hype Fairy', + 'Desert Fairy', + 'Chicken House', + 'Aginahs Cave', + 'Sahasrahlas Hut', + 'Cave Shop (Lake Hylia)', + 'Blacksmiths Hut', + 'Sick Kids House', + 'Lost Woods Gamble', + 'Fortune Teller (Light)', + 'Snitch Lady (East)', + 'Snitch Lady (West)', + 'Bush Covered House', + 'Tavern (Front)', + 'Light World Bomb Hut', + 'Kakariko Shop', + 'Mini Moldorm Cave', + 'Long Fairy Cave', + 'Good Bee Cave', + '20 Rupee Cave', + '50 Rupee Cave', + 'Ice Rod Cave', + 'Library', + 'Potion Shop', + 'Dam', + 'Lumberjack House', + 'Lake Hylia Fortune Teller', + 'Kakariko Gamble Game', + 'Inverted Big Bomb Shop'] + + +Inverted_Single_Cave_Targets = ['Blinds Hideout', + 'Bonk Fairy (Light)', + 'Lake Hylia Healer Fairy', + 'Swamp Healer Fairy', + 'Desert Healer Fairy', + 'Kings Grave', + 'Chicken House', + 'Aginahs Cave', + 'Sahasrahlas Hut', + 'Cave Shop (Lake Hylia)', + 'Sick Kids House', + 'Lost Woods Gamble', + 'Fortune Teller (Light)', + 'Snitch Lady (East)', + 'Snitch Lady (West)', + 'Bush Covered House', + 'Tavern (Front)', + 'Light World Bomb Hut', + 'Kakariko Shop', + 'Cave 45', + 'Graveyard Cave', + 'Checkerboard Cave', + 'Mini Moldorm Cave', + 'Long Fairy Cave', + 'Good Bee Cave', + '20 Rupee Cave', + '50 Rupee Cave', + 'Ice Rod Cave', + 'Bonk Rock Cave', + 'Library', + 'Potion Shop', + 'Hookshot Fairy', + 'Waterfall of Wishing', + 'Capacity Upgrade', + 'Pyramid Fairy', + 'East Dark World Hint', + 'Palace of Darkness Hint', + 'Dark Lake Hylia Healer Fairy', + 'Dark Lake Hylia Ledge Healer Fairy', + 'Dark Lake Hylia Ledge Spike Cave', + 'Dark Lake Hylia Ledge Hint', + 'Hype Cave', + 'Bonk Fairy (Dark)', + 'Brewery', + 'C-Shaped House', + 'Chest Game', + 'Dark World Hammer Peg Cave', + 'Red Shield Shop', + 'Fortune Teller (Dark)', + 'Village of Outcasts Shop', + 'Dark Lake Hylia Shop', + 'Dark World Lumberjack Shop', + 'Archery Game', + 'Mire Shed', + 'Dark Desert Hint', + 'Dark Desert Healer Fairy', + 'Spike Cave', + 'Cave Shop (Dark Death Mountain)', + 'Dark Death Mountain Healer Fairy', + 'Mimic Cave', + 'Dark World Potion Shop', + 'Lumberjack House', + 'Lake Hylia Fortune Teller', + 'Kakariko Gamble Game', + 'Dam'] + +# in inverted we put dark sanctuary in west dark world for now +Inverted_Dark_Sanctuary_Doors = ['Inverted Dark Sanctuary', + 'Fortune Teller (Dark)', + 'Brewery', + 'C-Shaped House', + 'Chest Game', + 'Dark World Lumberjack Shop', + 'Red Shield Shop', + 'Bumper Cave (Bottom)', + 'Bumper Cave (Top)', + 'Skull Woods Final Section', + 'Thieves Town'] + # these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), ('Lake Hylia Central Island Teleporter', 'Dark Lake Hylia Central Island'), @@ -1909,6 +3000,221 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central ('Pyramid Drop', 'East Dark World') ] +inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), + ('Zoras River', 'Zoras River'), + ('Kings Grave Outer Rocks', 'Kings Grave Area'), + ('Kings Grave Inner Rocks', 'Light World'), + ('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'), + ('Master Sword Meadow', 'Master Sword Meadow'), + ('Hobo Bridge', 'Hobo Bridge'), + ('Desert Palace East Wing', 'Desert Palace East'), + ('Bat Cave Drop Ledge', 'Bat Cave Drop Ledge'), + ('Bat Cave Door', 'Bat Cave (left)'), + ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), + ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), + ('Desert Palace Stairs', 'Desert Palace Stairs'), + ('Desert Palace Stairs Drop', 'Light World'), + ('Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (North) Spot'), + ('Desert Ledge Return Rocks', 'Desert Ledge'), + ('Throne Room', 'Sewers (Dark)'), ('Sewers Door', 'Sewers'), + ('Sanctuary Push Door', 'Sanctuary'), + ('Sewer Drop', 'Sewers'), + ('Sewers Back Door', 'Sewers (Dark)'), + ('Agahnim 1', 'Agahnim 1'), + ('Death Mountain Entrance Rock', 'Death Mountain Entrance'), + ('Death Mountain Entrance Drop', 'Light World'), + ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'), + ('Death Mountain Return Ledge Drop', 'Light World'), + ('Old Man House Front to Back', 'Old Man House Back'), + ('Old Man House Back to Front', 'Old Man House'), + ('Broken Bridge (West)', 'East Death Mountain (Bottom)'), + ('Broken Bridge (East)', 'Death Mountain'), + ('East Death Mountain Drop', 'East Death Mountain (Bottom)'), + ('Spiral Cave Ledge Access', 'Spiral Cave Ledge'), + ('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'), + ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), + ('East Death Mountain (Top)', 'East Death Mountain (Top)'), + ('Death Mountain (Top)', 'Death Mountain (Top)'), + ('Death Mountain Drop', 'Death Mountain'), + ('Tower of Hera Small Key Door', 'Tower of Hera (Basement)'), + ('Tower of Hera Big Key Door', 'Tower of Hera (Top)'), + ('Dark Lake Hylia Drop (East)', 'Dark Lake Hylia'), + ('Dark Lake Hylia Drop (South)', 'Dark Lake Hylia'), + ('Dark Lake Hylia Teleporter', 'Dark Lake Hylia'), + ('Dark Lake Hylia Ledge Pier', 'Dark Lake Hylia Ledge'), + ('Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia'), + ('East Dark World Pier', 'East Dark World'), + ('South Dark World Bridge', 'South Dark World'), + ('East Dark World Bridge', 'East Dark World'), + ('Village of Outcasts Heavy Rock', 'West Dark World'), + ('Village of Outcasts Drop', 'South Dark World'), + ('Village of Outcasts Eastern Rocks', 'Hammer Peg Area'), + ('Village of Outcasts Pegs', 'Dark Grassy Lawn'), + ('Peg Area Rocks', 'West Dark World'), + ('Grassy Lawn Pegs', 'West Dark World'), + ('East Dark World River Pier', 'Northeast Dark World'), + ('West Dark World Gap', 'West Dark World'), + ('East Dark World Broken Bridge Pass', 'East Dark World'), + ('Northeast Dark World Broken Bridge Pass', 'Northeast Dark World'), + ('Bumper Cave Entrance Rock', 'Bumper Cave Entrance'), + ('Bumper Cave Entrance Drop', 'West Dark World'), + ('Bumper Cave Ledge Drop', 'West Dark World'), + ('Skull Woods Forest', 'Skull Woods Forest'), + ('Paradox Cave Push Block Reverse', 'Paradox Cave Chest Area'), + ('Paradox Cave Push Block', 'Paradox Cave Front'), + ('Paradox Cave Bomb Jump', 'Paradox Cave'), + ('Paradox Cave Drop', 'Paradox Cave Chest Area'), + ('Light World Death Mountain Shop', 'Light World Death Mountain Shop'), + ('Fairy Ascension Rocks', 'Fairy Ascension Plateau'), + ('Fairy Ascension Drop', 'East Death Mountain (Bottom)'), + ('Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau'), + ('Fairy Ascension Ledge Access', 'Fairy Ascension Ledge'), + ('Fairy Ascension Cave Climb', 'Fairy Ascension Cave (Top)'), + ('Fairy Ascension Cave Pots', 'Fairy Ascension Cave (Bottom)'), + ('Fairy Ascension Cave Drop', 'Fairy Ascension Cave (Drop)'), + ('Dark Death Mountain Drop (East)', 'Dark Death Mountain (East Bottom)'), + ('Swamp Palace Moat', 'Swamp Palace (First Room)'), + ('Swamp Palace Small Key Door', 'Swamp Palace (Starting Area)'), + ('Swamp Palace (Center)', 'Swamp Palace (Center)'), + ('Swamp Palace (North)', 'Swamp Palace (North)'), + ('Thieves Town Big Key Door', 'Thieves Town (Deep)'), + ('Skull Woods Torch Room', 'Skull Woods Final Section (Mothula)'), + ('Skull Woods First Section Bomb Jump', 'Skull Woods First Section (Top)'), + ('Skull Woods First Section South Door', 'Skull Woods First Section (Right)'), + ('Skull Woods First Section West Door', 'Skull Woods First Section (Left)'), + ('Skull Woods First Section (Right) North Door', 'Skull Woods First Section'), + ('Skull Woods First Section (Left) Door to Right', 'Skull Woods First Section (Right)'), + ('Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section'), + ('Skull Woods First Section (Top) One-Way Path', 'Skull Woods First Section'), + ('Skull Woods Second Section (Drop)', 'Skull Woods Second Section'), + ('Blind Fight', 'Blind Fight'), + ('Desert Palace Pots (Outer)', 'Desert Palace Main (Inner)'), + ('Desert Palace Pots (Inner)', 'Desert Palace Main (Outer)'), + ('Ice Palace Entrance Room', 'Ice Palace (Main)'), + ('Ice Palace (East)', 'Ice Palace (East)'), + ('Ice Palace (East Top)', 'Ice Palace (East Top)'), + ('Ice Palace (Kholdstare)', 'Ice Palace (Kholdstare)'), + ('Misery Mire Entrance Gap', 'Misery Mire (Main)'), + ('Misery Mire (West)', 'Misery Mire (West)'), + ('Misery Mire Big Key Door', 'Misery Mire (Final Area)'), + ('Misery Mire (Vitreous)', 'Misery Mire (Vitreous)'), + ('Turtle Rock Entrance Gap', 'Turtle Rock (First Section)'), + ('Turtle Rock Entrance Gap Reverse', 'Turtle Rock (Entrance)'), + ('Turtle Rock Pokey Room', 'Turtle Rock (Chain Chomp Room)'), + ('Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Second Section)'), + ('Turtle Rock (Chain Chomp Room) (South)', 'Turtle Rock (First Section)'), + ('Turtle Rock Chain Chomp Staircase', 'Turtle Rock (Chain Chomp Room)'), + ('Turtle Rock (Big Chest) (North)', 'Turtle Rock (Second Section)'), + ('Turtle Rock Big Key Door', 'Turtle Rock (Crystaroller Room)'), + ('Turtle Rock Big Key Door Reverse', 'Turtle Rock (Second Section)'), + ('Turtle Rock Dark Room Staircase', 'Turtle Rock (Dark Room)'), + ('Turtle Rock (Dark Room) (North)', 'Turtle Rock (Crystaroller Room)'), + ('Turtle Rock (Dark Room) (South)', 'Turtle Rock (Eye Bridge)'), + ('Turtle Rock Dark Room (South)', 'Turtle Rock (Dark Room)'), + ('Turtle Rock (Trinexx)', 'Turtle Rock (Trinexx)'), + ('Palace of Darkness Bridge Room', 'Palace of Darkness (Center)'), + ('Palace of Darkness Bonk Wall', 'Palace of Darkness (Bonk Section)'), + ('Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (Big Key Chest)'), + ('Palace of Darkness (North)', 'Palace of Darkness (North)'), + ('Palace of Darkness Big Key Door', 'Palace of Darkness (Final Section)'), + ('Palace of Darkness Hammer Peg Drop', 'Palace of Darkness (Center)'), + ('Palace of Darkness Spike Statue Room Door', 'Palace of Darkness (Harmless Hellway)'), + ('Palace of Darkness Maze Door', 'Palace of Darkness (Maze)'), + ('Ganons Tower (Tile Room)', 'Ganons Tower (Tile Room)'), + ('Ganons Tower (Tile Room) Key Door', 'Ganons Tower (Compass Room)'), + ('Ganons Tower (Bottom) (East)', 'Ganons Tower (Bottom)'), + ('Ganons Tower (Hookshot Room)', 'Ganons Tower (Hookshot Room)'), + ('Ganons Tower (Map Room)', 'Ganons Tower (Map Room)'), + ('Ganons Tower (Double Switch Room)', 'Ganons Tower (Firesnake Room)'), + ('Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)'), + ('Ganons Tower (Bottom) (West)', 'Ganons Tower (Bottom)'), + ('Ganons Tower Big Key Door', 'Ganons Tower (Top)'), + ('Ganons Tower Torch Rooms', 'Ganons Tower (Before Moldorm)'), + ('Ganons Tower Moldorm Door', 'Ganons Tower (Moldorm)'), + ('Ganons Tower Moldorm Gap', 'Agahnim 2'), + ('Ganon Drop', 'Bottom of Pyramid'), + ('Pyramid Drop', 'East Dark World'), + ('Post Aga Teleporter', 'Light World'), + ('LW Hyrule Castle Ledge SQ', 'Hyrule Castle Ledge'), + ('EDM Hyrule Castle Ledge SQ', 'Hyrule Castle Ledge'), + ('WDM Hyrule Castle Ledge SQ', 'Hyrule Castle Ledge'), + ('Secret Passage Inner Bushes', 'Light World'), + ('Secret Passage Outer Bushes', 'Hyrule Castle Secret Entrance Area'), + ('Potion Shop Inner Bushes', 'Light World'), + ('Potion Shop Outer Bushes', 'Potion Shop Area'), + ('Potion Shop Inner Rock', 'Northeast Light World'), + ('Potion Shop Outer Rock', 'Potion Shop Area'), + ('Potion Shop River Drop', 'River'), + ('Graveyard Cave Inner Bushes', 'Light World'), + ('Graveyard Cave Outer Bushes', 'Graveyard Cave Area'), + ('Graveyard Cave Mirror Spot', 'West Dark World'), + ('Light World River Drop', 'River'), + ('Light World Pier', 'Light World'), + ('Potion Shop Pier', 'Potion Shop Area'), + ('Hyrule Castle Ledge Courtyard Drop', 'Light World'), + ('Mimic Cave Ledge Access', 'Mimic Cave Ledge'), + ('Mimic Cave Ledge Drop', 'East Death Mountain (Bottom)'), + ('Turtle Rock Tail Drop', 'Turtle Rock (Top)'), + ('Turtle Rock Drop', 'Dark Death Mountain'), + ('Desert Ledge Drop', 'Light World'), + ('Floating Island Drop', 'Dark Death Mountain'), + ('Dark Lake Hylia Central Island Teleporter', 'Lake Hylia Central Island'), + ('Dark Desert Teleporter', 'Light World'), + ('East Dark World Teleporter', 'Light World'), + ('South Dark World Teleporter', 'Light World'), + ('West Dark World Teleporter', 'Light World'), + ('Dark Death Mountain Teleporter (West)', 'Death Mountain'), + ('Dark Death Mountain Teleporter (East)', 'East Death Mountain (Top)'), + ('Dark Death Mountain Teleporter (East Bottom)', 'East Death Mountain (Bottom)'), + ('Mire Mirror Spot', 'Dark Desert'), + ('Dark Desert Drop', 'Dark Desert'), + ('Desert Palace Stairs Mirror Spot', 'Dark Desert'), + ('Desert Palace North Mirror Spot', 'Dark Desert'), + ('Maze Race Mirror Spot', 'West Dark World'), + ('Lake Hylia Central Island Mirror Spot', 'Dark Lake Hylia'), + ('Hammer Peg Area Mirror Spot', 'Hammer Peg Area'), + ('Bumper Cave Ledge Mirror Spot', 'Bumper Cave Ledge'), + ('Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance'), + ('Death Mountain Mirror Spot', 'Dark Death Mountain'), + ('East Death Mountain Mirror Spot (Top)', 'Dark Death Mountain'), + ('East Death Mountain Mirror Spot (Bottom)', 'Dark Death Mountain (East Bottom)'), + ('Death Mountain (Top) Mirror Spot', 'Dark Death Mountain'), + ('Dark Death Mountain Ledge Mirror Spot (East)', 'Dark Death Mountain Ledge'), + ('Dark Death Mountain Ledge Mirror Spot (West)', 'Dark Death Mountain Ledge'), + ('Floating Island Mirror Spot', 'Death Mountain Floating Island (Dark World)'), + ('Laser Bridge Mirror Spot', 'Dark Death Mountain Isolated Ledge'), + ('East Dark World Mirror Spot', 'East Dark World'), + ('West Dark World Mirror Spot', 'West Dark World'), + ('South Dark World Mirror Spot', 'South Dark World'), + ('Potion Shop Mirror Spot', 'Northeast Dark World'), + ('Northeast Dark World Mirror Spot', 'Northeast Dark World'), + ('Shopping Mall Mirror Spot', 'Dark Lake Hylia Ledge'), + ('Skull Woods Mirror Spot', 'Skull Woods Forest (West)'), + ('DDM Flute', 'The Sky'), + ('DDM Landing', 'Dark Death Mountain'), + ('NEDW Flute', 'The Sky'), + ('NEDW Landing', 'Northeast Dark World'), + ('WDW Flute', 'The Sky'), + ('WDW Landing', 'West Dark World'), + ('SDW Flute', 'The Sky'), + ('SDW Landing', 'South Dark World'), + ('EDW Flute', 'The Sky'), + ('EDW Landing', 'East Dark World'), + ('DLHL Flute', 'The Sky'), + ('DLHL Landing', 'Dark Lake Hylia Ledge'), + ('DD Flute', 'The Sky'), + ('DD Landing', 'Dark Desert Ledge'), + ('EDDM Flute', 'The Sky'), + ('Dark Grassy Lawn Flute', 'The Sky'), + ('Hammer Peg Area Flute', 'The Sky'), + ('Chris Houlihan Room Exit', 'Pyramid Ledge'), + ('Bush Covered Lawn Inner Bushes', 'Light World'), + ('Bush Covered Lawn Outer Bushes', 'Bush Covered Lawn'), + ('Bush Covered Lawn Mirror Spot', 'Dark Grassy Lawn'), + ('Bomb Hut Inner Bushes', 'Light World'), + ('Bomb Hut Outer Bushes', 'Bomb Hut Area'), + ('Bomb Hut Mirror Spot', 'West Dark World')] # non-shuffled entrance links default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'), ("Blinds Hideout", "Blinds Hideout"), @@ -2061,6 +3367,154 @@ default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'), ('Pyramid Entrance', 'Bottom of Pyramid') ] +inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'), + ('Blinds Hideout', 'Blinds Hideout'), + ('Dam', 'Dam'), + ('Lumberjack House', 'Lumberjack House'), + ('Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance'), + ('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance'), + ('Hyrule Castle Secret Entrance Exit', 'Light World'), + ('Bonk Fairy (Light)', 'Bonk Fairy (Light)'), + ('Lake Hylia Fairy', 'Lake Hylia Healer Fairy'), + ('Lake Hylia Fortune Teller', 'Lake Hylia Fortune Teller'), + ('Light Hype Fairy', 'Swamp Healer Fairy'), + ('Desert Fairy', 'Desert Healer Fairy'), + ('Kings Grave', 'Kings Grave'), + ('Tavern North', 'Tavern'), + ('Chicken House', 'Chicken House'), + ('Aginahs Cave', 'Aginahs Cave'), + ('Sahasrahlas Hut', 'Sahasrahlas Hut'), + ('Cave Shop (Lake Hylia)', 'Cave Shop (Lake Hylia)'), + ('Capacity Upgrade', 'Capacity Upgrade'), + ('Kakariko Well Drop', 'Kakariko Well (top)'), + ('Kakariko Well Cave', 'Kakariko Well (bottom)'), + ('Kakariko Well Exit', 'Light World'), + ('Blacksmiths Hut', 'Blacksmiths Hut'), + ('Bat Cave Drop', 'Bat Cave (right)'), + ('Bat Cave Cave', 'Bat Cave (left)'), + ('Bat Cave Exit', 'Light World'), + ('Sick Kids House', 'Sick Kids House'), + ('Elder House (East)', 'Elder House'), + ('Elder House (West)', 'Elder House'), + ('Elder House Exit (East)', 'Light World'), + ('Elder House Exit (West)', 'Light World'), + ('North Fairy Cave Drop', 'North Fairy Cave'), + ('North Fairy Cave', 'North Fairy Cave'), + ('North Fairy Cave Exit', 'Light World'), + ('Lost Woods Gamble', 'Lost Woods Gamble'), + ('Fortune Teller (Light)', 'Fortune Teller (Light)'), + ('Snitch Lady (East)', 'Snitch Lady (East)'), + ('Snitch Lady (West)', 'Snitch Lady (West)'), + ('Bush Covered House', 'Bush Covered House'), + ('Tavern (Front)', 'Tavern (Front)'), + ('Light World Bomb Hut', 'Light World Bomb Hut'), + ('Kakariko Shop', 'Kakariko Shop'), + ('Lost Woods Hideout Drop', 'Lost Woods Hideout (top)'), + ('Lost Woods Hideout Stump', 'Lost Woods Hideout (bottom)'), + ('Lost Woods Hideout Exit', 'Light World'), + ('Lumberjack Tree Tree', 'Lumberjack Tree (top)'), + ('Lumberjack Tree Cave', 'Lumberjack Tree (bottom)'), + ('Lumberjack Tree Exit', 'Light World'), + ('Cave 45', 'Cave 45'), + ('Graveyard Cave', 'Graveyard Cave'), + ('Checkerboard Cave', 'Checkerboard Cave'), + ('Mini Moldorm Cave', 'Mini Moldorm Cave'), + ('Long Fairy Cave', 'Long Fairy Cave'), + ('Good Bee Cave', 'Good Bee Cave'), + ('20 Rupee Cave', '20 Rupee Cave'), + ('50 Rupee Cave', '50 Rupee Cave'), + ('Ice Rod Cave', 'Ice Rod Cave'), + ('Bonk Rock Cave', 'Bonk Rock Cave'), + ('Library', 'Library'), + ('Kakariko Gamble Game', 'Kakariko Gamble Game'), + ('Potion Shop', 'Potion Shop'), + ('Two Brothers House (East)', 'Two Brothers House'), + ('Two Brothers House (West)', 'Two Brothers House'), + ('Two Brothers House Exit (East)', 'Light World'), + ('Two Brothers House Exit (West)', 'Maze Race Ledge'), + ('Sanctuary', 'Sanctuary'), + ('Sanctuary Grave', 'Sewer Drop'), + ('Sanctuary Exit', 'Light World'), + ('Old Man House (Bottom)', 'Old Man House'), + ('Old Man House Exit (Bottom)', 'Death Mountain'), + ('Old Man House (Top)', 'Old Man House Back'), + ('Old Man House Exit (Top)', 'Death Mountain'), + ('Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Peak)'), + ('Spectacle Rock Cave (Bottom)', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave', 'Spectacle Rock Cave (Top)'), + ('Spectacle Rock Cave Exit', 'Death Mountain'), + ('Spectacle Rock Cave Exit (Top)', 'Death Mountain'), + ('Spectacle Rock Cave Exit (Peak)', 'Death Mountain'), + ('Paradox Cave (Bottom)', 'Paradox Cave Front'), + ('Paradox Cave (Middle)', 'Paradox Cave'), + ('Paradox Cave (Top)', 'Paradox Cave'), + ('Paradox Cave Exit (Bottom)', 'East Death Mountain (Bottom)'), + ('Paradox Cave Exit (Middle)', 'East Death Mountain (Bottom)'), + ('Paradox Cave Exit (Top)', 'East Death Mountain (Top)'), + ('Hookshot Fairy', 'Hookshot Fairy'), + ('Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Bottom)'), + ('Fairy Ascension Cave (Top)', 'Fairy Ascension Cave (Top)'), + ('Fairy Ascension Cave Exit (Bottom)', 'Fairy Ascension Plateau'), + ('Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Ledge'), + ('Spiral Cave', 'Spiral Cave (Top)'), + ('Spiral Cave (Bottom)', 'Spiral Cave (Bottom)'), + ('Spiral Cave Exit', 'East Death Mountain (Bottom)'), + ('Spiral Cave Exit (Top)', 'Spiral Cave Ledge'), + ('Pyramid Fairy', 'Pyramid Fairy'), + ('East Dark World Hint', 'East Dark World Hint'), + ('Palace of Darkness Hint', 'Palace of Darkness Hint'), + ('Dark Lake Hylia Shop', 'Dark Lake Hylia Shop'), + ('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'), + ('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy'), + ('Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Spike Cave'), + ('Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Hint'), + ('Hype Cave', 'Hype Cave'), + ('Bonk Fairy (Dark)', 'Bonk Fairy (Dark)'), + ('Brewery', 'Brewery'), + ('C-Shaped House', 'C-Shaped House'), + ('Chest Game', 'Chest Game'), + ('Dark World Hammer Peg Cave', 'Dark World Hammer Peg Cave'), + ('Red Shield Shop', 'Red Shield Shop'), + ('Fortune Teller (Dark)', 'Fortune Teller (Dark)'), + ('Dark World Shop', 'Village of Outcasts Shop'), + ('Dark World Lumberjack Shop', 'Dark World Lumberjack Shop'), + ('Dark World Potion Shop', 'Dark World Potion Shop'), + ('Archery Game', 'Archery Game'), + ('Mire Shed', 'Mire Shed'), + ('Dark Desert Hint', 'Dark Desert Hint'), + ('Dark Desert Fairy', 'Dark Desert Healer Fairy'), + ('Spike Cave', 'Spike Cave'), + ('Hookshot Cave', 'Hookshot Cave'), + ('Superbunny Cave (Top)', 'Superbunny Cave'), + ('Cave Shop (Dark Death Mountain)', 'Cave Shop (Dark Death Mountain)'), + ('Superbunny Cave (Bottom)', 'Superbunny Cave'), + ('Superbunny Cave Exit (Bottom)', 'Dark Death Mountain (East Bottom)'), + ('Hookshot Cave Exit (North)', 'Death Mountain Floating Island (Dark World)'), + ('Hookshot Cave Back Entrance', 'Hookshot Cave'), + ('Mimic Cave', 'Mimic Cave'), + ('Inverted Pyramid Hole', 'Pyramid'), + ('Inverted Links House', 'Inverted Links House'), + ('Inverted Links House Exit', 'South Dark World'), + ('Inverted Big Bomb Shop', 'Inverted Big Bomb Shop'), + ('Inverted Dark Sanctuary', 'Inverted Dark Sanctuary'), + ('Old Man Cave (West)', 'Bumper Cave'), + ('Old Man Cave (East)', 'Death Mountain Return Cave'), + ('Old Man Cave Exit (West)', 'Dark Death Mountain'), + ('Old Man Cave Exit (East)', 'East Dark World'), + ('Dark Death Mountain Fairy', 'Old Man Cave'), + ('Bumper Cave (Bottom)', 'Old Man Cave'), + ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'), + ('Bumper Cave Exit (Top)', 'Death Mountain Return Ledge'), + ('Bumper Cave Exit (Bottom)', 'Light World'), + ('Death Mountain Return Cave (East)', 'Bumper Cave'), + ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave'), + ('Death Mountain Return Cave Exit (West)', 'Death Mountain'), + ('Death Mountain Return Cave Exit (East)', 'Death Mountain'), + ('Hookshot Cave Exit (South)', 'Dark Death Mountain'), + ('Superbunny Cave Exit (Top)', 'Dark Death Mountain'), + ('Pyramid Exit', 'Light World'), + ('Inverted Pyramid Entrance', 'Bottom of Pyramid')] + # non shuffled dungeons default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Palace Main (Inner)'), ('Desert Palace Entrance (West)', 'Desert Palace Main (Outer)'), @@ -2121,6 +3575,58 @@ default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Palace ('Ganons Tower Exit', 'Dark Death Mountain (Top)') ] +inverted_default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Palace Main (Inner)'), + ('Desert Palace Entrance (West)', 'Desert Palace Main (Outer)'), + ('Desert Palace Entrance (North)', 'Desert Palace North'), + ('Desert Palace Entrance (East)', 'Desert Palace Main (Outer)'), + ('Desert Palace Exit (South)', 'Desert Palace Stairs'), + ('Desert Palace Exit (West)', 'Desert Ledge'), + ('Desert Palace Exit (East)', 'Desert Palace Lone Stairs'), + ('Desert Palace Exit (North)', 'Desert Palace Entrance (North) Spot'), + ('Eastern Palace', 'Eastern Palace'), + ('Eastern Palace Exit', 'Light World'), + ('Tower of Hera', 'Tower of Hera (Bottom)'), + ('Tower of Hera Exit', 'Death Mountain (Top)'), + ('Hyrule Castle Entrance (South)', 'Hyrule Castle'), + ('Hyrule Castle Entrance (West)', 'Hyrule Castle'), + ('Hyrule Castle Entrance (East)', 'Hyrule Castle'), + ('Hyrule Castle Exit (South)', 'Light World'), + ('Hyrule Castle Exit (West)', 'Hyrule Castle Ledge'), + ('Hyrule Castle Exit (East)', 'Hyrule Castle Ledge'), + ('Thieves Town', 'Thieves Town (Entrance)'), + ('Thieves Town Exit', 'West Dark World'), + ('Skull Woods First Section Hole (East)', 'Skull Woods First Section (Right)'), + ('Skull Woods First Section Hole (West)', 'Skull Woods First Section (Left)'), + ('Skull Woods First Section Hole (North)', 'Skull Woods First Section (Top)'), + ('Skull Woods First Section Door', 'Skull Woods First Section'), + ('Skull Woods First Section Exit', 'Skull Woods Forest'), + ('Skull Woods Second Section Hole', 'Skull Woods Second Section (Drop)'), + ('Skull Woods Second Section Door (East)', 'Skull Woods Second Section'), + ('Skull Woods Second Section Door (West)', 'Skull Woods Second Section'), + ('Skull Woods Second Section Exit (East)', 'Skull Woods Forest'), + ('Skull Woods Second Section Exit (West)', 'Skull Woods Forest (West)'), + ('Skull Woods Final Section', 'Skull Woods Final Section (Entrance)'), + ('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'), + ('Ice Palace', 'Ice Palace (Entrance)'), + ('Misery Mire', 'Misery Mire (Entrance)'), + ('Misery Mire Exit', 'Dark Desert'), + ('Palace of Darkness', 'Palace of Darkness (Entrance)'), + ('Palace of Darkness Exit', 'East Dark World'), + ('Swamp Palace', 'Swamp Palace (Entrance)'), + ('Swamp Palace Exit', 'South Dark World'), + ('Turtle Rock', 'Turtle Rock (Entrance)'), + ('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'), + ('Turtle Rock Ledge Exit (East)', 'Dark Death Mountain Ledge'), + ('Dark Death Mountain Ledge (West)', 'Turtle Rock (Second Section)'), + ('Dark Death Mountain Ledge (East)', 'Turtle Rock (Big Chest)'), + ('Turtle Rock Isolated Ledge Exit', 'Dark Death Mountain Isolated Ledge'), + ('Turtle Rock Isolated Ledge Entrance', 'Turtle Rock (Eye Bridge)'), + ('Inverted Ganons Tower', 'Inverted Ganons Tower (Entrance)'), + ('Inverted Ganons Tower Exit', 'Hyrule Castle Ledge'), + ('Inverted Agahnims Tower', 'Inverted Agahnims Tower'), + ('Inverted Agahnims Tower Exit', 'Dark Death Mountain'), + ('Turtle Rock Exit (Front)', 'Dark Death Mountain'), + ('Ice Palace Exit', 'Dark Lake Hylia')] # format: # Key=Name @@ -2130,6 +3636,7 @@ default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Palace # ToDo somehow merge this with creation of the locations door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000)), + 'Inverted Big Bomb Shop': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000)), 'Desert Palace Entrance (South)': (0x08, (0x0084, 0x30, 0x0314, 0x0c56, 0x00a6, 0x0ca8, 0x0128, 0x0cc3, 0x0133, 0x0a, 0xfa, 0x0000, 0x0000)), 'Desert Palace Entrance (West)': (0x0A, (0x0083, 0x30, 0x0280, 0x0c46, 0x0003, 0x0c98, 0x0088, 0x0cb3, 0x0090, 0x0a, 0xfd, 0x0000, 0x0000)), 'Desert Palace Entrance (North)': (0x0B, (0x0063, 0x30, 0x0016, 0x0c00, 0x00a2, 0x0c28, 0x0128, 0x0c6d, 0x012f, 0x00, 0x0e, 0x0000, 0x0000)), @@ -2139,7 +3646,9 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Hyrule Castle Entrance (South)': (0x03, (0x0061, 0x1b, 0x0530, 0x0692, 0x0784, 0x06cc, 0x07f8, 0x06ff, 0x0803, 0x0e, 0xfa, 0x0000, 0x87be)), 'Hyrule Castle Entrance (West)': (0x02, (0x0060, 0x1b, 0x0016, 0x0600, 0x06ae, 0x0604, 0x0728, 0x066d, 0x0733, 0x00, 0x02, 0x0000, 0x8124)), 'Hyrule Castle Entrance (East)': (0x04, (0x0062, 0x1b, 0x004a, 0x0600, 0x0856, 0x0604, 0x08c8, 0x066d, 0x08d3, 0x00, 0xfa, 0x0000, 0x8158)), + 'Inverted Pyramid Entrance': (0x35, (0x0010, 0x1b, 0x0418, 0x0679, 0x06b4, 0x06c6, 0x0728, 0x06e6, 0x0733, 0x07, 0xf9, 0x0000, 0x0000)), 'Agahnims Tower': (0x23, (0x00e0, 0x1b, 0x0032, 0x0600, 0x0784, 0x0634, 0x07f8, 0x066d, 0x0803, 0x00, 0x0a, 0x0000, 0x82be)), + 'Inverted Ganons Tower': (0x23, (0x00e0, 0x1b, 0x0032, 0x0600, 0x0784, 0x0634, 0x07f8, 0x066d, 0x0803, 0x00, 0x0a, 0x0000, 0x82be)), 'Thieves Town': (0x33, (0x00db, 0x58, 0x0b2e, 0x075a, 0x0176, 0x07a8, 0x01f8, 0x07c7, 0x0203, 0x06, 0xfa, 0x0000, 0x0000)), 'Skull Woods First Section Door': (0x29, (0x0058, 0x40, 0x0f4c, 0x01f6, 0x0262, 0x0248, 0x02e8, 0x0263, 0x02ef, 0x0a, 0xfe, 0x0000, 0x0000)), 'Skull Woods Second Section Door (East)': (0x28, (0x0057, 0x40, 0x0eb8, 0x01e6, 0x01c2, 0x0238, 0x0248, 0x0253, 0x024f, 0x0a, 0xfe, 0x0000, 0x0000)), @@ -2187,12 +3696,14 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Hookshot Cave': (0x39, (0x003c, 0x45, 0x04da, 0x00a3, 0x0cd6, 0x0107, 0x0d48, 0x0112, 0x0d53, 0x0b, 0xfa, 0x0000, 0x0000)), 'Hookshot Cave Back Entrance': (0x3A, (0x002c, 0x45, 0x004c, 0x0000, 0x0c56, 0x0038, 0x0cc8, 0x006f, 0x0cd3, 0x00, 0x0a, 0x0000, 0x0000)), 'Ganons Tower': (0x36, (0x000c, 0x43, 0x0052, 0x0000, 0x0884, 0x0028, 0x08f8, 0x006f, 0x0903, 0x00, 0xfc, 0x0000, 0x0000)), + 'Inverted Agahnims Tower': (0x36, (0x000c, 0x43, 0x0052, 0x0000, 0x0884, 0x0028, 0x08f8, 0x006f, 0x0903, 0x00, 0xfc, 0x0000, 0x0000)), 'Pyramid Entrance': (0x35, (0x0010, 0x5b, 0x0b0e, 0x075a, 0x0674, 0x07a8, 0x06e8, 0x07c7, 0x06f3, 0x06, 0xfa, 0x0000, 0x0000)), 'Skull Woods First Section Hole (West)': ([0xDB84D, 0xDB84E], None), 'Skull Woods First Section Hole (East)': ([0xDB84F, 0xDB850], None), 'Skull Woods First Section Hole (North)': ([0xDB84C], None), 'Skull Woods Second Section Hole': ([0xDB851, 0xDB852], None), 'Pyramid Hole': ([0xDB854, 0xDB855, 0xDB856], None), + 'Inverted Pyramid Hole': ([0xDB854, 0xDB855, 0xDB856, 0x180340], None), 'Waterfall of Wishing': (0x5B, (0x0114, 0x0f, 0x0080, 0x0200, 0x0e00, 0x0207, 0x0e60, 0x026f, 0x0e7d, 0x00, 0x00, 0x0000, 0x0000)), 'Dam': (0x4D, (0x010b, 0x3b, 0x04a0, 0x0e8a, 0x06fa, 0x0ed8, 0x0778, 0x0ef7, 0x077f, 0x06, 0xfa, 0x0000, 0x0000)), 'Blinds Hideout': (0x60, (0x0119, 0x18, 0x02b2, 0x064a, 0x0186, 0x0697, 0x0208, 0x06b7, 0x0213, 0x06, 0xfa, 0x0000, 0x0000)), @@ -2252,6 +3763,7 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Dark World Hammer Peg Cave': (0x7E, (0x0127, 0x62, 0x0894, 0x091e, 0x0492, 0x09a6, 0x0508, 0x098b, 0x050f, 0x00, 0x00, 0x0000, 0x0000)), 'Red Shield Shop': (0x74, (0x0110, 0x5a, 0x079a, 0x06e8, 0x04d6, 0x0738, 0x0548, 0x0755, 0x0553, 0x08, 0xf8, 0x0AA8, 0x0000)), 'Dark Sanctuary Hint': (0x59, (0x0112, 0x53, 0x001e, 0x0400, 0x06e2, 0x0446, 0x0758, 0x046d, 0x075f, 0x00, 0x00, 0x0000, 0x0000)), + 'Inverted Dark Sanctuary': (0x59, (0x0112, 0x53, 0x001e, 0x0400, 0x06e2, 0x0446, 0x0758, 0x046d, 0x075f, 0x00, 0x00, 0x0000, 0x0000)), 'Fortune Teller (Dark)': (0x65, (0x0122, 0x51, 0x0610, 0x04b4, 0x027e, 0x0507, 0x02f8, 0x0523, 0x0303, 0x0a, 0xf6, 0x091E, 0x0000)), 'Dark World Shop': (0x5F, (0x010f, 0x58, 0x1058, 0x0814, 0x02be, 0x0868, 0x0338, 0x0883, 0x0343, 0x0a, 0xf6, 0x0000, 0x0000)), 'Dark World Lumberjack Shop': (0x56, (0x010f, 0x42, 0x041c, 0x0074, 0x04e2, 0x00c7, 0x0558, 0x00e3, 0x055f, 0x0a, 0xf6, 0x0000, 0x0000)), @@ -2265,6 +3777,7 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Dark Death Mountain Fairy': (0x6F, (0x0115, 0x43, 0x1400, 0x0294, 0x0600, 0x02e8, 0x0678, 0x0303, 0x0685, 0x0a, 0xf6, 0x0000, 0x0000)), 'Mimic Cave': (0x4E, (0x010c, 0x05, 0x07e0, 0x0103, 0x0d00, 0x0156, 0x0d78, 0x0172, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000)), 'Big Bomb Shop': (0x52, (0x011c, 0x6c, 0x0506, 0x0a9a, 0x0832, 0x0ae7, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfa, 0x0816, 0x0000)), + 'Inverted Links House': (0x52, (0x011c, 0x6c, 0x0506, 0x0a9a, 0x0832, 0x0ae7, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfa, 0x0816, 0x0000)), 'Dark Lake Hylia Shop': (0x73, (0x010f, 0x75, 0x0380, 0x0c6a, 0x0a00, 0x0cb8, 0x0a58, 0x0cd7, 0x0a85, 0x06, 0xfa, 0x0000, 0x0000)), 'Lumberjack House': (0x75, (0x011f, 0x02, 0x049c, 0x0088, 0x04e6, 0x00d8, 0x0558, 0x00f7, 0x0563, 0x08, 0xf8, 0x07AA, 0x0000)), 'Lake Hylia Fortune Teller': (0x72, (0x0122, 0x35, 0x0380, 0x0c6a, 0x0a00, 0x0cb8, 0x0a58, 0x0cd7, 0x0a85, 0x06, 0xfa, 0x0000, 0x0000)), @@ -2275,6 +3788,7 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 # value = entrance # # | (entrance #, exit #) exit_ids = {'Links House Exit': (0x01, 0x00), + 'Inverted Links House Exit': (0x01, 0x00), 'Chris Houlihan Room Exit': (None, 0x3D), 'Desert Palace Exit (South)': (0x09, 0x0A), 'Desert Palace Exit (West)': (0x0B, 0x0C), @@ -2286,6 +3800,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Hyrule Castle Exit (West)': (0x03, 0x02), 'Hyrule Castle Exit (East)': (0x05, 0x04), 'Agahnims Tower Exit': (0x24, 0x25), + 'Inverted Agahnims Tower Exit': (0x24, 0x25), 'Thieves Town Exit': (0x34, 0x35), 'Skull Woods First Section Exit': (0x2A, 0x2B), 'Skull Woods Second Section Exit (East)': (0x29, 0x2A), @@ -2333,6 +3848,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Hookshot Cave Exit (South)': (0x3A, 0x3B), 'Hookshot Cave Exit (North)': (0x3B, 0x3C), 'Ganons Tower Exit': (0x37, 0x38), + 'Inverted Ganons Tower Exit': (0x37, 0x38), 'Pyramid Exit': (0x36, 0x37), 'Waterfall of Wishing': 0x5C, 'Dam': 0x4E, @@ -2384,6 +3900,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'East Dark World Hint': 0x69, 'Palace of Darkness Hint': 0x68, 'Big Bomb Shop': 0x53, + 'Inverted Big Bomb Shop': 0x53, 'Village of Outcasts Shop': 0x60, 'Dark Lake Hylia Shop': 0x60, 'Dark World Lumberjack Shop': 0x60, @@ -2397,6 +3914,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Dark World Hammer Peg Cave': 0x83, 'Red Shield Shop': 0x57, 'Dark Sanctuary Hint': 0x5A, + 'Inverted Dark Sanctuary': 0x5A, 'Fortune Teller (Dark)': 0x66, 'Archery Game': 0x59, 'Mire Shed': 0x5F, diff --git a/Gui.py b/Gui.py index be544aa837..078a3ee15b 100755 --- a/Gui.py +++ b/Gui.py @@ -145,7 +145,7 @@ def guiMain(args=None): modeFrame = Frame(drowDownFrame) modeVar = StringVar() modeVar.set('open') - modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'swordless') + modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'swordless', 'inverted') modeOptionMenu.pack(side=RIGHT) modeLabel = Label(modeFrame, text='Game Mode') modeLabel.pack(side=LEFT) diff --git a/InvertedRegions.py b/InvertedRegions.py new file mode 100644 index 0000000000..bc2ea357a8 --- /dev/null +++ b/InvertedRegions.py @@ -0,0 +1,642 @@ +import collections +from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType + + +def create_inverted_regions(world, player): + + world.regions += [ + create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest', 'Bombos Tablet'], + ["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam', + 'Inverted Big Bomb Shop', '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', + 'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier', + 'Bonk Rock Cave', 'Library', 'Two Brothers House (East)', 'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow', + 'Sanctuary', 'Sanctuary Grave', 'Death Mountain Entrance Rock', 'Light World River Drop', + 'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)', + 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', + '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', + 'East Dark World Mirror Spot', 'West Dark World Mirror Spot', 'South Dark World Mirror Spot', 'Cave 45', 'Checkerboard Cave', 'Mire Mirror Spot', 'Hammer Peg Area Mirror Spot', + 'Shopping Mall Mirror Spot', 'Skull Woods Mirror Spot', 'Inverted Pyramid Entrance','Hyrule Castle Entrance (South)', 'Secret Passage Outer Bushes', 'LW Hyrule Castle Ledge SQ', 'Bush Covered Lawn Outer Bushes', + 'Potion Shop Outer Bushes', 'Graveyard Cave Outer Bushes', 'Bomb Hut Outer Bushes']), + create_lw_region(player, 'Bush Covered Lawn', None, ['Bush Covered House', 'Bush Covered Lawn Inner Bushes', 'Bush Covered Lawn Mirror Spot']), + create_lw_region(player, 'Bomb Hut Area', None, ['Light World Bomb Hut', 'Bomb Hut Inner Bushes', 'Bomb Hut Mirror Spot']), + create_lw_region(player, 'Hyrule Castle Secret Entrance Area', None, ['Hyrule Castle Secret Entrance Stairs', 'Secret Passage Inner Bushes']), + create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop', 'Bumper Cave Entrance Mirror Spot']), + create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Mirror Spot']), + create_cave_region(player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top", + "Blind\'s Hideout - Left", + "Blind\'s Hideout - Right", + "Blind\'s Hideout - Far Left", + "Blind\'s Hideout - Far Right"]), + create_lw_region(player, 'Northeast Light World', None, ['Zoras River', 'Waterfall of Wishing', 'Potion Shop Outer Rock', 'Northeast Dark World Mirror Spot']), + create_lw_region(player, 'Potion Shop Area', None, ['Potion Shop', 'Potion Shop Inner Bushes', 'Potion Shop Inner Rock', 'Potion Shop Mirror Spot', 'Potion Shop River Drop']), + create_lw_region(player, 'Graveyard Cave Area', None, ['Graveyard Cave', 'Graveyard Cave Inner Bushes', 'Graveyard Cave Mirror Spot']), + create_lw_region(player, 'River', None, ['Light World Pier', 'Potion Shop Pier']), + 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(player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']), + create_cave_region(player, 'Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']), + create_lw_region(player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']), + create_cave_region(player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']), + create_cave_region(player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']), + create_cave_region(player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']), + create_cave_region(player, 'Inverted Links House', 'your house', ['Link\'s House'], ['Inverted Links House Exit']), + create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']), + create_cave_region(player, 'Tavern', 'the tavern', ['Kakariko Tavern']), + create_cave_region(player, 'Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']), + create_cave_region(player, 'Snitch Lady (East)', 'a boring house'), + create_cave_region(player, 'Snitch Lady (West)', 'a boring house'), + create_cave_region(player, 'Bush Covered House', 'the grass man'), + create_cave_region(player, 'Tavern (Front)', 'the tavern'), + create_cave_region(player, 'Light World Bomb Hut', 'a restock room'), + create_cave_region(player, 'Kakariko Shop', 'a common shop'), + create_cave_region(player, 'Fortune Teller (Light)', 'a fortune teller'), + create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'), + create_cave_region(player, 'Lumberjack House', 'a boring house'), + create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'), + create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'), + create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Swamp Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Desert Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Chicken House', 'a house with a chest', ['Chicken House']), + create_cave_region(player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']), + create_cave_region(player, 'Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']), + 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)']), + create_cave_region(player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']), + create_cave_region(player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']), + create_lw_region(player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']), + create_cave_region(player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']), + create_cave_region(player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']), + create_cave_region(player, 'Sick Kids House', 'the sick kid', ['Sick Kid']), + create_lw_region(player, 'Hobo Bridge', ['Hobo']), + create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']), + create_cave_region(player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']), + create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']), + create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']), + create_cave_region(player, 'Cave 45', 'a cave with an item', ['Cave 45']), + create_cave_region(player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']), + create_cave_region(player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']), + create_cave_region(player, 'Long Fairy Cave', 'a fairy fountain'), + 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']), + create_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']), + create_cave_region(player, 'Good Bee Cave', 'a cold bee'), + create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'), + create_cave_region(player, 'Cave Shop (Lake Hylia)', 'a common shop'), + create_cave_region(player, 'Cave Shop (Dark Death Mountain)', 'a common shop'), + create_cave_region(player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']), + create_cave_region(player, 'Library', 'the library', ['Library']), + create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'), + create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop']), + create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), + create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies'), + create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), + create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)', 'Maze Race Mirror Spot']), + create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'), + create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)', 'Desert Ledge Drop']), + create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)', 'Desert Palace Stairs Mirror Spot']), + create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']), + create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks', 'Desert Palace North Mirror Spot']), + 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']), + create_dungeon_region(player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']), + create_dungeon_region(player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']), + create_dungeon_region(player, 'Desert Palace North', 'Desert Palace', ['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']), + 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']), + create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']), + create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'), + create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower', 'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']), + 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']), + create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks + create_dungeon_region(player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'], ['Sewers Door']), + 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']), + create_dungeon_region(player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']), + create_dungeon_region(player, 'Inverted Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze'], ['Agahnim 1', 'Inverted Agahnims Tower Exit']), + create_dungeon_region(player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None), + create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']), + create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']), + create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']), + create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', + 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Mirror Spot', 'WDM Hyrule Castle Ledge SQ']), + create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']), + create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)', 'Bumper Cave Ledge Mirror Spot']), + create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), + create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), + create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), + create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Mirror Spot (Bottom)', 'Hookshot Fairy', + 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)', 'EDM Hyrule Castle Ledge SQ']), + create_cave_region(player, 'Hookshot Fairy', 'fairies deep in a cave'), + 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(player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left', + 'Paradox Cave Lower - Left', + 'Paradox Cave Lower - Right', + 'Paradox Cave Lower - Far Right', + 'Paradox Cave Lower - Middle', + 'Paradox Cave Upper - Left', + 'Paradox Cave Upper - Right'], + ['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']), + create_cave_region(player, 'Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']), + create_cave_region(player, 'Light World Death Mountain Shop', 'a common shop'), + create_lw_region(player, 'East Death Mountain (Top)', ['Floating Island'], ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'East Death Mountain Mirror Spot (Top)', 'Fairy Ascension Ledge Access', 'Mimic Cave Ledge Access', + 'Floating Island Mirror Spot']), + create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (West)']), + create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave', 'Mimic Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (East)']), + create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']), + create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']), + create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']), + create_cave_region(player, 'Fairy Ascension Cave (Bottom)', 'a connector', None, ['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']), + create_cave_region(player, 'Fairy Ascension Cave (Drop)', 'a connector', None, ['Fairy Ascension Cave Pots']), + create_cave_region(player, 'Fairy Ascension Cave (Top)', 'a connector', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']), + create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)', 'Laser Bridge Mirror Spot']), + create_lw_region(player, 'Death Mountain (Top)', ['Ether Tablet', 'Spectacle Rock'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop', 'Death Mountain (Top) Mirror Spot']), + create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)']), + 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(player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']), + 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(player, 'East Dark World', ['Pyramid'], ['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)', + 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Northeast Dark World Broken Bridge Pass', 'East Dark World Teleporter', 'EDW Flute']), + create_dw_region(player, 'Northeast Dark World', ['Catfish'], ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass', 'NEDW Flute', 'Dark Lake Hylia Teleporter']), + create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'), + create_cave_region(player, 'East Dark World Hint', 'a storyteller'), + create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', 'East Dark World Bridge', 'Inverted Links House', 'Archery Game', 'Bonk Fairy (Dark)', + 'Dark Lake Hylia Shop', 'South Dark World Teleporter', 'Post Aga Teleporter', 'SDW Flute']), + create_cave_region(player, 'Inverted Big Bomb Shop', 'the bomb shop'), + create_cave_region(player, 'Archery Game', 'a game of skill'), + create_dw_region(player, 'Dark Lake Hylia', None, ['East Dark World Pier', 'Dark Lake Hylia Ledge Pier', 'Ice Palace', 'Dark Lake Hylia Central Island Teleporter']), + 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', 'DLHL Flute']), + create_cave_region(player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'), + create_cave_region(player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'), + 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']), + 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', 'Bumper Cave Entrance Rock', + 'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks', 'Red Shield Shop', 'Inverted Dark Sanctuary', 'Fortune Teller (Dark)', 'Dark World Lumberjack Shop', + 'West Dark World Teleporter', 'WDW Flute']), + create_dw_region(player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Dark World Shop', 'Dark Grassy Lawn Flute']), + create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Dark World Hammer Peg Cave', 'Peg Area Rocks', 'Hammer Peg Area Flute']), + create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Drop']), + create_cave_region(player, 'Fortune Teller (Dark)', 'a fortune teller'), + create_cave_region(player, 'Village of Outcasts Shop', 'a common shop'), + create_cave_region(player, 'Dark Lake Hylia Shop', 'a common shop'), + create_cave_region(player, 'Dark World Lumberjack Shop', 'a common shop'), + create_cave_region(player, 'Dark World Potion Shop', 'a common shop'), + create_cave_region(player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']), + create_cave_region(player, 'Pyramid Fairy', 'a cave with two chests', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']), + create_cave_region(player, 'Brewery', 'a house with a chest', ['Brewery']), + create_cave_region(player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']), + create_cave_region(player, 'Chest Game', 'a game of 16 chests', ['Chest Game']), + create_cave_region(player, 'Red Shield Shop', 'the rare shop'), + create_cave_region(player, 'Inverted Dark Sanctuary', 'a storyteller'), + create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']), + 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)']), + create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']), + create_dw_region(player, 'Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy', 'DD Flute']), + create_dw_region(player, 'Dark Desert Ledge', None, ['Dark Desert Drop', 'Dark Desert Teleporter']), + create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']), + create_cave_region(player, 'Dark Desert Hint', 'a storyteller'), + create_dw_region(player, 'Dark Death Mountain', None, ['Dark Death Mountain Drop (East)', 'Inverted Agahnims Tower', 'Superbunny Cave (Top)', 'Hookshot Cave', 'Turtle Rock', + 'Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)', 'Turtle Rock Tail Drop', 'DDM Flute']), + create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)']), + create_dw_region(player, 'Turtle Rock (Top)', None, ['Dark Death Mountain Teleporter (East)', 'Turtle Rock Drop']), + create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Turtle Rock Isolated Ledge Entrance']), + create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Teleporter (East Bottom)', 'EDDM Flute']), + create_cave_region(player, 'Superbunny Cave', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], + ['Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)']), + create_cave_region(player, 'Spike Cave', 'Spike Cave', ['Spike Cave']), + 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)']), + create_dw_region(player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance']), + create_cave_region(player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']), + + create_dungeon_region(player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']), + create_dungeon_region(player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']), + create_dungeon_region(player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']), + 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)']), + 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']), + create_dungeon_region(player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest', + 'Thieves\' Town - Map Chest', + 'Thieves\' Town - Compass Chest', + 'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']), + create_dungeon_region(player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic', + 'Thieves\' Town - Big Chest', + 'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']), + create_dungeon_region(player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']), + 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(player, '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 (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 (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']), + create_dungeon_region(player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']), + 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(player, '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 (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']), + create_dungeon_region(player, 'Ice Palace (Entrance)', 'Ice Palace', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']), + 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)']), + create_dungeon_region(player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']), + create_dungeon_region(player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']), + create_dungeon_region(player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']), + create_dungeon_region(player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']), + 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']), + create_dungeon_region(player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']), + create_dungeon_region(player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']), + create_dungeon_region(player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']), + create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']), + 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']), + 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(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(player, '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 (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 (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']), + 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 Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']), + create_dungeon_region(player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']), + 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(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']), + create_dungeon_region(player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']), + 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(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']), + 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(player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']), + create_dungeon_region(player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']), + create_dungeon_region(player, 'Inverted 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', 'Inverted Ganons Tower Exit']), + create_dungeon_region(player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']), + 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 (Bottom) (East)']), + 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 (Map Room)', 'Ganons Tower (Double Switch Room)']), + create_dungeon_region(player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']), + create_dungeon_region(player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']), + 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 (Bottom) (West)']), + 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']), + create_dungeon_region(player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']), + 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']), + create_dungeon_region(player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']), + create_dungeon_region(player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None), + create_cave_region(player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']), + create_cave_region(player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']), + create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Drop']), # houlihan room exits here in inverted + + # to simplify flute connections + create_cave_region(player, 'The Sky', 'A Dark Sky', None, ['DDM Landing','NEDW Landing', 'WDW Landing', 'SDW Landing', 'EDW Landing', 'DD Landing', 'DLHL Landing']) + ] + + for region_name, (room_id, shopkeeper, replaceable) in shop_table.items(): + region = world.get_region(region_name, player) + shop = Shop(region, room_id, ShopType.Shop, shopkeeper, replaceable) + region.shop = shop + world.shops.append(shop) + for index, (item, price) in enumerate(default_shop_contents[region_name]): + shop.add_inventory(index, item, price) + + region = world.get_region('Capacity Upgrade', player) + shop = Shop(region, 0x0115, ShopType.UpgradeShop, 0x04, True) + region.shop = shop + world.shops.append(shop) + shop.add_inventory(0, 'Bomb Upgrade (+5)', 100, 7) + shop.add_inventory(1, 'Arrow Upgrade (+5)', 100, 7) + world.intialize_regions() + +def create_lw_region(player, name, locations=None, exits=None): + return _create_region(player, name, RegionType.LightWorld, 'Light World', locations, exits) + +def create_dw_region(player, name, locations=None, exits=None): + return _create_region(player, name, RegionType.DarkWorld, 'Dark World', locations, exits) + +def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None): + return _create_region(player, name, RegionType.Cave, hint, locations, exits) + +def create_dungeon_region(player, name, hint='Hyrule', locations=None, exits=None): + return _create_region(player, name, RegionType.Dungeon, hint, locations, exits) + +def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None): + ret = Region(name, type, hint, player) + if locations is None: + locations = [] + if exits is None: + exits = [] + + for exit in exits: + ret.exits.append(Entrance(player, exit, ret)) + for location in locations: + address, crystal, hint_text = location_table[location] + ret.locations.append(Location(player, location, address, crystal, hint_text, ret)) + return ret + +def mark_dark_world_regions(world): + # cross world caves may have some sections marked as both in_light_world, and in_dark_work. + # That is ok. the bunny logic will check for this case and incorporate special rules. + + queue = collections.deque(region for region in world.regions if region.type == RegionType.DarkWorld) + seen = set(queue) + while queue: + current = queue.popleft() + current.is_dark_world = True + for exit in current.exits: + if exit.connected_region.type == RegionType.LightWorld: + # Don't venture into the dark world + continue + if exit.connected_region not in seen: + seen.add(exit.connected_region) + queue.append(exit.connected_region) + + queue = collections.deque(region for region in world.regions if region.type == RegionType.LightWorld) + seen = set(queue) + while queue: + current = queue.popleft() + current.is_light_world = True + for exit in current.exits: + if exit.connected_region.type == RegionType.DarkWorld: + # Don't venture into the light world + continue + if exit.connected_region not in seen: + seen.add(exit.connected_region) + queue.append(exit.connected_region) + +# (room_id, shopkeeper, replaceable) +shop_table = { + 'Cave Shop (Dark Death Mountain)': (0x0112, 0xC1, True), + 'Red Shield Shop': (0x0110, 0xC1, True), + 'Dark Lake Hylia Shop': (0x010F, 0xC1, True), + 'Dark World Lumberjack Shop': (0x010F, 0xC1, True), + 'Village of Outcasts Shop': (0x010F, 0xC1, True), + 'Dark World Potion Shop': (0x010F, 0xC1, True), + 'Light World Death Mountain Shop': (0x00FF, 0xA0, True), + 'Kakariko Shop': (0x011F, 0xA0, True), + 'Cave Shop (Lake Hylia)': (0x0112, 0xA0, True), + 'Potion Shop': (0x0109, 0xFF, False), + # Bomb Shop not currently modeled as a shop, due to special nature of items +} +# region, [item] +# slot, item, price, max=0, replacement=None, replacement_price=0 +# item = (item, price) + +_basic_shop_defaults = [('Red Potion', 150), ('Small Heart', 10), ('Bombs (10)', 50)] +_dark_world_shop_defaults = [('Red Potion', 150), ('Blue Shield', 50), ('Bombs (10)', 50)] +default_shop_contents = { + 'Cave Shop (Dark Death Mountain)': _basic_shop_defaults, + 'Red Shield Shop': [('Red Shield', 500), ('Bee', 10), ('Arrows (10)', 30)], + 'Dark Lake Hylia Shop': _dark_world_shop_defaults, + 'Dark World Lumberjack Shop': _dark_world_shop_defaults, + 'Village of Outcasts Shop': _dark_world_shop_defaults, + 'Dark World Potion Shop': _dark_world_shop_defaults, + 'Light World Death Mountain Shop': _basic_shop_defaults, + 'Kakariko Shop': _basic_shop_defaults, + 'Cave Shop (Lake Hylia)': _basic_shop_defaults, + 'Potion Shop': [('Red Potion', 120), ('Green Potion', 60), ('Blue Potion', 160)], +} + +location_table = {'Mushroom': (0x180013, False, 'in the woods'), + 'Bottle Merchant': (0x2EB18, False, 'with a merchant'), + 'Flute Spot': (0x18014A, False, 'underground'), + 'Sunken Treasure': (0x180145, False, 'underwater'), + 'Purple Chest': (0x33D68, False, 'from a box'), + 'Blind\'s Hideout - Top': (0xEB0F, 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 - Far Left': (0xEB18, False, 'in a basement'), + 'Blind\'s Hideout - Far Right': (0xEB1B, False, 'in a basement'), + 'Link\'s Uncle': (0x2DF45, False, 'with your uncle'), + 'Secret Passage': (0xE971, False, 'near your uncle'), + 'King Zora': (0xEE1C3, False, 'at a high price'), + 'Zora\'s Ledge': (0x180149, False, 'near Zora'), + 'Waterfall Fairy - Left': (0xE9B0, False, 'near a fairy'), + 'Waterfall Fairy - Right': (0xE9D1, False, 'near a fairy'), + 'King\'s Tomb': (0xE97A, False, 'alone in a cave'), + 'Floodgate Chest': (0xE98C, False, 'in the dam'), + 'Link\'s House': (0xE9BC, False, 'in your home'), + 'Kakariko Tavern': (0xE9CE, False, 'in the bar'), + 'Chicken House': (0xE9E9, False, 'near poultry'), + 'Aginah\'s Cave': (0xE9F2, False, 'with Aginah'), + 'Sahasrahla\'s Hut - Left': (0xEA82, False, 'near the elder'), + 'Sahasrahla\'s Hut - Middle': (0xEA85, False, 'near the elder'), + 'Sahasrahla\'s Hut - Right': (0xEA88, False, 'near the elder'), + 'Sahasrahla': (0x2F1FC, False, 'with the elder'), + 'Kakariko Well - Top': (0xEA8E, False, 'in a well'), + 'Kakariko Well - Left': (0xEA91, False, 'in a well'), + 'Kakariko Well - Middle': (0xEA94, False, 'in a well'), + 'Kakariko Well - Right': (0xEA97, False, 'in a well'), + 'Kakariko Well - Bottom': (0xEA9A, False, 'in a well'), + 'Blacksmith': (0x18002A, False, 'with the smith'), + 'Magic Bat': (0x180015, False, 'with the bat'), + 'Sick Kid': (0x339CF, False, 'with the sick'), + 'Hobo': (0x33E7D, False, 'with the hobo'), + 'Lost Woods Hideout': (0x180000, False, 'near a thief'), + 'Lumberjack Tree': (0x180001, False, 'in a hole'), + 'Cave 45': (0x180003, False, 'alone in a cave'), + 'Graveyard Cave': (0x180004, 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 - Left': (0xEB45, False, 'near Moldorms'), + 'Mini Moldorm Cave - Right': (0xEB48, False, 'near Moldorms'), + 'Mini Moldorm Cave - Far Right': (0xEB4B, False, 'near Moldorms'), + 'Mini Moldorm Cave - Generous Guy': (0x180010, False, 'near Moldorms'), + 'Ice Rod Cave': (0xEB4E, False, 'in a frozen cave'), + 'Bonk Rock Cave': (0xEB3F, False, 'alone in a cave'), + 'Library': (0x180012, False, 'near books'), + 'Potion Shop': (0x180014, False, 'near potions'), + 'Lake Hylia Island': (0x180144, False, 'on an island'), + 'Maze Race': (0x180142, False, 'at the race'), + 'Desert Ledge': (0x180143, False, 'in the desert'), + 'Desert Palace - Big Chest': (0xE98F, False, 'in Desert Palace'), + 'Desert Palace - Torch': (0x180160, False, 'in Desert Palace'), + 'Desert Palace - Map Chest': (0xE9B6, 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 - Boss': (0x180151, False, 'with Lanmolas'), + 'Eastern Palace - Compass Chest': (0xE977, False, 'in Eastern Palace'), + 'Eastern Palace - Big Chest': (0xE97D, 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 - Map Chest': (0xE9F5, False, 'in Eastern Palace'), + 'Eastern Palace - Boss': (0x180150, False, 'with the Armos'), + 'Master Sword Pedestal': (0x289B0, False, 'at the pedestal'), + 'Hyrule Castle - Boomerang Chest': (0xE974, False, 'in Hyrule Castle'), + 'Hyrule Castle - Map Chest': (0xEB0C, False, 'in Hyrule Castle'), + 'Hyrule Castle - Zelda\'s Chest': (0xEB09, False, 'in Hyrule Castle'), + 'Sewers - Dark Cross': (0xE96E, 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 - Right': (0xEB63, False, 'in the sewers'), + 'Sanctuary': (0xEA79, False, 'in Sanctuary'), + 'Castle Tower - Room 03': (0xEAB5, False, 'in Castle Tower'), + 'Castle Tower - Dark Maze': (0xEAB2, False, 'in Castle Tower'), + 'Old Man': (0xF69FA, False, 'with the old man'), + '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 - Left': (0xEB2D, 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 - Middle': (0xEB36, 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'), + 'Spiral Cave': (0xE9BF, False, 'in spiral cave'), + 'Ether Tablet': (0x180016, False, 'at a monolith'), + 'Spectacle Rock': (0x180140, False, 'atop a rock'), + '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 - Big Key Chest': (0xE9E6, 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 - Boss': (0x180152, False, 'with Moldorm'), + 'Pyramid': (0x180147, False, 'on the pyramid'), + 'Catfish': (0xEE185, False, 'with a catfish'), + 'Stumpy': (0x330C7, False, 'with tree boy'), + 'Digging Game': (0x180148, False, 'underground'), + 'Bombos Tablet': (0x180017, False, 'at a monolith'), + 'Hype Cave - Top': (0xEB1E, 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 - Bottom': (0xEB27, False, 'near a bat-like man'), + 'Hype Cave - Generous Guy': (0x180011, False, 'with a bat-like man'), + 'Peg Cave': (0x180006, False, 'alone in a cave'), + 'Pyramid Fairy - Left': (0xE980, False, 'near a fairy'), + 'Pyramid Fairy - Right': (0xE983, False, 'near a fairy'), + 'Brewery': (0xE9EC, False, 'alone in a home'), + 'C-Shaped House': (0xE9EF, False, 'alone in a home'), + 'Chest Game': (0xEDA8, False, 'as a prize'), + 'Bumper Cave Ledge': (0x180146, False, 'on a ledge'), + 'Mire Shed - Left': (0xEA73, False, 'near sparks'), + 'Mire Shed - Right': (0xEA76, False, 'near sparks'), + 'Superbunny Cave - Top': (0xEA7C, False, 'in a connection'), + 'Superbunny Cave - Bottom': (0xEA7F, False, 'in a connection'), + 'Spike Cave': (0xEA8B, False, 'beyond spikes'), + 'Hookshot Cave - Top Right': (0xEB51, False, 'across pits'), + 'Hookshot Cave - Top Left': (0xEB54, False, 'across pits'), + 'Hookshot Cave - Bottom Right': (0xEB5A, False, 'across pits'), + 'Hookshot Cave - Bottom Left': (0xEB57, False, 'across pits'), + 'Floating Island': (0x180141, False, 'on an island'), + 'Mimic Cave': (0xE9C5, False, 'in a cave of mimicry'), + 'Swamp Palace - Entrance': (0xEA9D, False, 'in Swamp Palace'), + 'Swamp Palace - Map Chest': (0xE986, False, 'in Swamp Palace'), + 'Swamp Palace - Big Chest': (0xE989, 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 - West Chest': (0xEAA3, 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 - Waterfall Room': (0xEAAF, False, 'in Swamp Palace'), + 'Swamp Palace - Boss': (0x180154, False, 'with Arrghus'), + 'Thieves\' Town - Big Key Chest': (0xEA04, False, 'in Thieves\' Town'), + 'Thieves\' Town - Map Chest': (0xEA01, False, 'in Thieves\' Town'), + 'Thieves\' Town - Compass Chest': (0xEA07, False, 'in Thieves\' Town'), + 'Thieves\' Town - Ambush Chest': (0xEA0A, False, 'in Thieves\' Town'), + 'Thieves\' Town - Attic': (0xEA0D, 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 - Boss': (0x180156, False, 'with Blind'), + 'Skull Woods - Compass Chest': (0xE992, False, 'in Skull Woods'), + 'Skull Woods - Map Chest': (0xE99B, False, 'in Skull Woods'), + 'Skull Woods - Big Chest': (0xE998, False, 'in Skull Woods'), + 'Skull Woods - Pot Prison': (0xE9A1, 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 - Bridge Room': (0xE9FE, False, 'near Mothula'), + 'Skull Woods - Boss': (0x180155, False, 'with Mothula'), + 'Ice Palace - Compass Chest': (0xE9D4, False, 'in Ice Palace'), + 'Ice Palace - Freezor Chest': (0xE995, 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 - Spike Room': (0xE9E0, 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 - Boss': (0x180157, False, 'with Kholdstare'), + 'Misery Mire - Big Chest': (0xEA67, False, 'in Misery Mire'), + 'Misery Mire - Map Chest': (0xEA6A, False, 'in Misery Mire'), + 'Misery Mire - Main Lobby': (0xEA5E, False, 'in Misery Mire'), + 'Misery Mire - Bridge Chest': (0xEA61, False, 'in Misery Mire'), + 'Misery Mire - Spike Chest': (0xE9DA, 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 - Boss': (0x180158, False, 'with Vitreous'), + 'Turtle Rock - Compass Chest': (0xEA22, 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 - Chain Chomps': (0xEA16, 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 - Crystaroller Room': (0xEA34, 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 - Top Left': (0xEA2B, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Top Right': (0xEA28, False, 'in Turtle Rock'), + 'Turtle Rock - Boss': (0x180159, False, 'with Trinexx'), + '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 - Stalfos Basement': (0xEA49, 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 - Map Chest': (0xEA52, 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 - Right': (0xEA4F, 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 - Big Chest': (0xEA40, 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'), + '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 - Right': (0xEADC, 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 Right': (0xEAE8, 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 - 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 - Bottom Left': (0xEABE, 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 - 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 Right': (0xEAC7, 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 - Bob\'s Chest': (0xEADF, 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 - Right': (0xEAF7, 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 - Right': (0xEB00, 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'), + 'Ganon': (None, False, 'from me'), + 'Agahnim 1': (None, False, 'from Ganon\'s wizardry form'), + 'Agahnim 2': (None, False, 'from Ganon\'s wizardry form'), + 'Floodgate': (None, False, None), + 'Frog': (None, False, None), + 'Missing Smith': (None, False, None), + 'Dark Blacksmith Ruins': (None, False, None), + 'Eastern Palace - Prize': ([0x1209D, 0x53EF8, 0x53EF9, 0x180052, 0x18007C, 0xC6FE], True, 'Eastern Palace'), + 'Desert Palace - Prize': ([0x1209E, 0x53F1C, 0x53F1D, 0x180053, 0x180078, 0xC6FF], True, 'Desert Palace'), + 'Tower of Hera - Prize': ([0x120A5, 0x53F0A, 0x53F0B, 0x18005A, 0x18007A, 0xC706], True, 'Tower of Hera'), + 'Palace of Darkness - Prize': ([0x120A1, 0x53F00, 0x53F01, 0x180056, 0x18007D, 0xC702], True, 'Palace of Darkness'), + 'Swamp Palace - Prize': ([0x120A0, 0x53F6C, 0x53F6D, 0x180055, 0x180071, 0xC701], True, 'Swamp Palace'), + 'Thieves\' Town - Prize': ([0x120A6, 0x53F36, 0x53F37, 0x18005B, 0x180077, 0xC707], True, 'Thieves\' Town'), + 'Skull Woods - Prize': ([0x120A3, 0x53F12, 0x53F13, 0x180058, 0x18007B, 0xC704], True, 'Skull Woods'), + 'Ice Palace - Prize': ([0x120A4, 0x53F5A, 0x53F5B, 0x180059, 0x180073, 0xC705], True, 'Ice Palace'), + 'Misery Mire - Prize': ([0x120A2, 0x53F48, 0x53F49, 0x180057, 0x180075, 0xC703], True, 'Misery Mire'), + 'Turtle Rock - Prize': ([0x120A7, 0x53F24, 0x53F25, 0x18005C, 0x180079, 0xC708], True, 'Turtle Rock')} diff --git a/ItemList.py b/ItemList.py index 97ab419827..629f293189 100644 --- a/ItemList.py +++ b/ItemList.py @@ -209,7 +209,7 @@ difficulties = { 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'] - 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', 'inverted'] 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') if world.timer in ['ohko', 'timed-ohko']: @@ -662,7 +662,7 @@ def test(): for difficulty in ['easy', 'normal', 'hard', 'expert', 'insane']: for goal in ['ganon', 'triforcehunt', 'pedestal']: for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']: - for mode in ['open', 'standard', 'swordless']: + for mode in ['open', 'standard', 'swordless', 'inverted']: for progressive in ['on', 'off']: for shuffle in ['full', 'insane']: for retro in [True, False]: diff --git a/Main.py b/Main.py index bef644a992..33afc1d14d 100644 --- a/Main.py +++ b/Main.py @@ -9,7 +9,8 @@ import time from BaseClasses import World, CollectionState, Item, Region, Location, Shop from Regions import create_regions, mark_light_world_regions -from EntranceShuffle import link_entrances +from InvertedRegions import create_inverted_regions, mark_dark_world_regions +from EntranceShuffle import link_entrances, link_inverted_entrances from Rom import patch_rom, get_enemizer_patch, apply_rom_settings, Sprite, LocalRom, JsonRom from Rules import set_rules from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive @@ -38,16 +39,27 @@ def main(args, seed=None): world.difficulty_requirements = difficulties[world.difficulty] - for player in range(1, world.players + 1): - create_regions(world, player) - create_dungeons(world, player) + if world.mode != 'inverted': + for player in range(1, world.players + 1): + create_regions(world, player) + create_dungeons(world, player) + else: + for player in range(1, world.players + 1): + create_inverted_regions(world, player) + create_dungeons(world, player) logger.info('Shuffling the World about.') - for player in range(1, world.players + 1): - link_entrances(world, player) + if world.mode != 'inverted': + for player in range(1, world.players + 1): + link_entrances(world, player) - mark_light_world_regions(world) + mark_light_world_regions(world) + else: + for player in range(1, world.players + 1): + link_inverted_entrances(world, player) + + mark_dark_world_regions(world) logger.info('Generating Item Pool.') @@ -189,9 +201,14 @@ def copy_world(world): ret.fix_fake_world = world.fix_fake_world ret.lamps_needed_for_dark_rooms = world.lamps_needed_for_dark_rooms - for player in range(1, world.players + 1): - create_regions(ret, player) - create_dungeons(ret, player) + if world.mode != 'inverted': + for player in range(1, world.players + 1): + create_regions(ret, player) + create_dungeons(ret, player) + else: + for player in range(1, world.players + 1): + create_inverted_regions(ret, player) + create_dungeons(ret, player) copy_dynamic_regions_and_locations(world, ret) @@ -365,7 +382,10 @@ def create_playthrough(world): 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)) + if world.mode != 'inverted': + old_world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = get_path(state, world.get_region('Big Bomb Shop', player)) + else: + old_world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = get_path(state, world.get_region('Inverted Big Bomb Shop', player)) # 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)]) diff --git a/README.md b/README.md index 8da432d650..4c07f5ec8b 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,16 @@ Special notes: - The magic barrier to Hyrule Castle Tower can be broken with a Hammer. - The Hammer can be used to activate the Ether and Bombos tablets. +### Inverted + +This mode is similar to Open but requires the Moon Pearl in order to not transform into a bunny in the Light World and the Sanctuary spawn point is moved to the Dark Sanctuary + +Special Notes: + +- Link's House is shuffled freely. The Dark Sanctuary is shuffled within West Dark World. +- There are a number of overworld changes to account for the inability to mirror from the Light World to the Dark World. +- Legacy shuffles are not implemente for this mode. + ## Game Logic This determines the Item Requirements for each location. @@ -336,7 +346,7 @@ Output a Spoiler File (default: False) Select the game logic (default: noglitches) ``` ---mode [{standard,open,swordless}] +--mode [{standard,open,swordless,inverted}] ``` Select the game mode. (default: open) diff --git a/Rom.py b/Rom.py index b16b88b6e0..c425169c0c 100644 --- a/Rom.py +++ b/Rom.py @@ -3,17 +3,18 @@ import json import hashlib import logging import os +import random import struct import subprocess -import random from BaseClasses import ShopType, Region, Location, Item from Dungeons import dungeon_music_addresses -from Text import MultiByteTextMapper, text_addresses, Credits, TextTable +from Text import MultiByteTextMapper, CompressedTextMapper, 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 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 output_path, local_path, int16_as_bytes, int32_as_bytes +from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc from Items import ItemFactory, item_table +from EntranceShuffle import door_addresses JAP10HASH = '03a63945398191337e896e5771f77173' @@ -37,6 +38,10 @@ class JsonRom(object): def write_int16(self, address, value): self.write_bytes(address, int16_as_bytes(value)) + def write_int16s(self, startaddress, values): + byte_list = [int16_as_bytes(value) for value in values] + self.patches[str(startaddress)] = [byte for bytes in byte_list for byte in bytes] + def write_int32(self, address, value): self.write_bytes(address, int32_as_bytes(value)) @@ -70,9 +75,17 @@ class LocalRom(object): def write_int16(self, address, value): self.write_bytes(address, int16_as_bytes(value)) + def write_int16s(self, startaddress, values): + for i, value in enumerate(values): + self.write_int16(startaddress + (i * 2), value) + def write_int32(self, address, value): self.write_bytes(address, int32_as_bytes(value)) + def write_int32s(self, startaddress, values): + for i, value in enumerate(values): + self.write_int32(startaddress + (i * 2), value) + def write_to_file(self, file): with open(file, 'wb') as outfile: outfile.write(self.buffer) @@ -218,14 +231,21 @@ def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shuffleene 'IcePalace': world.get_dungeon("Ice Palace", player).boss.enemizer_name, 'MiseryMire': world.get_dungeon("Misery Mire", player).boss.enemizer_name, 'TurtleRock': world.get_dungeon("Turtle Rock", player).boss.enemizer_name, - 'GanonsTower1': world.get_dungeon('Ganons Tower', player).bosses['bottom'].enemizer_name, - 'GanonsTower2': world.get_dungeon('Ganons Tower', player).bosses['middle'].enemizer_name, - 'GanonsTower3': world.get_dungeon('Ganons Tower', player).bosses['top'].enemizer_name, 'GanonsTower4': 'Agahnim2', 'Ganon': 'Ganon', } } + if world.mode != 'inverted': + options['ManualBosses']['GanonsTower1'] = world.get_dungeon('Ganons Tower', player).bosses['bottom'].enemizer_name + options['ManualBosses']['GanonsTower2'] = world.get_dungeon('Ganons Tower', player).bosses['middle'].enemizer_name + options['ManualBosses']['GanonsTower3'] = world.get_dungeon('Ganons Tower', player).bosses['top'].enemizer_name + else: + options['ManualBosses']['GanonsTower1'] = world.get_dungeon('Inverted Ganons Tower', player).bosses['bottom'].enemizer_name + options['ManualBosses']['GanonsTower2'] = world.get_dungeon('Inverted Ganons Tower', player).bosses['middle'].enemizer_name + options['ManualBosses']['GanonsTower3'] = world.get_dungeon('Inverted Ganons Tower', player).bosses['top'].enemizer_name + + rom.write_to_file(randopatch_path) with open(options_path, 'w') as f: @@ -501,7 +521,9 @@ def patch_rom(world, player, rom): else: # patch door table rom.write_byte(0xDBB73 + exit.addresses, exit.target) - + if world.mode == 'inverted': + patch_shuffled_dark_sanc(world, rom, player) + write_custom_shops(rom, world, player) # patch medallion requirements @@ -527,7 +549,7 @@ def patch_rom(world, player, rom): rom.write_byte(0x51DE, 0x00) # set open mode: - if world.mode in ['open', 'swordless']: + if world.mode in ['open', 'swordless', 'inverted']: rom.write_byte(0x180032, 0x01) # open mode # disable sword sprite from uncle @@ -541,7 +563,9 @@ def patch_rom(world, player, rom): rom.write_bytes(0x6D2EB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E]) rom.write_bytes(0x6D31B, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) rom.write_bytes(0x6D323, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) - else: + if world.mode == 'inverted': + set_inverted_mode(world, rom) + elif world.mode == 'standard': rom.write_byte(0x180032, 0x00) # standard mode # set light cones @@ -855,6 +879,8 @@ def patch_rom(world, player, rom): # assorted fixes 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. + if world.mode == 'inverted': + rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted 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(0x180168, 0x08) # Spike Cave Damage @@ -877,12 +903,12 @@ def patch_rom(world, player, rom): rom.write_byte(0x18302C, 0x18) # starting max health rom.write_byte(0x18302D, 0x18) # starting current health rom.write_byte(0x183039, 0x68) # starting abilities, bit array - rom.write_byte(0x18004A, 0x00) # Inverted mode (off) + rom.write_byte(0x18004A, 0x00 if world.mode != 'inverted' else 0x01) # Inverted mode rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier - rom.write_byte(0x2AF79, 0xD0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both) - rom.write_byte(0x3A943, 0xD0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) - rom.write_byte(0x3A96D, 0xF0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) - rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) + rom.write_byte(0x2AF79, 0xD0 if world.mode != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both) + rom.write_byte(0x3A943, 0xD0 if world.mode != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) + rom.write_byte(0x3A96D, 0xF0 if world.mode != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) + rom.write_byte(0x3A9A7, 0xD0 if world.mode != 'inverted' else 0xF0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) rom.write_bytes(0x180080, [50, 50, 70, 70]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10) @@ -1321,6 +1347,18 @@ def write_strings(rom, world, player): 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 + # inverted spawn menu changes + if world.mode == 'inverted': + tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n{CHOICE3}" + tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n Mountain Cave\n{CHOICE2}" + tt['intro_main'] = CompressedTextMapper.convert( + "{INTRO}\n Episode III\n{PAUSE3}\n A Link to\n the Past\n" + + "{PAUSE3}\nInverted\n Randomizer\n{PAUSE3}\nAfter mostly disregarding what happened in the first two games.\n" + + "{PAUSE3}\nLink has been transported to the Dark World\n{PAUSE3}\nWhile he was slumbering\n" + + "{PAUSE3}\nWhatever will happen?\n{PAUSE3}\n{CHANGEPIC}\nGanon has moved around all the items in Hyrule.\n" + + "{PAUSE7}\nYou will have to find all the items necessary to beat Ganon.\n" + + "{PAUSE7}\nThis is your chance to be a hero.\n{PAUSE3}\n{CHANGEPIC}\n" + + "You must get the 7 crystals to beat Ganon.\n{PAUSE9}\n{CHANGEPIC}", False) rom.write_bytes(0xE0000, tt.getBytes()) credits = Credits() @@ -1359,6 +1397,218 @@ def write_strings(rom, world, player): rom.write_bytes(0x181500, data) rom.write_bytes(0x76CC0, [byte for p in pointers for byte in [p & 0xFF, p >> 8 & 0xFF]]) +def set_inverted_mode(world, rom): + rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals + rom.write_byte(snes_to_pc(0x02B34D), 0xF0) + rom.write_byte(snes_to_pc(0x06DB78), 0x8B) + rom.write_byte(snes_to_pc(0x0DB3C5), 0xC6) + rom.write_byte(snes_to_pc(0x07A3F4), 0xF0) # duck + rom.write_int16s(snes_to_pc(0x02E849), [0x0043, 0x0056, 0x0058, 0x006C, 0x006F, 0x0070, 0x007B, 0x007F, 0x001B]) # dw flute + rom.write_int16(snes_to_pc(0x02E8D5), 0x07C8) + rom.write_int16(snes_to_pc(0x02E8F7), 0x01F8) + rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof + # the following bytes should only be written in vanilla + # or they'll overwrite the randomizer's shuffles + if world.shuffle == 'vanilla': + rom.write_byte(0x15B8C, 0x6C) + rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house + rom.write_byte(0xDBB73 + 0x52, 0x01) + rom.write_byte(0xDBB73 + 0x23, 0x37) # switch AT and GT + rom.write_byte(0xDBB73 + 0x36, 0x24) + rom.write_int16(0x15AEE + 2*0x38, 0x00E0) + rom.write_int16(0x15AEE + 2*0x25, 0x000C) + rom.write_byte(0xDBB73 + 0x15, 0x06) # bumper and old man cave + rom.write_int16(0x15AEE + 2*0x17, 0x00F0) + rom.write_byte(0xDBB73 + 0x05, 0x16) + rom.write_int16(0x15AEE + 2*0x07, 0x00FB) + rom.write_byte(0xDBB73 + 0x2D, 0x17) + rom.write_int16(0x15AEE + 2*0x2F, 0x00EB) + rom.write_byte(0xDBB73 + 0x06, 0x2E) + rom.write_int16(0x15AEE + 2*0x08, 0x00E6) + rom.write_byte(0xDBB73 + 0x16, 0x5E) + rom.write_byte(0xDBB73 + 0x6F, 0x07) # DDM fairy to old man cave + rom.write_int16(0x15AEE + 2*0x18, 0x00F1) + rom.write_byte(0x15B8C + 0x18, 0x43) + rom.write_int16(0x15BDB + 2 * 0x18, 0x1400) + rom.write_int16(0x15C79 + 2 * 0x18, 0x0294) + rom.write_int16(0x15D17 + 2 * 0x18, 0x0600) + rom.write_int16(0x15DB5 + 2 * 0x18, 0x02E8) + rom.write_int16(0x15E53 + 2 * 0x18, 0x0678) + rom.write_int16(0x15EF1 + 2 * 0x18, 0x0303) + rom.write_int16(0x15F8F + 2 * 0x18, 0x0685) + rom.write_byte(0x1602D + 0x18, 0x0A) + rom.write_byte(0x1607C + 0x18, 0xF6) + rom.write_int16(0x160CB + 2 * 0x18, 0x0000) + rom.write_int16(0x16169 + 2 * 0x18, 0x0000) + rom.write_int16(0x15AEE + 2 * 0x3D, 0x0003) # pyramid exit and houlihan + rom.write_byte(0x15B8C + 0x3D, 0x5B) + rom.write_int16(0x15BDB + 2 * 0x3D, 0x0B0E) + rom.write_int16(0x15C79 + 2 * 0x3D, 0x075A) + rom.write_int16(0x15D17 + 2 * 0x3D, 0x0674) + rom.write_int16(0x15DB5 + 2 * 0x3D, 0x07A8) + rom.write_int16(0x15E53 + 2 * 0x3D, 0x06E8) + rom.write_int16(0x15EF1 + 2 * 0x3D, 0x07C7) + rom.write_int16(0x15F8F + 2 * 0x3D, 0x06F3) + rom.write_byte(0x1602D + 0x3D, 0x06) + rom.write_byte(0x1607C + 0x3D, 0xFA) + rom.write_int16(0x160CB + 2 * 0x3D, 0x0000) + rom.write_int16(0x16169 + 2 * 0x3D, 0x0000) + rom.write_int16(snes_to_pc(0x02D8D4), 0x112) # change sactuary spawn point to dark sanc + rom.write_bytes(snes_to_pc(0x02D8E8), [0x22, 0x22, 0x22, 0x23, 0x04, 0x04, 0x04, 0x05]) + rom.write_int16(snes_to_pc(0x02D91A), 0x0400) + rom.write_int16(snes_to_pc(0x02D928), 0x222E) + rom.write_int16(snes_to_pc(0x02D936), 0x229A) + rom.write_int16(snes_to_pc(0x02D944), 0x0480) + rom.write_int16(snes_to_pc(0x02D952), 0x00A5) + rom.write_int16(snes_to_pc(0x02D960), 0x007F) + rom.write_byte(snes_to_pc(0x02D96D), 0x14) + rom.write_byte(snes_to_pc(0x02D974), 0x00) + rom.write_byte(snes_to_pc(0x02D97B), 0xFF) + rom.write_byte(snes_to_pc(0x02D982), 0x00) + rom.write_byte(snes_to_pc(0x02D989), 0x02) + rom.write_byte(snes_to_pc(0x02D990), 0x00) + rom.write_int16(snes_to_pc(0x02D998), 0x0000) + rom.write_int16(snes_to_pc(0x02D9A6), 0x005A) + rom.write_byte(snes_to_pc(0x02D9B3), 0x12) + # keep the old man spawn point at old man house unless shuffle is vanilla + if world.shuffle == 'vanilla': + rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) + rom.write_int16(snes_to_pc(0x02D8DE), 0x00F1) + rom.write_bytes(snes_to_pc(0x02D910), [0x1F, 0x1E, 0x1F, 0x1F, 0x03, 0x02, 0x03, 0x03]) + rom.write_int16(snes_to_pc(0x02D924), 0x0300) + rom.write_int16(snes_to_pc(0x02D932), 0x1F10) + rom.write_int16(snes_to_pc(0x02D940), 0x1FC0) + rom.write_int16(snes_to_pc(0x02D94E), 0x0378) + rom.write_int16(snes_to_pc(0x02D95C), 0x0187) + rom.write_int16(snes_to_pc(0x02D96A), 0x017F) + rom.write_byte(snes_to_pc(0x02D972), 0x06) + rom.write_byte(snes_to_pc(0x02D979), 0x00) + rom.write_byte(snes_to_pc(0x02D980), 0xFF) + rom.write_byte(snes_to_pc(0x02D987), 0x00) + rom.write_byte(snes_to_pc(0x02D98E), 0x22) + rom.write_byte(snes_to_pc(0x02D995), 0x12) + rom.write_int16(snes_to_pc(0x02D9A2), 0x0000) + rom.write_int16(snes_to_pc(0x02D9B0), 0x0007) + rom.write_byte(snes_to_pc(0x02D9B8), 0x12) + rom.write_bytes(0x180247, [0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00]) + rom.write_int16(0x15AEE + 2 * 0x06, 0x0020) # post aga hyrule castle spawn + rom.write_byte(0x15B8C + 0x06, 0x1B) + rom.write_int16(0x15BDB + 2 * 0x06, 0x00AE) + rom.write_int16(0x15C79 + 2 * 0x06, 0x0610) + rom.write_int16(0x15D17 + 2 * 0x06, 0x077E) + rom.write_int16(0x15DB5 + 2 * 0x06, 0x0672) + rom.write_int16(0x15E53 + 2 * 0x06, 0x07F8) + rom.write_int16(0x15EF1 + 2 * 0x06, 0x067D) + rom.write_int16(0x15F8F + 2 * 0x06, 0x0803) + rom.write_byte(0x1602D + 0x06, 0x00) + rom.write_byte(0x1607C + 0x06, 0xF2) + rom.write_int16(0x160CB + 2 * 0x06, 0x0000) + rom.write_int16(0x16169 + 2 * 0x06, 0x0000) + rom.write_int16(snes_to_pc(0x02E87B), 0x00AE) # move flute splot 9 + rom.write_int16(snes_to_pc(0x02E89D), 0x0610) + rom.write_int16(snes_to_pc(0x02E8BF), 0x077E) + rom.write_int16(snes_to_pc(0x02E8E1), 0x0672) + rom.write_int16(snes_to_pc(0x02E903), 0x07F8) + rom.write_int16(snes_to_pc(0x02E925), 0x067D) + rom.write_int16(snes_to_pc(0x02E947), 0x0803) + rom.write_int16(snes_to_pc(0x02E969), 0x0000) + rom.write_int16(snes_to_pc(0x02E98B), 0xFFF2) + rom.write_byte(snes_to_pc(0x1AF696), 0xF0) # bat sprite retreat + rom.write_byte(snes_to_pc(0x1AF6B2), 0x33) + rom.write_bytes(snes_to_pc(0x1AF730), [0x6A, 0x9E, 0x0C, 0x00, 0x7A, 0x9E, 0x0C, + 0x00, 0x8A, 0x9E, 0x0C, 0x00, 0x6A, 0xAE, + 0x0C, 0x00, 0x7A, 0xAE, 0x0C, 0x00, 0x8A, + 0xAE, 0x0C, 0x00, 0x67, 0x97, 0x0C, 0x00, + 0x8D, 0x97, 0x0C, 0x00]) + rom.write_int16s(snes_to_pc(0x0FF1C8), [0x190F, 0x190F, 0x190F, 0x194C, 0x190F, + 0x194B, 0x190F, 0x195C, 0x594B, 0x194C, + 0x19EE, 0x19EE, 0x194B, 0x19EE, 0x19EE, + 0x19EE, 0x594B, 0x190F, 0x595C, 0x190F, + 0x190F, 0x195B, 0x190F, 0x190F, 0x19EE, + 0x19EE, 0x195C, 0x19EE, 0x19EE, 0x19EE, + 0x19EE, 0x595C, 0x595B, 0x190F, 0x190F, + 0x190F]) + rom.write_int16s(snes_to_pc(0x0FA480), [0x190F, 0x196B, 0x9D04, 0x9D04, 0x196B, + 0x190F, 0x9D04, 0x9D04]) + rom.write_int16s(snes_to_pc(0x1bb810), [0x00BE, 0x00C0, 0x013E]) + rom.write_int16s(snes_to_pc(0x1bb836), [0x001B, 0x001B, 0x001B]) + rom.write_int16(snes_to_pc(0x308300), 0x0140) + rom.write_int16(snes_to_pc(0x308320), 0x001B) + if world.shuffle == 'vanilla': + rom.write_byte(snes_to_pc(0x308340), 0x7B) + rom.write_int16(snes_to_pc(0x1af504), 0x148B) + rom.write_int16(snes_to_pc(0x1af50c), 0x149B) + rom.write_int16(snes_to_pc(0x1af514), 0x14A4) + rom.write_int16(snes_to_pc(0x1af51c), 0x1489) + rom.write_int16(snes_to_pc(0x1af524), 0x14AC) + rom.write_int16(snes_to_pc(0x1af52c), 0x54AC) + rom.write_int16(snes_to_pc(0x1af534), 0x148C) + rom.write_int16(snes_to_pc(0x1af53c), 0x548C) + rom.write_int16(snes_to_pc(0x1af544), 0x1484) + rom.write_int16(snes_to_pc(0x1af54c), 0x5484) + rom.write_int16(snes_to_pc(0x1af554), 0x14A2) + rom.write_int16(snes_to_pc(0x1af55c), 0x54A2) + rom.write_int16(snes_to_pc(0x1af564), 0x14A0) + rom.write_int16(snes_to_pc(0x1af56c), 0x54A0) + rom.write_int16(snes_to_pc(0x1af574), 0x148E) + rom.write_int16(snes_to_pc(0x1af57c), 0x548E) + rom.write_int16(snes_to_pc(0x1af584), 0x14AE) + rom.write_int16(snes_to_pc(0x1af58c), 0x54AE) + rom.write_byte(snes_to_pc(0x00DB9D), 0x1A) # castle hole graphics + rom.write_byte(snes_to_pc(0x00DC09), 0x1A) + rom.write_byte(snes_to_pc(0x00D009), 0x31) + rom.write_byte(snes_to_pc(0x00D0e8), 0xE0) + rom.write_byte(snes_to_pc(0x00D1c7), 0x00) + rom.write_int16(snes_to_pc(0x1BE8DA), 0x39AD) + rom.write_byte(0xF6E58, 0x80) # no whirlpool under castle gate + rom.write_bytes(0x0086E, [0x5C, 0x00, 0xA0, 0xA1]) # TR tail + rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warps under rocks + rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) + rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) + rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) + rom.write_bytes(snes_to_pc(0x1BC3DF), [0xD8, 0xD1]) + rom.write_bytes(snes_to_pc(0x1BD1D8), [0xA8, 0x02, 0x82, 0xFF, 0xFF]) + rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) + rom.write_int16(0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door + rom.write_int16(0xDBA71 + 2 * 0x35, 0x06A4) + if world.shuffle == 'vanilla': + rom.write_byte(0xDBB73 + 0x35, 0x36) + rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp + if world.shuffle == 'vanilla': + rom.write_int16(0x15AEE + 2 * 0x37, 0x0010) # pyramid exit to new hc area + rom.write_byte(0x15B8C + 0x37, 0x1B) + rom.write_int16(0x15BDB + 2 * 0x37, 0x0418) + rom.write_int16(0x15C79 + 2 * 0x37, 0x0679) + rom.write_int16(0x15D17 + 2 * 0x37, 0x06B4) + rom.write_int16(0x15DB5 + 2 * 0x37, 0x06C6) + rom.write_int16(0x15E53 + 2 * 0x37, 0x0738) + rom.write_int16(0x15EF1 + 2 * 0x37, 0x06E6) + rom.write_int16(0x15F8F + 2 * 0x37, 0x0733) + rom.write_byte(0x1602D + 0x37, 0x07) + rom.write_byte(0x1607C + 0x37, 0xF9) + rom.write_int16(0x160CB + 2 * 0x37, 0x0000) + rom.write_int16(0x16169 + 2 * 0x37, 0x0000) + rom.write_bytes(snes_to_pc(0x1BC387), [0xDD, 0xD1]) + rom.write_bytes(snes_to_pc(0x1BD1DD), [0xA4, 0x06, 0x82, 0x9E, 0x06, 0x82, 0xFF, 0xFF]) + rom.write_byte(0x180089, 0x01) # open TR after exit + rom.write_byte(snes_to_pc(0x0ABFBB), 0x90) + rom.write_byte(snes_to_pc(0x0280A6), 0xD0) + rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) + +def patch_shuffled_dark_sanc(world, rom, player): + dark_sanc_entrance = str(world.get_region('Inverted Dark Sanctuary', player).entrances[0].name) + 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 = door_addresses[dark_sanc_entrance][1] + if dark_sanc_entrance == 'Skull Woods Final Section': + link_y = 0x00F8 + door_index = door_addresses[str(dark_sanc_entrance)][0] + + rom.write_byte(0x180241, 0x01) + rom.write_byte(0x180248, door_index + 1) + rom.write_int16(0x180250, room_id) + rom.write_byte(0x180252, ow_area) + rom.write_int16s(0x180253, [vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x]) + rom.write_bytes(0x180262, [unknown_1, unknown_2, 0x00]) + InconvenientEntrances = {'Turtle Rock': 'Turtle Rock Main', 'Misery Mire': 'Misery Mire', 'Ice Palace': 'Ice Palace', diff --git a/Rules.py b/Rules.py index bf6f0a50f9..17b6f4e99a 100644 --- a/Rules.py +++ b/Rules.py @@ -6,13 +6,21 @@ def set_rules(world, player): if world.logic == 'nologic': logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!') - world.get_region('Links House', player).can_reach = lambda state: True - world.get_region('Sanctuary', player).can_reach = lambda state: True - old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) - return - - global_rules(world, player) + if world.mode != 'inverted': + world.get_region('Links House', player).can_reach = lambda state: True + world.get_region('Sanctuary', player).can_reach = lambda state: True + old_rule = world.get_region('Old Man House', player).can_reach + world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + return + else: + world.get_region('Inverted Links House', player).can_reach = lambda state: True + world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach = lambda state: True + if world.shuffle != 'vanilla': + old_rule = world.get_region('Old Man House', player).can_reach + world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + return + if world.mode != 'inverted': + global_rules(world, player) if world.mode == 'open': open_rules(world, player) @@ -20,6 +28,9 @@ def set_rules(world, player): standard_rules(world, player) elif world.mode == 'swordless': swordless_rules(world, player) + elif world.mode == 'inverted': + open_rules(world, player) + inverted_rules(world, player) else: raise NotImplementedError('Not implemented yet') @@ -36,15 +47,20 @@ def set_rules(world, player): elif world.goal == 'ganon': # require aga2 to beat ganon add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) - - set_big_bomb_rules(world, player) + + if world.mode != 'inverted': + set_big_bomb_rules(world, player) + else: + set_inverted_big_bomb_rules(world, player) # if swamp and dam have not been moved we require mirror for swamp palace if not world.swamp_patch_required[player]: add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has_Mirror(player)) - set_bunny_rules(world, player) - + if world.mode != 'inverted': + set_bunny_rules(world, player) + else: + set_inverted_bunny_rules(world, player) def set_rule(spot, rule): spot.access_rule = rule @@ -301,7 +317,7 @@ def global_rules(world, player): for location in ['Skull Woods - Boss']: forbid_item(world.get_location(location, player), 'Small Key (Skull Woods)', player) - set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or (state.has('Bombos', player) and state.has_sword(player))) + set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_melt_things(player)) set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player)) set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state.has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state.has_key('Small Key (Ice Palace)', player, 1)))) # TODO: investigate change from VT. Changed to hookshot or 2 keys (no checking for big key in specific chests) @@ -422,14 +438,388 @@ def global_rules(world, player): set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has('Crystal 1', player) and state.has('Crystal 2', player) and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)) +def inverted_rules(world, player): + world.get_location('Ganon', player).item_rule = lambda item: item.name == 'Triforce' and item.player == player + world.get_region('Inverted Links House', player).can_reach = lambda state: True + world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach = lambda state: True + + # we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled! + if world.shuffle != 'vanilla': + old_rule = world.get_region('Old Man House', player).can_reach + world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + # overworld requirements + set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Potion Shop Pier', player), lambda state: state.has('Flippers', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Light World Pier', player), lambda state: state.has('Flippers', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has_Boots(player) and state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Potion Shop Inner Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Potion Shop Outer Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Potion Shop Outer Rock', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Potion Shop Inner Rock', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Graveyard Cave Inner Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Graveyard Cave Outer Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Secret Passage Inner Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Secret Passage Outer Bushes', player), lambda state: state.has_Pearl(player)) + # Caution: If king's grave is releaxed at all to account for reaching it via a two way cave's exit in insanity mode, then the bomb shop logic will need to be updated (that would involve create a small ledge-like Region for it) + set_rule(world.get_entrance('Bonk Fairy (Light)', player), lambda state: state.has_Boots(player) and state.has_Pearl(player)) + add_rule(world.get_location('Sunken Treasure', player), lambda state: state.can_reach('Light World', 'Region', player) and state.has('Open Floodgate', player)) + set_rule(world.get_entrance('Bat Cave Drop Ledge', player), lambda state: state.has('Hammer', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Lumberjack Tree Tree', player), lambda state: state.has_Boots(player) and state.has_Pearl(player) and state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('Bonk Rock Cave', player), lambda state: state.has_Boots(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Desert Palace Stairs', player), lambda state: state.has('Book of Mudora', player)) # bunny can use book + set_rule(world.get_entrance('Sanctuary Grave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('20 Rupee Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Dark Lake Hylia Central Island Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.can_flute(player) and state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer + set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer + set_rule(world.get_entrance('West Dark World Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) + set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player) and state.has_Pearl(player)) + set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) + set_rule(world.get_location('Purple Chest', player), lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest + + set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Waterfall of Wishing', player), lambda state: state.has('Flippers', player) and state.has_Pearl(player)) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. Can be improved in future Todo + set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) or (state.can_reach('Light World', 'Region', player) and state.has_Mirror(player))) + set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith + set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) + set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player) and state.has_Pearl(player)) + set_rule(world.get_location('Sick Kid', player), lambda state: state.has_bottle(player)) + set_rule(world.get_location('Mushroom', player), lambda state: state.has_Pearl(player)) # need pearl to pick up bushes + set_rule(world.get_entrance('Bush Covered Lawn Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Bush Covered Lawn Inner Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Bush Covered Lawn Outer Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Bomb Hut Inner Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Bomb Hut Outer Bushes', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('North Fairy Cave Drop', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Lost Woods Hideout Drop', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_location('Library', player), lambda state: state.has_Boots(player)) + set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and (state.can_reach('Potion Shop Area', 'Region', player))) # new inverted region, need pearl for bushes or access to potion shop door/waterfall fairy + set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Desert Ledge Return Rocks', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) # should we decide to place something that is not a dungeon end up there at some point + set_rule(world.get_entrance('Checkerboard Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_location('Master Sword Pedestal', player), lambda state: state.has('Red Pendant', player) and state.has('Blue Pendant', player) and state.has('Green Pendant', player)) + set_rule(world.get_location('Sahasrahla', player), lambda state: state.has('Green Pendant', player)) + set_rule(world.get_entrance('Agahnim 1', player), lambda state: state.has_sword(player) and state.has_key('Small Key (Agahnims Tower)', player, 2)) + set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player)) + set_rule(world.get_location('Castle Tower - Dark Maze', player), lambda state: state.has_key('Small Key (Agahnims Tower)', player)) + set_rule(world.get_entrance('LW Hyrule Castle Ledge SQ', player), lambda state: state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('EDM Hyrule Castle Ledge SQ', player), lambda state: state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('WDM Hyrule Castle Ledge SQ', player), lambda state: state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('Hyrule Castle Secret Entrance Drop', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up + set_rule(world.get_entrance('Broken Bridge (West)', player), lambda state: state.has('Hookshot', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Broken Bridge (East)', player), lambda state: state.has('Hookshot', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Dark Death Mountain Teleporter (East Bottom)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Mirror', player)) # can erase block + set_rule(world.get_entrance('Death Mountain (Top)', player), lambda state: state.has('Hammer', player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Dark Death Mountain Teleporter (East)', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player) and state.has_Pearl(player)) # bunny cannot use hammer + set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) + set_rule(world.get_entrance('East Death Mountain (Top)', player), lambda state: state.has('Hammer', player) and state.has_Pearl(player)) # bunny can not use hammer + + set_rule(world.get_location('Catfish', player), lambda state: state.can_lift_rocks(player) or (state.has('Flippers', player) and state.has_Mirror(player) and state.has_Pearl(player) and state.can_reach('Light World', 'Region', player))) + set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass', player), lambda state: ((state.can_lift_rocks(player) or state.has('Hammer', player)) or state.has('Flippers', player))) + set_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: (state.can_lift_rocks(player) or state.has('Hammer', player))) + set_rule(world.get_entrance('South Dark World Bridge', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Bonk Fairy (Dark)', player), lambda state: state.has_Boots(player)) + set_rule(world.get_entrance('West Dark World Gap', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) + set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has('Flippers', player)) # ToDo any fake flipper set up? + set_rule(world.get_entrance('Dark Lake Hylia Ledge Pier', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) # Fake Flippers + set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('East Dark World Bridge', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has('Flippers', player)) # ToDo any fake flipper set up? (Qirn Jump) + set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Hammer Peg Area Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Dark World Hammer Peg Cave', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Village of Outcasts Eastern Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Peg Area Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Village of Outcasts Pegs', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Grassy Lawn Pegs', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player)) + set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player)) + + set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player)) + set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!) + + set_rule(world.get_entrance('Hookshot Cave', player), lambda state: state.can_lift_rocks(player)) + + set_rule(world.get_entrance('East Death Mountain Mirror Spot (Top)', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Death Mountain (Top) Mirror Spot', player), lambda state: state.has_Mirror(player)) + + set_rule(world.get_entrance('East Death Mountain Mirror Spot (Bottom)', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Dark Death Mountain Ledge Mirror Spot (East)', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Dark Death Mountain Ledge Mirror Spot (West)', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Laser Bridge Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling + + set_rule(world.get_location('Spike Cave', player), lambda state: + state.has('Hammer', player) and state.can_lift_rocks(player) and + ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or + (state.has('Cane of Byrna', player) and + (state.can_extend_magic(player, 12, True) or + (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4)))))) + ) + + set_rule(world.get_location('Hookshot Cave - Top Right', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_location('Hookshot Cave - Top Left', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_location('Hookshot Cave - Bottom Right', player), lambda state: state.has('Hookshot', player) or state.has('Pegasus Boots', player)) + set_rule(world.get_location('Hookshot Cave - Bottom Left', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('Floating Island Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) + set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player)) + + # new inverted spots + set_rule(world.get_entrance('Post Aga Teleporter', player), lambda state: state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('Mire Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Desert Palace Stairs Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Death Mountain Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('East Dark World Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('West Dark World Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('South Dark World Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Northeast Dark World Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Potion Shop Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Shopping Mall Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Maze Race Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Desert Palace North Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Death Mountain (Top) Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Graveyard Cave Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Bomb Hut Mirror Spot', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Skull Woods Mirror Spot', player), lambda state: state.has_Mirror(player)) + + # inverted flute spots + + set_rule(world.get_entrance('DDM Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('NEDW Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('WDW Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('SDW Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('EDW Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('DLHL Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('DD Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('EDDM Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('Dark Grassy Lawn Flute', player), lambda state: state.can_flute(player)) + set_rule(world.get_entrance('Hammer Peg Area Flute', player), lambda state: state.can_flute(player)) + + set_rule(world.get_entrance('Sewers Door', player), lambda state: state.has_key('Small Key (Escape)', player)) + set_rule(world.get_entrance('Sewers Back Door', player), lambda state: state.has_key('Small Key (Escape)', player)) + + set_rule(world.get_location('Eastern Palace - Big Chest', player), lambda state: state.has('Big Key (Eastern Palace)', player)) + set_rule(world.get_location('Eastern Palace - Boss', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Eastern Palace - Prize', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) + for location in ['Eastern Palace - Boss', 'Eastern Palace - Big Chest']: + forbid_item(world.get_location(location, player), 'Big Key (Eastern Palace)', player) + + set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player)) + set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has_Boots(player)) + set_rule(world.get_entrance('Desert Palace East Wing', player), lambda state: state.has_key('Small Key (Desert Palace)', player)) + set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and world.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state.has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and world.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) + for location in ['Desert Palace - Boss', 'Desert Palace - Big Chest']: + forbid_item(world.get_location(location, player), 'Big Key (Desert Palace)', player) + + for location in ['Desert Palace - Boss', 'Desert Palace - Big Key Chest', 'Desert Palace - Compass Chest']: + forbid_item(world.get_location(location, player), 'Small Key (Desert Palace)', player) + + set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state.has_key('Small Key (Tower of Hera)', player) or item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player)) + set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) + set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player)) + set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player)) + set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) + set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player)) + for location in ['Tower of Hera - Boss', 'Tower of Hera - Big Chest', 'Tower of Hera - Compass Chest']: + forbid_item(world.get_location(location, player), 'Big Key (Tower of Hera)', player) +# for location in ['Tower of Hera - Big Key Chest']: +# forbid_item(world.get_location(location, player), 'Small Key (Tower of Hera)', player) + + set_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player)) + add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) + + set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state.has_key('Small Key (Swamp Palace)', player)) + set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player) or item_name(state, 'Swamp Palace - Big Chest', player) == ('Big Key (Swamp Palace)', player)) + set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) + set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player)) + set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player)) + for location in ['Swamp Palace - Entrance']: + forbid_item(world.get_location(location, player), 'Big Key (Swamp Palace)', player) + + set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player)) + set_rule(world.get_entrance('Blind Fight', player), lambda state: state.has_key('Small Key (Thieves Town)', player)) + set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Prize', player)) + set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state.has_key('Small Key (Thieves Town)', player) or item_name(state, 'Thieves\' Town - Big Chest', player) == ('Small Key (Thieves Town)', player)) and state.has('Hammer', player)) + set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) + set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state.has_key('Small Key (Thieves Town)', player)) + for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves\' Town - Boss']: + forbid_item(world.get_location(location, player), 'Big Key (Thieves Town)', player) + for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Boss']: + forbid_item(world.get_location(location, player), 'Small Key (Thieves Town)', player) + + set_rule(world.get_entrance('Skull Woods First Section South Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player)) + set_rule(world.get_entrance('Skull Woods First Section (Right) North Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player)) + set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section + set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) + set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) or item_name(state, 'Skull Woods - Big Chest', player) == ('Big Key (Skull Woods)', player)) + set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) + set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and state.has_sword(player)) # sword required for curtain + set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player)) + for location in ['Skull Woods - Boss']: + forbid_item(world.get_location(location, player), 'Small Key (Skull Woods)', player) + + set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_melt_things(player)) + set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player)) + set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state.has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state.has_key('Small Key (Ice Palace)', player, 1)))) + # TODO: investigate change from VT. Changed to hookshot or 2 keys (no checking for big key in specific chests) + set_rule(world.get_entrance('Ice Palace (East)', player), lambda state: (state.has('Hookshot', player) or (item_in_locations(state, 'Big Key (Ice Palace)', player, [('Ice Palace - Spike Room', player), ('Ice Palace - Big Key Chest', player), ('Ice Palace - Map Chest', player)]) and state.has_key('Small Key (Ice Palace)', player))) and (state.world.can_take_damage or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player))) + set_rule(world.get_entrance('Ice Palace (East Top)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) + set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Prize', player)) + for location in ['Ice Palace - Big Chest', 'Ice Palace - Boss']: + forbid_item(world.get_location(location, player), 'Big Key (Ice Palace)', player) + + set_rule(world.get_entrance('Misery Mire Entrance Gap', player), lambda state: (state.has_Boots(player) or state.has('Hookshot', player)) and (state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player))) # need to defeat wizzrobes, bombs don't work ... + set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player)) + set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) + set_rule(world.get_entrance('Misery Mire Big Key Door', player), lambda state: state.has('Big Key (Misery Mire)', player)) + # you can squander the free small key from the pot by opening the south door to the north west switch room, locking you out of accessing a color switch ... + # big key gives backdoor access to that from the teleporter in the north west + set_rule(world.get_location('Misery Mire - Map Chest', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 1) or state.has('Big Key (Misery Mire)', player)) + # in addition, you can open the door to the map room before getting access to a color switch, so this is locked behing 2 small keys or the big key... + set_rule(world.get_location('Misery Mire - Main Lobby', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 2) or state.has_key('Big Key (Misery Mire)', player)) + # we can place a small key in the West wing iff it also contains/blocks the Big Key, as we cannot reach and softlock with the basement key door yet + set_rule(world.get_entrance('Misery Mire (West)', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 2) if ((item_name(state, 'Misery Mire - Compass Chest', player) in [('Big Key (Misery Mire)', player)]) or + (item_name(state, 'Misery Mire - Big Key Chest', player) in [('Big Key (Misery Mire)', player)])) else state.has_key('Small Key (Misery Mire)', player, 3)) + set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: state.has_fire_source(player)) + set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: state.has_fire_source(player)) + set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player)) + set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player)) + for location in ['Misery Mire - Big Chest', 'Misery Mire - Boss']: + forbid_item(world.get_location(location, player), 'Big Key (Misery Mire)', player) + + set_rule(world.get_entrance('Turtle Rock Entrance Gap', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('Turtle Rock Entrance Gap Reverse', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_location('Turtle Rock - Compass Chest', player), lambda state: state.has('Cane of Somaria', player)) # We could get here from the middle section without Cane as we don't cross the entrance gap! + set_rule(world.get_location('Turtle Rock - Roller Room - Left', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player)) + set_rule(world.get_location('Turtle Rock - Roller Room - Right', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player)) + set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player) and (state.has('Cane of Somaria', player) or state.has('Hookshot', player))) + set_rule(world.get_entrance('Turtle Rock (Big Chest) (North)', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player)) + set_rule(world.get_entrance('Turtle Rock Big Key Door', player), lambda state: state.has('Big Key (Turtle Rock)', player)) + set_rule(world.get_entrance('Turtle Rock (Dark Room) (North)', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('Turtle Rock (Dark Room) (South)', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_entrance('Turtle Rock (Trinexx)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 4) and state.has('Big Key (Turtle Rock)', player) and state.has('Cane of Somaria', player)) + set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player)) + + set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: state.can_shoot_arrows(player)) + set_rule(world.get_entrance('Palace of Darkness Hammer Peg Drop', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Palace of Darkness Bridge Room', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 1)) # If we can reach any other small key door, we already have back door access to this area + set_rule(world.get_entrance('Palace of Darkness Big Key Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) and state.has('Big Key (Palace of Darkness)', player) and state.can_shoot_arrows(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Palace of Darkness (North)', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 4)) + set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player)) + + set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 3))) + set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + + set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Harmless Hellway', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 4))) + set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + set_rule(world.get_entrance('Palace of Darkness Maze Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6)) + set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Boss', player)) + set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize', player)) + + # these key rules are conservative, you might be able to get away with more lenient rules + randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'] + compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'] + + set_rule(world.get_location('Ganons Tower - Bob\'s Torch', player), lambda state: state.has_Boots(player)) + set_rule(world.get_entrance('Ganons Tower (Tile Room)', player), lambda state: state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player)) + + set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player), ('Small Key (Ganons Tower)', player)] and state.has_key('Small Key (Ganons Tower)', player, 3))) + set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state.has_key('Small Key (Ganons Tower)', player, 3)) + + # It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere. We reflect this in the chest requirements. + # However we need to leave these at the lower values to derive that with 3 keys it is always possible to reach Bob and Ice Armos. + set_rule(world.get_entrance('Ganons Tower (Double Switch Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 2)) + # It is possible to need more than 3 keys .... + set_rule(world.get_entrance('Ganons Tower (Firesnake Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3)) + + #The actual requirements for these rooms to avoid key-lock + set_rule(world.get_location('Ganons Tower - Firesnake Room', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(randomizer_room_chests, [player] * len(randomizer_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 2))) + for location in randomizer_room_chests: + set_rule(world.get_location(location, player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(randomizer_room_chests, [player] * len(randomizer_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 3))) + + # Once again it is possible to need more than 3 keys... + set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3) and state.has('Fire Rod', player)) + # Actual requirements + for location in compass_room_chests: + set_rule(world.get_location(location, player), lambda state: state.has('Fire Rod', player) and (state.has_key('Small Key (Ganons Tower)', player, 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(compass_room_chests, [player] * len(compass_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 3)))) + + set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player)) + + set_rule(world.get_location('Ganons Tower - Big Key Room - Left', player), lambda state: world.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Big Key Chest', player), lambda state: world.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Big Key Room - Right', player), lambda state: world.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state)) + + set_rule(world.get_entrance('Ganons Tower Big Key Door', player), lambda state: state.has('Big Key (Ganons Tower)', player) and state.can_shoot_arrows(player)) + set_rule(world.get_entrance('Ganons Tower Torch Rooms', player), lambda state: state.has_fire_source(player) and world.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state)) + set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3)) + set_rule(world.get_entrance('Ganons Tower Moldorm Door', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4)) + set_rule(world.get_entrance('Ganons Tower Moldorm Gap', player), lambda state: state.has('Hookshot', player) and world.get_entrance('Ganons Tower Moldorm Gap', player).parent_region.dungeon.bosses['top'].can_defeat(state)) + set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player)) + set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player)) + for location in ['Ganons Tower - Big Chest', 'Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right', + 'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Validation Chest']: + forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player) + + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has('Crystal 1', player) and state.has('Crystal 2', player) + and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player) + and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times + set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop + + set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic. + + set_trock_key_rules(world, player) + + set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has('Crystal 1', player) and state.has('Crystal 2', player) and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)) def no_glitches_rules(world, player): - set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player)) - set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) - set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) - set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) + if world.mode != 'inverted': + set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player)) + set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Flippers', player)) # can be fake flippered to + set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) + set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) + else: + set_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player) and (state.has('Flippers', player) or state.can_lift_rocks(player))) + set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to + set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) + set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has('Flippers', player)) + add_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player)) add_rule(world.get_entrance('Ganons Tower (Double Switch Room)', player), lambda state: state.has('Hookshot', player)) DMs_room_chests = ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right', 'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'] @@ -465,8 +855,12 @@ def no_glitches_rules(world, player): add_conditional_lamp('Palace of Darkness Maze Door', 'Palace of Darkness (Entrance)', 'Entrance') add_conditional_lamp('Palace of Darkness - Dark Basement - Left', 'Palace of Darkness (Entrance)', 'Location') add_conditional_lamp('Palace of Darkness - Dark Basement - Right', 'Palace of Darkness (Entrance)', 'Location') - add_conditional_lamp('Agahnim 1', 'Agahnims Tower', 'Entrance') - add_conditional_lamp('Castle Tower - Dark Maze', 'Agahnims Tower', 'Location') + if world.mode != 'inverted': + add_conditional_lamp('Agahnim 1', 'Agahnims Tower', 'Entrance') + add_conditional_lamp('Castle Tower - Dark Maze', 'Agahnims Tower', 'Location') + else: + add_conditional_lamp('Agahnim 1', 'Inverted Agahnims Tower', 'Entrance') + add_conditional_lamp('Castle Tower - Dark Maze', 'Inverted Agahnims Tower', 'Location') add_conditional_lamp('Old Man', 'Old Man Cave', 'Location') add_conditional_lamp('Old Man Cave Exit (East)', 'Old Man Cave', 'Entrance') add_conditional_lamp('Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave', 'Entrance') @@ -528,12 +922,13 @@ def standard_rules(world, player): def set_trock_key_rules(world, player): - all_state = world.get_all_state(True) # First set all relevant locked doors to impassible. for entrance in ['Turtle Rock Dark Room Staircase', 'Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)', 'Turtle Rock Pokey Room']: set_rule(world.get_entrance(entrance, player), lambda state: False) + all_state = world.get_all_state(True) + # Check if each of the four main regions of the dungoen can be reached. The previous code section prevents key-costing moves within the dungeon. can_reach_back = all_state.can_reach(world.get_region('Turtle Rock (Eye Bridge)', player)) if world.can_access_trock_eyebridge is None else world.can_access_trock_eyebridge world.can_access_trock_eyebridge = can_reach_back @@ -840,6 +1235,176 @@ def set_big_bomb_rules(world, player): # -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and state.has_Mirror(player)) or ((state.can_lift_heavy_rocks(player) or state.has('Ocarina', player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state))) +def set_inverted_big_bomb_rules(world, player): + bombshop_entrance = world.get_region('Inverted Big Bomb Shop', player).entrances[0] + Normal_LW_entrances = ['Blinds Hideout', + 'Bonk Fairy (Light)', + 'Lake Hylia Fairy', + 'Light Hype Fairy', + 'Desert Fairy', + 'Chicken House', + 'Aginahs Cave', + 'Sahasrahlas Hut', + 'Cave Shop (Lake Hylia)', + 'Blacksmiths Hut', + 'Sick Kids House', + 'Lost Woods Gamble', + 'Fortune Teller (Light)', + 'Snitch Lady (East)', + 'Snitch Lady (West)', + 'Bush Covered House', + 'Tavern (Front)', + 'Light World Bomb Hut', + 'Kakariko Shop', + 'Mini Moldorm Cave', + 'Long Fairy Cave', + 'Good Bee Cave', + '20 Rupee Cave', + '50 Rupee Cave', + 'Ice Rod Cave', + 'Bonk Rock Cave', + 'Library', + 'Potion Shop', + 'Waterfall of Wishing', + 'Dam', + 'Lumberjack House', + 'Lake Hylia Fortune Teller', + 'Eastern Palace', + 'Kakariko Gamble Game', + 'Kakariko Well Cave', + 'Bat Cave Cave', + 'Elder House (East)', + 'Elder House (West)', + 'North Fairy Cave', + 'Lost Woods Hideout Stump', + 'Lumberjack Tree Cave', + 'Two Brothers House (East)', + 'Sanctuary', + 'Hyrule Castle Entrance (South)', + 'Hyrule Castle Secret Entrance Stairs'] + LW_walkable_entrances = ['Dark Lake Hylia Ledge Fairy', + 'Dark Lake Hylia Ledge Spike Cave', + 'Dark Lake Hylia Ledge Hint', + 'Mire Shed', + 'Dark Desert Hint', + 'Dark Desert Fairy', + 'Misery Mire'] + Northern_DW_entrances = ['Brewery', + 'C-Shaped House', + 'Chest Game', + 'Dark World Hammer Peg Cave', + 'Red Shield Shop', + 'Dark Sanctuary Hint', + 'Fortune Teller (Dark)', + 'Dark World Shop', + 'Dark World Lumberjack Shop', + 'Thieves Town', + 'Skull Woods First Section Door', + 'Skull Woods Second Section Door (East)'] + Southern_DW_entrances = ['Hype Cave', + 'Bonk Fairy (Dark)', + 'Archery Game', + 'Inverted Big Bomb Shop', + 'Dark Lake Hylia Shop', + 'Swamp Palace'] + Isolated_DW_entrances = ['Spike Cave', + 'Cave Shop (Dark Death Mountain)', + 'Dark Death Mountain Fairy', + 'Mimic Cave', + 'Skull Woods Second Section Door (West)', + 'Skull Woods Final Section', + 'Ice Palace', + 'Turtle Rock', + 'Dark Death Mountain Ledge (West)', + 'Dark Death Mountain Ledge (East)', + 'Bumper Cave (Top)', + 'Superbunny Cave (Top)', + 'Superbunny Cave (Bottom)', + 'Hookshot Cave', + 'Turtle Rock Isolated Ledge Entrance', + 'Hookshot Cave Back Entrance', + 'Inverted Ganons Tower'] + Isolated_LW_entrances = ['Capacity Upgrade', + 'Tower of Hera', + 'Death Mountain Return Cave (West)', + 'Paradox Cave (Top)', + 'Fairy Ascension Cave (Top)', + 'Spiral Cave', + 'Desert Palace Entrance (East)'] + West_LW_DM_entrances = ['Old Man Cave (East)', + 'Old Man House (Bottom)', + 'Old Man House (Top)', + 'Death Mountain Return Cave (East)', + 'Spectacle Rock Cave Peak', + 'Spectacle Rock Cave', + 'Spectacle Rock Cave (Bottom)'] + East_LW_DM_entrances = ['Paradox Cave (Bottom)', + 'Paradox Cave (Middle)', + 'Hookshot Fairy', + 'Spiral Cave (Bottom)'] + Mirror_from_SDW_entrances = ['Two Brothers House (West)', + 'Cave 45'] + Castle_ledge_entrances = ['Hyrule Castle Entrance (West)', + 'Hyrule Castle Entrance (East)', + 'Inverted Ganons Tower'] + Desert_mirrorable_ledge_entrances = ['Desert Palace Entrance (West)', + 'Desert Palace Entrance (North)', + 'Desert Palace Entrance (South)', + 'Checkerboard Cave',] + Desert_ledge_entrances = ['Desert Palace Entrance (West)', + 'Desert Palace Entrance (North)', + 'Desert Palace Entrance (South)'] + + set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Inverted Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) + + #crossing peg bridge starting from the southern dark world + def cross_peg_bridge(state): + return state.has('Hammer', player) + + # Key for below abbreviations: + # P = pearl + # A = Aga1 + # H = hammer + # M = Mirror + # G = Glove + if bombshop_entrance.name in Normal_LW_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player)) + elif bombshop_entrance.name in LW_walkable_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player)) + elif bombshop_entrance.name in Northern_DW_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and (cross_peg_bridge(state) or (state.has_Mirror(player) and state.has('Beat Agahnim 1', player))))) + elif bombshop_entrance.name == 'Bumper Cave (Bottom)': + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and (cross_peg_bridge(state) or state.has_Mirror(player) and state.has('Beat Agahnim 1', player)))) + elif bombshop_entrance.name in Southern_DW_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or state.can_flute(player) or (state.has_Mirror(player) and state.has('Beat Agahnim 1', player))) + elif bombshop_entrance.name in Isolated_DW_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player)) + elif bombshop_entrance.name in Isolated_LW_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and state.has_Mirror(player)) + elif bombshop_entrance.name in West_LW_DM_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and state.has_Mirror(player)) + elif bombshop_entrance.name in East_LW_DM_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and state.has_Mirror(player)) + elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)': + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and state.has_Mirror(player)) + elif bombshop_entrance.name in Castle_ledge_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player)) + elif bombshop_entrance.name in Desert_ledge_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and state.can_flute(player)) + elif bombshop_entrance.name == 'Checkerboard Cave': + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player)) + elif bombshop_entrance.name == 'Old Man Cave (West)': + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and ((state.can_lift_rocks(player) and cross_peg_bridge(state)) or state.can_flute(player))) + elif bombshop_entrance.name == 'Graveyard Cave': + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player)) + elif bombshop_entrance.name in Mirror_from_SDW_entrances: + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and (state.can_flute(player) or cross_peg_bridge(state))) + elif bombshop_entrance.name == 'Dark World Potion Shop': + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) or state.has('Hammer', player) or state.can_lift_rocks(player)) + elif bombshop_entrance.name == 'Kings Grave': + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Mirror(player)) + + def set_bunny_rules(world, player): # regions for the exits of multi-entrace caves/drops that bunny cannot pass @@ -910,3 +1475,76 @@ def set_bunny_rules(world, player): continue add_rule(location, get_rule_to_add(location.parent_region)) + +def set_inverted_bunny_rules(world, player): + + # regions for the exits of multi-entrace caves/drops that bunny cannot pass + # Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing. + bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)', + 'Turtle Rock (Eye Bridge)', 'Sewers', 'Pyramid', 'Spiral Cave (Top)', 'Desert Palace Main (Inner)', 'Fairy Ascension Cave (Drop)', 'The Sky'] + + bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', 'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge', 'Dark Blacksmith Ruins', 'Bombos Tablet', 'Ether Tablet', 'Purple Chest'] + + + def path_to_access_rule(path, entrance): + return lambda state: state.can_reach(entrance) and all(rule(state) for rule in path) + + def options_to_access_rule(options): + return lambda state: any(rule(state) for rule in options) + + def get_rule_to_add(region): + if not region.is_dark_world: + return lambda state: state.has_Pearl(player) + # in this case we are mixed region. + # we collect possible options. + + # The base option is having the moon pearl + possible_options = [lambda state: state.has_Pearl(player)] + + # We will search entrances recursively until we find + # one that leads to an exclusively dark world region + # for each such entrance a new option is added that consist of: + # a) being able to reach it, and + # b) being able to access all entrances from there to `region` + seen = set([region]) + queue = collections.deque([(region, [])]) + while queue: + (current, path) = queue.popleft() + for entrance in current.entrances: + new_region = entrance.parent_region + if new_region in seen: + continue + new_path = path + [entrance.access_rule] + seen.add(new_region) + if not new_region.is_dark_world: + continue # we don't care about pure light world entrances + if new_region.is_light_world: + queue.append((new_region, new_path)) + else: + # we have reached pure dark world, so we have a new possible option + possible_options.append(path_to_access_rule(new_path, entrance)) + return options_to_access_rule(possible_options) + + # Add requirements for bunny-impassible caves if they occur in the light world + for region in [world.get_region(name, player) for name in bunny_impassable_caves]: + + if not region.is_light_world: + continue + rule = get_rule_to_add(region) + for exit in region.exits: + add_rule(exit, rule) + + paradox_shop = world.get_region('Light World Death Mountain Shop', player) + if paradox_shop.is_light_world: + add_rule(paradox_shop.entrances[0], get_rule_to_add(paradox_shop)) + + # Add requirements for all locations that are actually in the light world, except those available to the bunny + for location in world.get_locations(): + if location.player == player and location.parent_region.is_light_world: + + if location.name in bunny_accessible_locations: + continue + + add_rule(location, get_rule_to_add(location.parent_region)) + + diff --git a/Text.py b/Text.py index 22b2e04477..b8baba8316 100644 --- a/Text.py +++ b/Text.py @@ -25,7 +25,8 @@ Uncle_texts = [ 'Forward this message to 10 other people or this seed will be awful.', 'I hope you like your seeds bootless and fluteless.', '10\n9\n8\n7\n6\n5\n4\n3\n2\n1\nGo!', - 'I\'m off to visit cousin Fritzl.' + 'I\'m off to visit cousin Fritzl.', + 'Don\'t forget to check Antlion Cave.' ] * 2 + [ "We're out of\nWeetabix. To\nthe store!", "This seed is\nbootless\nuntil boots.", From 90e5f522d81f99b026c6c9b03362e8fdbac5bf47 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Thu, 25 Jul 2019 18:25:14 -0400 Subject: [PATCH 15/52] Partially implement variable crystal requirements --- BaseClasses.py | 6 ++ Rom.py | 225 +++++++++++++++++++++++++------------------------ Rules.py | 10 +-- 3 files changed, 124 insertions(+), 117 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 2466a3b763..09fdba1db4 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -71,6 +71,8 @@ class World(object): self.fix_fake_world = True self.boss_shuffle = boss_shuffle self.hints = hints + self.crystals_needed_for_ganon = 7 + self.crystals_needed_for_gt = 7 self.dynamic_regions = [] self.dynamic_locations = [] self.spoiler = Spoiler(self) @@ -367,6 +369,10 @@ class CollectionState(object): def item_count(self, item, player): return self.prog_items.count((item, player)) + def has_crystals(self, count, player): + crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'] + return len([crystal for crystal in crystals if self.has(crystal, player)]) >= count + def can_lift_rocks(self, player): return self.has('Power Glove', player) or self.has('Titans Mitts', player) diff --git a/Rom.py b/Rom.py index c425169c0c..066897fe22 100644 --- a/Rom.py +++ b/Rom.py @@ -929,6 +929,9 @@ def patch_rom(world, player, rom): else: rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected + rom.write_byte(0x18005E, world.crystals_needed_for_gt) + rom.write_byte(0x18005F, world.crystals_needed_for_ganon) + rom.write_byte(0x18016A, 0x01 if world.keysanity else 0x00) # free roaming item text boxes rom.write_byte(0x18003B, 0x01 if world.keysanity else 0x00) # maps showing crystals on overworld @@ -1739,114 +1742,114 @@ InsanityEntrances = {'Sanctuary': 'Sanctuary', 'Hookshot Cave Back Entrance': 'The stairs on the floating island' } -HintLocations = ['telepathic_tile_eastern_palace', - 'telepathic_tile_tower_of_hera_floor_4', - 'telepathic_tile_spectacle_rock', - 'telepathic_tile_swamp_entrance', - 'telepathic_tile_thieves_town_upstairs', - 'telepathic_tile_misery_mire', - 'telepathic_tile_palace_of_darkness', - 'telepathic_tile_desert_bonk_torch_room', - 'telepathic_tile_castle_tower', - 'telepathic_tile_ice_large_room', - 'telepathic_tile_turtle_rock', - 'telepathic_tile_ice_entrace', - 'telepathic_tile_ice_stalfos_knights_room', - 'telepathic_tile_tower_of_hera_entrance', - 'telepathic_tile_south_east_darkworld_cave', - 'dark_palace_tree_dude', - 'dark_sanctuary_hint_0', - 'dark_sanctuary_hint_1', - 'dark_sanctuary_yes', - 'dark_sanctuary_hint_2'] - -InconvenientLocations = ['Spike Cave', - 'Sahasrahla', - 'Purple Chest', - 'Swamp Left', - 'Mire Left', - 'Tower of Hera - Big Key Chest', - 'Eastern Palace - Big Key Chest', - 'Thieves\' Town - Big Chest', - 'Ice Palace - Big Chest', - 'Ganons Tower - Big Chest', - 'Magic Bat'] -RelevantItems = ['Bow', - 'Book of Mudora', - 'Hammer', - 'Hookshot', - 'Magic Mirror', - 'Ocarina', - 'Pegasus Boots', - 'Power Glove', - 'Cape', - 'Mushroom', - 'Shovel', - 'Lamp', - 'Magic Powder', - 'Moon Pearl', - 'Cane of Somaria', - 'Fire Rod', - 'Flippers', - 'Ice Rod', - 'Titans Mitts', - 'Ether', - 'Bombos', - 'Quake', - 'Bottle', - 'Bottle (Red Potion)', - 'Bottle (Green Potion)', - 'Bottle (Blue Potion)', - 'Bottle (Fairy)', - 'Bottle (Bee)', - 'Bottle (Good Bee)', - 'Master Sword', - 'Tempered Sword', - 'Fighter Sword', - 'Golden Sword', - 'Progressive Sword', - 'Progressive Glove', - 'Master Sword', - 'Power Star', - 'Triforce Piece', - 'Single Arrow', - 'Blue Mail', - 'Red Mail', - 'Progressive Armor', - 'Blue Boomerang', - 'Red Boomerang', - 'Blue Shield', - 'Red Shield', - 'Mirror Shield', - 'Progressive Shield', - 'Bug Catching Net', - 'Cane of Byrna', - 'Magic Upgrade (1/2)', - 'Magic Upgrade (1/4)' - ] - -KeysanityItems = ['Small Key (Eastern Palace)', - 'Big Key (Eastern Palace)', - 'Small Key (Escape)', - 'Small Key (Desert Palace)', - 'Big Key (Desert Palace)', - 'Small Key (Tower of Hera)', - 'Big Key (Tower of Hera)', - 'Small Key (Agahnims Tower)', - 'Small Key (Palace of Darkness)', - 'Big Key (Palace of Darkness)', - 'Small Key (Thieves Town)', - 'Big Key (Thieves Town)', - 'Small Key (Swamp Palace)', - 'Big Key (Swamp Palace)', - 'Small Key (Skull Woods)', - 'Big Key (Skull Woods)', - 'Small Key (Ice Palace)', - 'Big Key (Ice Palace)', - 'Small Key (Misery Mire)', - 'Big Key (Misery Mire)', - 'Small Key (Turtle Rock)', - 'Big Key (Turtle Rock)', - 'Small Key (Ganons Tower)', - 'Big Key (Ganons Tower)' - ] +HintLocations = ['telepathic_tile_eastern_palace', + 'telepathic_tile_tower_of_hera_floor_4', + 'telepathic_tile_spectacle_rock', + 'telepathic_tile_swamp_entrance', + 'telepathic_tile_thieves_town_upstairs', + 'telepathic_tile_misery_mire', + 'telepathic_tile_palace_of_darkness', + 'telepathic_tile_desert_bonk_torch_room', + 'telepathic_tile_castle_tower', + 'telepathic_tile_ice_large_room', + 'telepathic_tile_turtle_rock', + 'telepathic_tile_ice_entrace', + 'telepathic_tile_ice_stalfos_knights_room', + 'telepathic_tile_tower_of_hera_entrance', + 'telepathic_tile_south_east_darkworld_cave', + 'dark_palace_tree_dude', + 'dark_sanctuary_hint_0', + 'dark_sanctuary_hint_1', + 'dark_sanctuary_yes', + 'dark_sanctuary_hint_2'] + +InconvenientLocations = ['Spike Cave', + 'Sahasrahla', + 'Purple Chest', + 'Swamp Left', + 'Mire Left', + 'Tower of Hera - Big Key Chest', + 'Eastern Palace - Big Key Chest', + 'Thieves\' Town - Big Chest', + 'Ice Palace - Big Chest', + 'Ganons Tower - Big Chest', + 'Magic Bat'] +RelevantItems = ['Bow', + 'Book of Mudora', + 'Hammer', + 'Hookshot', + 'Magic Mirror', + 'Ocarina', + 'Pegasus Boots', + 'Power Glove', + 'Cape', + 'Mushroom', + 'Shovel', + 'Lamp', + 'Magic Powder', + 'Moon Pearl', + 'Cane of Somaria', + 'Fire Rod', + 'Flippers', + 'Ice Rod', + 'Titans Mitts', + 'Ether', + 'Bombos', + 'Quake', + 'Bottle', + 'Bottle (Red Potion)', + 'Bottle (Green Potion)', + 'Bottle (Blue Potion)', + 'Bottle (Fairy)', + 'Bottle (Bee)', + 'Bottle (Good Bee)', + 'Master Sword', + 'Tempered Sword', + 'Fighter Sword', + 'Golden Sword', + 'Progressive Sword', + 'Progressive Glove', + 'Master Sword', + 'Power Star', + 'Triforce Piece', + 'Single Arrow', + 'Blue Mail', + 'Red Mail', + 'Progressive Armor', + 'Blue Boomerang', + 'Red Boomerang', + 'Blue Shield', + 'Red Shield', + 'Mirror Shield', + 'Progressive Shield', + 'Bug Catching Net', + 'Cane of Byrna', + 'Magic Upgrade (1/2)', + 'Magic Upgrade (1/4)' + ] + +KeysanityItems = ['Small Key (Eastern Palace)', + 'Big Key (Eastern Palace)', + 'Small Key (Escape)', + 'Small Key (Desert Palace)', + 'Big Key (Desert Palace)', + 'Small Key (Tower of Hera)', + 'Big Key (Tower of Hera)', + 'Small Key (Agahnims Tower)', + 'Small Key (Palace of Darkness)', + 'Big Key (Palace of Darkness)', + 'Small Key (Thieves Town)', + 'Big Key (Thieves Town)', + 'Small Key (Swamp Palace)', + 'Big Key (Swamp Palace)', + 'Small Key (Skull Woods)', + 'Big Key (Skull Woods)', + 'Small Key (Ice Palace)', + 'Big Key (Ice Palace)', + 'Small Key (Misery Mire)', + 'Big Key (Misery Mire)', + 'Small Key (Turtle Rock)', + 'Big Key (Turtle Rock)', + 'Small Key (Ganons Tower)', + 'Big Key (Ganons Tower)' + ] diff --git a/Rules.py b/Rules.py index 17b6f4e99a..6d2d77675c 100644 --- a/Rules.py +++ b/Rules.py @@ -43,7 +43,7 @@ def set_rules(world, player): if world.goal == 'dungeons': # require all dungeons to beat ganon - add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has('Beat Agahnim 1', player) and state.has('Beat Agahnim 2', player)) + add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has('Beat Agahnim 1', player) and state.has('Beat Agahnim 2', player) and state.has_crystals(7, player)) elif world.goal == 'ganon': # require aga2 to beat ganon add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) @@ -427,8 +427,7 @@ def global_rules(world, player): 'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Validation Chest']: forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player) - set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has('Crystal 1', player) and state.has('Crystal 2', player) - and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player) + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon, player) and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop @@ -436,7 +435,7 @@ def global_rules(world, player): set_trock_key_rules(world, player) - set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has('Crystal 1', player) and state.has('Crystal 2', player) and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)) + set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player)) def inverted_rules(world, player): world.get_location('Ganon', player).item_rule = lambda item: item.name == 'Triforce' and item.player == player @@ -901,8 +900,7 @@ def swordless_rules(world, player): set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) #in swordless mode bombos pads are present in the relevant parts of ice palace - set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has('Crystal 1', player) and state.has('Crystal 2', player) - and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)) + set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon, player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop From 8fb89971e54b137ce930fac097dbfb2f90266efc Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Thu, 25 Jul 2019 18:25:58 -0400 Subject: [PATCH 16/52] Fix standard mode --- Rom.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Rom.py b/Rom.py index 066897fe22..b006457066 100644 --- a/Rom.py +++ b/Rom.py @@ -931,6 +931,7 @@ def patch_rom(world, player, rom): rom.write_byte(0x18005E, world.crystals_needed_for_gt) rom.write_byte(0x18005F, world.crystals_needed_for_ganon) + rom.write_byte(0x18008A, 0x01 if world.mode == "standard" else 0x00) # block HC upstairs doors in rain state in standard mode rom.write_byte(0x18016A, 0x01 if world.keysanity else 0x00) # free roaming item text boxes rom.write_byte(0x18003B, 0x01 if world.keysanity else 0x00) # maps showing crystals on overworld From d4f1bb7091d1022e4a2031f742e97fe7c2f5114d Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 4 Aug 2019 11:37:44 -0400 Subject: [PATCH 17/52] Implement V31 prize packs --- Rom.py | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/Rom.py b/Rom.py index b006457066..f43547314d 100644 --- a/Rom.py +++ b/Rom.py @@ -697,6 +697,18 @@ def patch_rom(world, player, rom): 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3] + + def chunk(l,n): + return [l[i:i+n] for i in range(0, len(l), n)] + + # randomize last 7 slots + prizes [-7:] = random.sample(prizes, 7) + + #shuffle order of 7 main packs + packs = chunk(prizes[:56], 8) + random.shuffle(packs) + prizes[:56] = [drop for pack in packs for drop in pack] + if world.difficulty in ['hard', 'expert', 'insane']: prize_replacements = {0xE0: 0xDF, # Fairy -> heart 0xE3: 0xD8} # Big magic -> small magic @@ -709,7 +721,6 @@ def patch_rom(world, player, rom): prizes = [prize_replacements.get(prize, prize) for prize in prizes] dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes] rom.write_bytes(0x180100, dig_prizes) - random.shuffle(prizes) # write tree pull prizes rom.write_byte(0xEFBD4, prizes.pop()) @@ -729,28 +740,6 @@ def patch_rom(world, player, rom): # fill enemy prize packs rom.write_bytes(0x37A78, prizes) - # prize pack drop chances - if world.difficulty in ['hard', 'expert', 'insane']: - droprates = [0x01, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04] # 50%, 25%, 3* 12.5%, 2* 6.25% - else: - droprates = [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01] # 50% - - random.shuffle(droprates) - rom.write_bytes(0x37A62, droprates) - - vanilla_prize_pack_assignment = [131, 150, 132, 128, 128, 128, 128, 128, 2, 0, 2, 128, 160, 131, 151, 128, 128, 148, 145, 7, 0, 128, 0, 128, 146, 150, 128, 160, 0, 0, 0, 128, 4, 128, - 130, 6, 6, 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 128, 128, 144, 128, 145, 145, - 145, 151, 145, 149, 149, 147, 151, 20, 145, 146, 129, 130, 130, 128, 133, 128, 128, 128, 4, 4, 128, 145, 128, 128, 128, 128, 128, 128, 128, 128, 0, 128, - 128, 130, 138, 128, 128, 128, 128, 146, 145, 128, 130, 129, 129, 128, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 151, 128, 128, 128, 128, 194, - 128, 21, 21, 23, 6, 0, 128, 0, 192, 19, 64, 0, 2, 6, 16, 20, 0, 0, 64, 0, 0, 0, 0, 19, 70, 17, 128, 128, 0, 0, 0, 16, 0, 0, 0, 22, 22, 22, 129, 135, 130, - 0, 128, 128, 0, 0, 0, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 23, 0, 18, 0, 0, 0, 0, 0, 16, 23, 0, 64, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0] - - # shuffle enemies to prize packs - for i in range(243): - if vanilla_prize_pack_assignment[i] & 0x0F != 0x00: - rom.write_byte(0x6B632 + i, (vanilla_prize_pack_assignment[i] & 0xF0) | random.randint(1, 7)) - # set bonk prizes bonk_prizes = [0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xAC, 0xE3, 0xE3, 0xDA, 0xE3, 0xDA, 0xD8, 0xAC, 0xAC, 0xE3, 0xD8, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xDC, 0xDB, 0xE3, 0xDA, 0x79, 0x79, 0xE3, 0xE3, 0xDA, 0x79, 0xAC, 0xAC, 0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xE3, 0x79, 0xDE, 0xE3, 0xAC, 0xDB, 0x79, 0xE3, 0xD8, 0xAC, 0x79, 0xE3, 0xDB, 0xDB, 0xE3, 0xE3, 0x79, 0xD8, 0xDD] From b0f4fa8cec3fdd61c5efb29a4b5b3e4176e620de Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 4 Aug 2019 12:32:35 -0400 Subject: [PATCH 18/52] Partial implementation of many V31 features Partial support for Progressive bow - Still needs to be added to item pool - Silver hint handling remains TBD even for VT Added weapons selection. - Vanilla needs to be implemented - Assured needs to be implemented - Inverted swordless is almost certainly messed up. - Swordless standard mode will likely softlock - Random weapon standard mode is currently treated as uncle assured Deleted removed difficulties - Remaining difficulties still need to be adjusted Added locked property to locations: - This is used for preplaced items etc so that multiworld balancing knows they cannot be moved. Made a few of the difficulty changes from V31, but not all. Added required text changes to handle crystals requirements - More changes will likely me made in future - Currently there is is no way to tell ganon requirement in Inverted mode --- .gitignore | 1 + BaseClasses.py | 39 ++++++++++++- Bosses.py | 12 ++-- Dungeons.py | 21 ++++--- EntranceRandomizer.py | 26 +++++---- Fill.py | 5 +- Gui.py | 4 +- ItemList.py | 130 ++++++++++-------------------------------- Items.py | 1 + Main.py | 6 +- README.md | 13 +---- Rom.py | 75 +++++++----------------- Rules.py | 16 ++---- Text.py | 2 + _vendor/__init__.py | 0 15 files changed, 143 insertions(+), 208 deletions(-) create mode 100644 _vendor/__init__.py diff --git a/.gitignore b/.gitignore index 65c35fa8ae..c4ce894b5e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ README.html *multidata *multisave EnemizerCLI/ +.mypy_cache/ \ No newline at end of file diff --git a/BaseClasses.py b/BaseClasses.py index 09fdba1db4..7bca192d27 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -8,11 +8,12 @@ from Utils import int16_as_bytes class World(object): - 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): + def __init__(self, players, shuffle, logic, mode, swords, 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.logic = logic self.mode = mode + self.swords = swords self.difficulty = difficulty self.timer = timer self.progressive = progressive @@ -161,6 +162,13 @@ class World(object): ret.prog_items.add(('Red Shield', item.player)) elif self.difficulty_requirements.progressive_shield_limit >= 1: ret.prog_items.add(('Blue Shield', item.player)) + elif 'Bow' in item.name: + if ret.has('Silver Arrows', item.player): + pass + elif ret.has('Bow', item.player): + ret.prog_items.add(('Silver Arrows', item.player)) + else: + ret.prog_items.add(('Bow', item.player)) elif item.name.startswith('Bottle'): if ret.bottle_count(item.player) < self.difficulty_requirements.progressive_bottle_limit: ret.prog_items.add((item.name, item.player)) @@ -409,8 +417,6 @@ class CollectionState(object): basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player)) elif self.world.difficulty == 'expert' and not fullrefill: basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count(player)) - elif self.world.difficulty == 'insane' and not fullrefill: - basemagic = basemagic else: basemagic = basemagic + basemagic * self.bottle_count(player) return basemagic >= smallmagic @@ -524,6 +530,15 @@ class CollectionState(object): elif self.world.difficulty_requirements.progressive_shield_limit >= 1: self.prog_items.add(('Blue Shield', item.player)) changed = True + elif 'Bow' in item.name: + if self.has('Silver Arrows', item.player): + pass + elif self.has('Bow', item.player): + self.prog_items.add(('Silver Arrows', item.player)) + changed = True + else: + self.prog_items.add(('Bow', item.player)) + changed = True elif item.name.startswith('Bottle'): if self.bottle_count(item.player) < self.world.difficulty_requirements.progressive_bottle_limit: self.prog_items.add((item.name, item.player)) @@ -560,6 +575,22 @@ class CollectionState(object): to_remove = 'Power Glove' else: to_remove = None + elif 'Shield' in item.name: + if self.has('Mirror Shield', item.player): + to_remove = 'Mirror Shield' + elif self.has('Red Shield', item.player): + to_remove = 'Red Shield' + elif self.has('Blue Shield', item.player): + to_remove = 'Blue Shield' + else: + to_remove = 'None' + elif 'Bow' in item.name: + if self.has('Silver Arrows', item.player): + to_remove = 'Silver Arrows' + elif self.has('Bow', item.player): + to_remove = 'Bow' + else: + to_remove = None if to_remove is not None: try: @@ -742,6 +773,7 @@ class Location(object): self.recursion_count = 0 self.staleness_count = 0 self.event = False + self.locked = True self.always_allow = lambda item, state: False self.access_rule = lambda state: True self.item_rule = lambda item: True @@ -975,6 +1007,7 @@ class Spoiler(object): 'seed': self.world.seed, 'logic': self.world.logic, 'mode': self.world.mode, + 'swords': self.world.swords, 'goal': self.world.goal, 'shuffle': self.world.shuffle, 'algorithm': self.world.algorithm, diff --git a/Bosses.py b/Bosses.py index 43072966bb..8cdaee5a51 100644 --- a/Bosses.py +++ b/Bosses.py @@ -72,18 +72,18 @@ def KholdstareDefeatRule(state, player): state.has('Fire Rod', player) or ( state.has('Bombos', player) and - # FIXME: the following only actually works for the vanilla location for swordless mode - (state.has_sword(player) or state.world.mode == 'swordless') + # FIXME: the following only actually works for the vanilla location for swordless + (state.has_sword(player) or state.world.swords == 'swordless') ) ) and ( state.has_blunt_weapon(player) 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 ( state.has('Fire Rod', player) and state.has('Bombos', player) and - state.world.mode == 'swordless' and + state.world.swords == 'swordless' and state.can_extend_magic(player, 16) ) ) @@ -116,7 +116,7 @@ boss_table = { } def can_place_boss(world, boss, dungeon_name, level=None): - if world.mode in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace': + if world.swords in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace': return False if dungeon_name == 'Ganons Tower' and level == 'top': @@ -161,7 +161,7 @@ def place_bosses(world, player): if world.boss_shuffle in ["basic", "normal"]: # temporary hack for swordless kholdstare: - if world.mode == 'swordless': + if world.swords == 'swordless': world.get_dungeon('Ice Palace', player).boss = BossFactory('Kholdstare', player) logging.getLogger('').debug('Placing boss Kholdstare at Ice Palace') boss_locations.remove(['Ice Palace', None]) diff --git a/Dungeons.py b/Dungeons.py index 12592af748..3e412f70d5 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -46,11 +46,13 @@ def fill_dungeons(world): all_state_base = world.get_all_state() for player in range(1, world.players + 1): + pinball_room = world.get_location('Skull Woods - Pinball Room', player) if world.retro: - world.push_item(world.get_location('Skull Woods - Pinball Room', player), ItemFactory('Small Key (Universal)', player), False) + world.push_item(pinball_room, ItemFactory('Small Key (Universal)', player), False) else: - 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 + world.push_item(pinball_room, ItemFactory('Small Key (Skull Woods)', player), False) + pinball_room.event = True + pinball_room.locked = True dungeons = [(list(dungeon.regions), dungeon.big_key, list(dungeon.small_keys), list(dungeon.dungeon_items)) for dungeon in world.dungeons] @@ -77,6 +79,7 @@ def fill_dungeons(world): world.push_item(bk_location, big_key, False) bk_location.event = True + bk_location.locked = True dungeon_locations.remove(bk_location) big_key = None @@ -102,6 +105,7 @@ def fill_dungeons(world): world.push_item(sk_location, small_key, False) sk_location.event = True + sk_location.locked = True dungeon_locations.remove(sk_location) if small_keys: @@ -122,13 +126,14 @@ def fill_dungeons_restrictive(world, shuffled_locations): all_state_base = world.get_all_state() for player in range(1, world.players + 1): - skull_woods_big_chest = world.get_location('Skull Woods - Pinball Room', player) + pinball_room = world.get_location('Skull Woods - Pinball Room', player) if world.retro: - world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Universal)', player), False) + world.push_item(pinball_room, ItemFactory('Small Key (Universal)', player), False) else: - world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)', player), False) - skull_woods_big_chest.event = True - shuffled_locations.remove(skull_woods_big_chest) + world.push_item(pinball_room, ItemFactory('Small Key (Skull Woods)', player), False) + pinball_room.event = True + pinball_room.locked = True + shuffled_locations.remove(pinball_room) if world.keysanity: #in keysanity dungeon items are distributed as part of the normal item pool diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 4812742653..47efc450d5 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -28,7 +28,7 @@ def start(): No Logic: Distribute items without regard for item requirements. ''') - parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless', 'inverted'], + parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'inverted'], help='''\ Select game mode. (default: %(default)s) Open: World starts with Zelda rescued. @@ -36,17 +36,25 @@ def start(): but may lead to weird rain state issues if you exit through the Hyrule Castle side exits before rescuing Zelda in a full shuffle. - Swordless: Like Open, but with no swords. Curtains in - Skull Woods and Agahnims Tower are removed, - Agahnim\'s Tower barrier can be destroyed with - hammer. Misery Mire and Turtle Rock can be opened - without a sword. Hammer damages Ganon. Ether and - Bombos Tablet can be activated with Hammer (and Book). Inverted: Starting locations are Dark Sanctuary in West Dark World or at Link's House, which is shuffled freely. Requires the moon pearl to be Link in the Light World instead of a bunny. ''') + parser.add_argument('--swords', default='random', const='random', nargs='?', choices= ['random', 'assured', 'swordless', 'vanilla'], + help='''\ + Select sword placement. (default: %(default)s) + Random: All swords placed randomly. + Assured: Start game with a sword already. + Swordless: No swords. Curtains in Skull Woods and Agahnim\'s + Tower are removed, Agahnim\'s Tower barrier can be + destroyed with hammer. Misery Mire and Turtle Rock + can be opened without a sword. Hammer damages Ganon. + Ether and Bombos Tablet can be activated with Hammer + (and Book). Bombos pads have been added in Ice + Palace, to allow for an alternative to firerod. + Vanilla: Swords are in vanilla locations. + ''') parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], help='''\ Select completion goal. (default: %(default)s) @@ -59,14 +67,12 @@ def start(): Triforce Hunt: Places 30 Triforce Pieces in the world, collect 20 of them to beat the game. ''') - parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['easy', 'normal', 'hard', 'expert', 'insane'], + parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['normal', 'hard', 'expert'], help='''\ Select game difficulty. Affects available itempool. (default: %(default)s) - Easy: An easy setting with extra equipment. Normal: Normal difficulty. Hard: A harder setting with less equipment and reduced health. Expert: A harder yet setting with minimum equipment and health. - Insane: A setting with the absolute minimum in equipment and no extra health. ''') parser.add_argument('--timer', default='none', const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'], help='''\ diff --git a/Fill.py b/Fill.py index 86e0a5a7f0..0e1a48cc0c 100644 --- a/Fill.py +++ b/Fill.py @@ -374,6 +374,9 @@ def balance_multiworld_progression(world): reducing_state.sweep_for_events(locations=locations_to_test) + if testing.locked: + continue + if world.has_beaten_game(balancing_state): if not world.has_beaten_game(reducing_state): items_to_replace.append(testing) @@ -383,7 +386,7 @@ def balance_multiworld_progression(world): items_to_replace.append(testing) replaced_items = False - locations_for_replacing = [l for l in checked_locations if not l.event] + locations_for_replacing = [l for l in checked_locations if not l.event and not l.locked] while locations_for_replacing and items_to_replace: new_location = locations_for_replacing.pop() old_location = items_to_replace.pop() diff --git a/Gui.py b/Gui.py index 078a3ee15b..bcaa486e2e 100755 --- a/Gui.py +++ b/Gui.py @@ -145,7 +145,7 @@ def guiMain(args=None): modeFrame = Frame(drowDownFrame) modeVar = StringVar() modeVar.set('open') - modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'swordless', 'inverted') + modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'inverted') modeOptionMenu.pack(side=RIGHT) modeLabel = Label(modeFrame, text='Game Mode') modeLabel.pack(side=LEFT) @@ -169,7 +169,7 @@ def guiMain(args=None): difficultyFrame = Frame(drowDownFrame) difficultyVar = StringVar() difficultyVar.set('normal') - difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'easy', 'normal', 'hard', 'expert', 'insane') + difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'normal', 'hard', 'expert') difficultyOptionMenu.pack(side=RIGHT) difficultyLabel = Label(difficultyFrame, text='Game difficulty') difficultyLabel.pack(side=LEFT) diff --git a/ItemList.py b/ItemList.py index 629f293189..8b0ea681e7 100644 --- a/ItemList.py +++ b/ItemList.py @@ -29,17 +29,6 @@ normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)'] normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 - -easybaseitems = (['Sanctuary Heart Container'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 + ['Lamp'] * 2 + ['Silver Arrows'] * 2 + - ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12) -easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)'] -easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart -easyfirst15extra = ['Rupees (100)'] + ['Arrows (10)'] * 7 + ['Bombs (3)'] * 7 -easysecond10extra = ['Bombs (3)'] * 7 + ['Rupee (1)', 'Rupees (50)', 'Bombs (10)'] -easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)'] -easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2 -easytimedotherextra = ['Red Clock'] * 5 - hardbaseitems = ['Silver Arrows', 'Single Arrow', 'Bombs (10)'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 6 + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 7 + ['Bombs (3)'] * 4 hardfirst20extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Bombs (3)'] * 5 + ['Rupees (5)'] * 10 + ['Arrows (10)', 'Rupee (1)'] hardsecond10extra = ['Rupees (5)'] * 5 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] @@ -55,13 +44,6 @@ expertthird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] expertfourth5extra = ['Rupees (5)'] * 5 expertfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 -insanebaseitems = ['Rupees (300)'] * 4 + ['Single Arrow', 'Bombs (10)', 'Rupee (1)'] + ['Rupees (5)'] * 24 + ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (20)'] * 5 -insanefirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Rupees (5)'] * 12 -insanesecond15extra = ['Rupees (5)'] * 10 + ['Rupees (20)'] * 5 -insanethird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] * 3 + ['Rupee (1)'] -insanefourth5extra = ['Rupees (5)'] * 5 -insanefinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 - Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', @@ -72,14 +54,6 @@ Difficulty = namedtuple('Difficulty', total_items_to_place = 153 -def easy_conditional_extras(timer, _goal, _mode, pool, placed_items): - extraitems = total_items_to_place - len(pool) - len(placed_items) - if extraitems < len(easyextra): - return easylimitedextra - if timer in ['timed', 'timed-countdown']: - return easytimedotherextra - return [] - def no_conditional_extras(*_args): return [] @@ -109,30 +83,6 @@ difficulties = { progressive_armor_limit = 2, progressive_bottle_limit = 4, ), - 'easy': Difficulty( - baseitems = easybaseitems, - bottles = normalbottles, - bottle_count = 8, - same_bottle = False, - progressiveshield = ['Progressive Shield'] * 6, - basicshield = ['Blue Shield', 'Red Shield', 'Mirror Shield'] * 2, - progressivearmor = ['Progressive Armor'] * 4, - basicarmor = ['Blue Mail', 'Red Mail'] * 2, - swordless = ['Rupees (20)'] * 8, - progressivesword = ['Progressive Sword'] * 7, - basicsword = ['Master Sword', 'Tempered Sword', 'Golden Sword'] *2 + ['Fighter Sword'], - timedohko = ['Green Clock'] * 25, - timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5, # +5 more Red Clocks if there is room - triforcehunt = ['Triforce Piece'] * 30, - triforce_pieces_required = 20, - retro = ['Small Key (Universal)'] * 27, - conditional_extras = easy_conditional_extras, - extras = [easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra], - progressive_sword_limit = 4, - progressive_shield_limit = 3, - progressive_armor_limit = 2, - progressive_bottle_limit = 4, - ), 'hard': Difficulty( baseitems = hardbaseitems, bottles = hardbottles, @@ -181,34 +131,10 @@ difficulties = { progressive_armor_limit = 0, progressive_bottle_limit = 4, ), - 'insane': Difficulty( - baseitems = insanebaseitems, - bottles = hardbottles, - bottle_count = 4, - same_bottle = False, - progressiveshield = [], - basicshield = [], - progressivearmor = [], - basicarmor = [], - swordless = ['Rupees (20)'] * 3 + ['Silver Arrows'], - progressivesword = ['Progressive Sword'] * 3, - basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'], - timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, - timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, - triforcehunt = ['Triforce Piece'] * 30, - triforce_pieces_required = 20, - retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, - conditional_extras = no_conditional_extras, - extras = [insanefirst15extra, insanesecond15extra, insanethird10extra, insanefourth5extra, insanefinal25extra], - progressive_sword_limit = 2, - progressive_shield_limit = 0, - progressive_armor_limit = 0, - progressive_bottle_limit = 4, - ), } 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 ['normal', 'hard', 'expert'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'] or world.mode not in ['open', 'standard', 'swordless', 'inverted'] 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') @@ -217,29 +143,37 @@ def generate_itempool(world, player): world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) world.get_location('Ganon', player).event = True + world.get_location('Ganon', player).locked = True world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False) world.get_location('Agahnim 1', player).event = True + world.get_location('Agahnim 1', player).locked = True world.push_item(world.get_location('Agahnim 2', player), ItemFactory('Beat Agahnim 2', player), False) world.get_location('Agahnim 2', player).event = True + world.get_location('Agahnim 2', player).locked = True world.push_item(world.get_location('Dark Blacksmith Ruins', player), ItemFactory('Pick Up Purple Chest', player), False) world.get_location('Dark Blacksmith Ruins', player).event = True + world.get_location('Dark Blacksmith Ruins', player).locked = True world.push_item(world.get_location('Frog', player), ItemFactory('Get Frog', player), False) world.get_location('Frog', player).event = True + world.get_location('Frog', player).locked = True world.push_item(world.get_location('Missing Smith', player), ItemFactory('Return Smith', player), False) world.get_location('Missing Smith', player).event = True + world.get_location('Missing Smith', player).locked = True world.push_item(world.get_location('Floodgate', player), ItemFactory('Open Floodgate', player), False) world.get_location('Floodgate', player).event = True + world.get_location('Floodgate', player).locked = True # set up item pool if world.custom: - (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro, world.customitemarray) + (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.swords, world.retro, world.customitemarray) world.rupoor_cost = min(world.customitemarray[67], 9999) 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.swords, world.retro) world.itempool += ItemFactory(pool, player) for (location, item) in placed_items: world.push_item(world.get_location(location, player), ItemFactory(item, player), False) world.get_location(location, player).event = True + world.get_location(location, player).locked = True world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms if clock_mode is not None: world.clock_mode = clock_mode @@ -254,7 +188,7 @@ def generate_itempool(world, player): # 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) # 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 ['normal', 'hard'] and not (world.custom and world.customitemarray[30] == 0): [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): adv_heart_pieces = [item for item in world.itempool if item.name == 'Piece of Heart' and item.player == player][0:4] @@ -341,6 +275,7 @@ def create_dynamic_shop_locations(world, player): world.push_item(loc, ItemFactory(item['item'], player), False) loc.event = True + loc.locked = True def fill_prizes(world, attempts=15): @@ -393,7 +328,7 @@ def set_up_shops(world, player): #special shop types -def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro): +def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro): pool = [] placed_items = [] clock_mode = None @@ -411,8 +346,6 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro): pool.extend(basicgloves) lamps_needed_for_dark_rooms = 1 - if difficulty == 'easy': - lamps_needed_for_dark_rooms = 3 # insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start if shuffle == 'insanity_legacy': @@ -448,7 +381,7 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro): else: pool.extend(diff.basicarmor) - if mode == 'swordless': + if swords == 'swordless': pool.extend(diff.swordless) elif mode == 'standard': if want_progressives(): @@ -505,7 +438,7 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro): pool.extend(['Small Key (Universal)']) return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) -def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, retro, customitemarray): +def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray): pool = [] placed_items = [] clock_mode = None @@ -589,8 +522,6 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, r diff = difficulties[difficulty] lamps_needed_for_dark_rooms = 1 - if difficulty == 'easy': - lamps_needed_for_dark_rooms = customitemarray[12] # expert+ difficulties produce the same contents for # all bottles, since only one bottle is available @@ -659,24 +590,25 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, r # A quick test to ensure all combinations generate the correct amount of items. def test(): - for difficulty in ['easy', 'normal', 'hard', 'expert', 'insane']: + for difficulty in ['normal', 'hard', 'expert']: for goal in ['ganon', 'triforcehunt', 'pedestal']: for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']: - for mode in ['open', 'standard', 'swordless', 'inverted']: - for progressive in ['on', 'off']: - for shuffle in ['full', 'insane']: - for retro in [True, False]: - out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro) - count = len(out[0]) + len(out[1]) + for mode in ['open', 'standard', 'inverted']: + for swords in ['random', 'assured', 'swordless', 'vanilla']: + for progressive in ['on', 'off']: + for shuffle in ['full', 'insanity_legacy']: + for retro in [True, False]: + out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro) + count = len(out[0]) + len(out[1]) - correct_count = total_items_to_place - if goal in ['pedestal']: - # pedestal goals generate one extra item - correct_count += 1 - if retro: - correct_count += 28 + correct_count = total_items_to_place + if goal in ['pedestal']: + # pedestal goals generate one extra item + correct_count += 1 + if retro: + correct_count += 28 - assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, retro)) + assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, retro)) if __name__ == '__main__': test() diff --git a/Items.py b/Items.py index 1bc94cc9dc..f9a7d88481 100644 --- a/Items.py +++ b/Items.py @@ -24,6 +24,7 @@ def ItemFactory(items, player): # Format: Name: (Advancement, Priority, Type, ItemCode, Pedestal Hint Text, Pedestal Credit Text, Sick Kid Credit Text, Zora Credit Text, Witch Credit Text, Flute Boy Credit Text, Hint Text) item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'), + 'Progressive Bow': (True, False, None, 0x64, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Book of Mudora': (True, False, None, 0x1D, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), 'Hammer': (True, False, None, 0x09, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the hammer'), 'Hookshot': (True, False, None, 0x0A, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), diff --git a/Main.py b/Main.py index 33afc1d14d..7d6536f62d 100644 --- a/Main.py +++ b/Main.py @@ -24,7 +24,7 @@ def main(args, seed=None): start = time.clock() # initialize the world - 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) + world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, 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('') if seed is None: random.seed(None) @@ -180,7 +180,7 @@ def gt_filler(world): def copy_world(world): # ToDo: Not good yet - 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 = World(world.players, world.shuffle, world.logic, world.mode, world.swords, 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 = world.required_medallions.copy() ret.swamp_patch_required = world.swamp_patch_required.copy() ret.ganon_at_pyramid = world.ganon_at_pyramid.copy() @@ -238,6 +238,8 @@ def copy_world(world): item.location = ret.get_location(location.name, location.player) if location.event: ret.get_location(location.name, location.player).event = True + if location.locked: + ret.get_location(location.name, location.player).locked = True # copy remaining itempool. No item in itempool should have an assigned location for item in world.itempool: diff --git a/README.md b/README.md index 4c07f5ec8b..25bdbd9caf 100644 --- a/README.md +++ b/README.md @@ -95,12 +95,6 @@ This is only noticeably different if the the Ganon shuffle option is enabled. ## Game Difficulty -### Easy - -This setting doubles the number of swords, shields, armors, bottles, and silver arrows in the item pool. -This setting will also triple the number of Lamps available, and all will be obtainable before dark rooms. -Within dungeons, the number of items found will be displayed on screen if there is no timer. - ### Normal This is the default setting that has an item pool most similar to the original @@ -118,11 +112,6 @@ the player from having fairies in bottles. This setting is a more extreme version of the Hard setting. Potions are further nerfed, the item pool is less helpful, and the player can find no armor, only a Master Sword, and only a single bottle. -### Insane - -This setting is a modest step up from Expert. The main difference is that the player will never find any -additional health. - ## Timer Setting ### None @@ -358,7 +347,7 @@ Select the game mode. (default: open) Select the game completion goal. (default: ganon) ``` ---difficulty [{easy,normal,hard,expert,insane}] +--difficulty [{normal,hard,expert}] ``` Select the game difficulty. Affects available itempool. (default: normal) diff --git a/Rom.py b/Rom.py index f43547314d..da1f64fd87 100644 --- a/Rom.py +++ b/Rom.py @@ -549,7 +549,7 @@ def patch_rom(world, player, rom): rom.write_byte(0x51DE, 0x00) # set open mode: - if world.mode in ['open', 'swordless', 'inverted']: + if world.mode in ['open', 'inverted']: rom.write_byte(0x180032, 0x01) # open mode # disable sword sprite from uncle @@ -587,7 +587,7 @@ def patch_rom(world, player, rom): # potion magic restore amount rom.write_byte(0x180085, 0x40) # Half Magic #Cape magic cost - rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02]) + rom.write_bytes(0x3ADA7, [0x02, 0x04, 0x08]) # Byrna Invulnerability: off rom.write_byte(0x18004F, 0x00) #Disable catching fairies @@ -603,31 +603,11 @@ def patch_rom(world, player, rom): # Powdered Fairies Prize rom.write_byte(0x36DD0, 0xD8) # One Heart # potion heal amount - rom.write_byte(0x180084, 0x08) # One Heart + rom.write_byte(0x180084, 0x20) # 4 Hearts # potion magic restore amount rom.write_byte(0x180085, 0x20) # Quarter Magic #Cape magic cost - rom.write_bytes(0x3ADA7, [0x01, 0x01, 0x01]) - # Byrna Invulnerability: off - rom.write_byte(0x18004F, 0x00) - #Disable catching fairies - rom.write_byte(0x34FD6, 0x80) - overflow_replacement = GREEN_TWENTY_RUPEES - # Rupoor negative value - rom.write_int16(0x180036, world.rupoor_cost) - # Set stun items - rom.write_byte(0x180180, 0x00) # Nothing - # Make silver arrows only usable against Ganon - rom.write_byte(0x180181, 0x01) - elif world.difficulty == 'insane': - # Powdered Fairies Prize - rom.write_byte(0x36DD0, 0x79) # Bees - # potion heal amount - rom.write_byte(0x180084, 0x00) # No healing - # potion magic restore amount - rom.write_byte(0x180085, 0x00) # No healing - #Cape magic cost - rom.write_bytes(0x3ADA7, [0x01, 0x01, 0x01]) + rom.write_bytes(0x3ADA7, [0x02, 0x04, 0x08]) # Byrna Invulnerability: off rom.write_byte(0x18004F, 0x00) #Disable catching fairies @@ -664,12 +644,7 @@ def patch_rom(world, player, rom): else: overflow_replacement = GREEN_TWENTY_RUPEES - if world.difficulty in ['easy']: - rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon - elif world.retro and world.difficulty in ['hard', 'expert', 'insane']: #FIXME: this is temporary for v29 baserom (perhaps no so temporary?) - rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon - else: - rom.write_byte(0x180182, 0x01) # auto equip silvers on pickup + rom.write_byte(0x180182, 0x01) # auto equip silvers on pickup #Byrna residual magic cost rom.write_bytes(0x45C42, [0x04, 0x02, 0x01]) @@ -709,7 +684,7 @@ def patch_rom(world, player, rom): random.shuffle(packs) prizes[:56] = [drop for pack in packs for drop in pack] - if world.difficulty in ['hard', 'expert', 'insane']: + if world.difficulty in ['hard', 'expert']: prize_replacements = {0xE0: 0xDF, # Fairy -> heart 0xE3: 0xD8} # Big magic -> small magic prizes = [prize_replacements.get(prize, prize) for prize in prizes] @@ -753,26 +728,16 @@ def patch_rom(world, player, rom): rom.write_byte(address, prize) # Fill in item substitutions table - if world.difficulty in ['easy']: - rom.write_bytes(0x184000, [ - # original_item, limit, replacement_item, filler - 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees - 0x58, 0x01, 0x43, 0xFF, # silver arrows -> 1 arrow - 0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade - 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade - 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel - ]) - else: - rom.write_bytes(0x184000, [ - # original_item, limit, replacement_item, filler - 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees - 0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade - 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade - 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel - ]) + rom.write_bytes(0x184000, [ + # original_item, limit, replacement_item, filler + 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees + 0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade + 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade + 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel + ]) # set Fountain bottle exchange items - if world.difficulty in ['hard', 'expert', 'insane']: + if world.difficulty in ['hard', 'expert']: rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)]) rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)]) else: @@ -837,9 +802,7 @@ def patch_rom(world, player, rom): rom.write_int32(0x180200, -100 * 60 * 60 * 60) # red clock adjustment time (in frames, sint32) rom.write_int32(0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32) rom.write_int32(0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32) - if world.difficulty == 'easy': - rom.write_int32(0x18020C, (20 + ERtimeincrease) * 60 * 60) # starting time (in frames, sint32) - elif world.difficulty == 'normal': + if world.difficulty == 'normal': rom.write_int32(0x18020C, (10 + ERtimeincrease) * 60 * 60) # starting time (in frames, sint32) else: rom.write_int32(0x18020C, int((5 + ERtimeincrease / 2) * 60 * 60)) # starting time (in frames, sint32) @@ -866,7 +829,7 @@ def patch_rom(world, player, rom): rom.write_byte(0x180211, 0x06) #Game type, we set the Entrance and item randomization flags # 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 world dungeon 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. if world.mode == 'inverted': rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted @@ -878,6 +841,7 @@ def patch_rom(world, player, rom): rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest rom.write_byte(0x50599, 0x00) # disable below ganon chest rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest + rom.write_byte(0x18008B, 0x00) # Pyramid Hole not pre-opened rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness @@ -928,8 +892,6 @@ def patch_rom(world, player, rom): # compasses showing dungeon count if world.clock_mode != 'off': rom.write_byte(0x18003C, 0x00) # Currently must be off if timer is on, because they use same HUD location - elif world.difficulty == 'easy': - rom.write_byte(0x18003C, 0x02) # always on elif world.keysanity: rom.write_byte(0x18003C, 0x01) # show on pickup else: @@ -1311,6 +1273,9 @@ def write_strings(rom, world, player): greenpendant = world.find_items('Green Pendant', player)[0] tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant.hint_text + tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt == 1 else 'You need %d crystals to enter.') % world.crystals_needed_for_gt + tt['sign_ganon'] = ('You need %d crystal to beat Ganon.' if world.crystals_needed_for_ganon == 1 else 'You need %d crystals to beat Ganon.') % world.crystals_needed_for_ganon + tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)] tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)] tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)] diff --git a/Rules.py b/Rules.py index 6d2d77675c..1db807b8fe 100644 --- a/Rules.py +++ b/Rules.py @@ -26,14 +26,16 @@ def set_rules(world, player): open_rules(world, player) elif world.mode == 'standard': standard_rules(world, player) - elif world.mode == 'swordless': - swordless_rules(world, player) elif world.mode == 'inverted': open_rules(world, player) inverted_rules(world, player) else: raise NotImplementedError('Not implemented yet') + if world.swords == 'swordless': + # FIXME: !!! Does not handle inverted properly + swordless_rules(world, player) + if world.logic == 'noglitches': no_glitches_rules(world, player) elif world.logic == 'minorglitches': @@ -792,8 +794,7 @@ def inverted_rules(world, player): 'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Validation Chest']: forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player) - set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has('Crystal 1', player) and state.has('Crystal 2', player) - and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player) + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon, player) and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop @@ -801,7 +802,7 @@ def inverted_rules(world, player): set_trock_key_rules(world, player) - set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has('Crystal 1', player) and state.has('Crystal 2', player) and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)) + set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player)) def no_glitches_rules(world, player): if world.mode != 'inverted': @@ -887,11 +888,6 @@ def open_rules(world, player): def swordless_rules(world, player): - # for the time being swordless mode just inherits all fixes from open mode. - # should there ever be fixes that apply to open mode but not swordless, this - # can be revisited. - open_rules(world, player) - set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Agahnim 1', player), lambda state: (state.has('Hammer', player) or state.has('Fire Rod', player) or state.can_shoot_arrows(player) or state.has('Cane of Somaria', player)) and state.has_key('Small Key (Agahnims Tower)', player, 2)) set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) diff --git a/Text.py b/Text.py index b8baba8316..7cafee7d72 100644 --- a/Text.py +++ b/Text.py @@ -1883,5 +1883,7 @@ class TextTable(object): # 190 text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("How did you get up here?") text['fish_money'] = CompressedTextMapper.convert("It's a secret to everyone.") + text['sign_ganons_tower'] = CompressedTextMapper.convert("You need all 7 crystals to enter.") + text['sign_ganon'] = CompressedTextMapper.convert("You need all 7 crystals to beat Ganon.") text['end_pad_data'] = bytearray([0xfb]) text['terminator'] = bytearray([0xFF, 0xFF]) diff --git a/_vendor/__init__.py b/_vendor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 3713f6a1b5d0986c56fed488df255cc8af872780 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 4 Aug 2019 13:22:37 -0400 Subject: [PATCH 19/52] Add keysanity map reveal An overlooked v30 feature --- Rom.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Rom.py b/Rom.py index da1f64fd87..580c23f76c 100644 --- a/Rom.py +++ b/Rom.py @@ -898,6 +898,33 @@ def patch_rom(world, player, rom): rom.write_byte(0x18003C, 0x00) rom.write_byte(0x180045, 0xFF if world.keysanity else 0x00) # free roaming items in menu + + # Map reveals + reveal_bytes = { + "Eastern Palace": 0x2000, + "Desert Palace": 0x1000, + "Tower of Hera": 0x0020, + "Palace of Darkness": 0x0200, + "Thieves Town": 0x0010, + "Skull Woods": 0x0080, + "Swamp Palace": 0x0400, + "Ice Palace": 0x0040, + "Misery Mire'": 0x0100, + "Turtle Rock": 0x0008, + } + + def get_reveal_bytes(itemName): + locations = world.find_items(itemName, player) + if len(locations) < 1: + return 0x0000 + location = locations[0] + if location.parent_region and location.parent_region.dungeon: + return reveal_bytes.get(location.parent_region.dungeon.name, 0x0000) + return 0x0000 + + rom.write_int16(0x18017A, get_reveal_bytes('Green Pendant') if world.keysanity else 0x0000) # Sahasrahla reveal + rom.write_int16(0x18017C, get_reveal_bytes('Crystal 5')|get_reveal_bytes('Crystal 6') if world.keysanity else 0x0000) # Bomb Shop Reveal + rom.write_byte(0x180172, 0x01 if world.retro else 0x00) # universal keys rom.write_byte(0x180175, 0x01 if world.retro else 0x00) # rupee bow rom.write_byte(0x180176, 0x0A if world.retro else 0x00) # wood arrow cost From ef7c3d4f06a07eda8b68e1e4816572244b8d875a Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 4 Aug 2019 17:40:13 -0400 Subject: [PATCH 20/52] New Item/Location accessibility options Replaces existing check_only_beatable, which became the "none" option. TR can run out of key placement options, with the 100% locations option, but I really don't care enough. It exists mostly for people who want to 100% a seed, or to point to if they ask about keys locked behind themselves. --- BaseClasses.py | 8 ++--- EntranceRandomizer.py | 12 ++++--- Fill.py | 4 +-- Gui.py | 5 --- ItemList.py | 6 +++- Main.py | 13 +++---- README.md | 8 ++--- Rules.py | 80 +++++++++++++++++++++++++++++++++---------- 8 files changed, 85 insertions(+), 51 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 7bca192d27..6e2335d423 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -8,7 +8,7 @@ from Utils import int16_as_bytes class World(object): - def __init__(self, players, shuffle, logic, mode, swords, 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, swords, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, accessibility, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints): self.players = players self.shuffle = shuffle self.logic = logic @@ -50,7 +50,7 @@ class World(object): self.lock_aga_door_in_escape = False self.fix_trock_doors = self.shuffle != 'vanilla' self.save_and_quit_from_boss = True - self.check_beatable_only = check_beatable_only + self.accessibility = accessibility self.fix_skullwoods_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] self.fix_palaceofdarkness_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] self.fix_trock_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] @@ -1014,7 +1014,7 @@ class Spoiler(object): 'difficulty': self.world.difficulty, 'timer': self.world.timer, 'progressive': self.world.progressive, - 'completeable': not self.world.check_beatable_only, + 'accessibility': self.world.accessibility, 'dungeonitems': self.world.place_dungeon_items, 'quickswap': self.world.quickswap, 'fastmenu': self.world.fastmenu, @@ -1047,7 +1047,7 @@ class Spoiler(object): outfile.write('Goal: %s\n' % self.metadata['goal']) outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle']) outfile.write('Filling Algorithm: %s\n' % self.metadata['algorithm']) - outfile.write('All Locations Accessible: %s\n' % ('Yes' if self.metadata['completeable'] else 'No, some locations may be unreachable')) + outfile.write('Accessibility: %s\n' % self.metadata['accessibility']) 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('Menu speed: %s\n' % self.metadata['fastmenu']) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 47efc450d5..1803dd73ad 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -186,11 +186,13 @@ def start(): Remove Maps and Compasses from Itempool, replacing them by empty slots. ''', action='store_true') - parser.add_argument('--beatableonly', help='''\ - Only check if the game is beatable with placement. Do not - ensure all locations are reachable. This only has an effect - on the restrictive algorithm currently. - ''', action='store_true') + parser.add_argument('--accessibility', default='items', const='items', nargs='?', choices=['items', 'locations', 'none'], help='''\ + Select Item/Location Accessibility. (default: %(default)s) + Items: You can reach all unique inventory items. No guarantees about + reaching all locations or all keys. + Locations: You will be able to reach every location in the game. + None: You will be able to reach enough locations to beat the game. + ''') parser.add_argument('--hints', help='''\ Make telepathic tiles and storytellers give helpful hints. ''', action='store_true') diff --git a/Fill.py b/Fill.py index 0e1a48cc0c..bfc1145e38 100644 --- a/Fill.py +++ b/Fill.py @@ -184,7 +184,7 @@ def fill_restrictive(world, base_state, locations, itempool): maximum_exploration_state = sweep_from_pool() perform_access_check = True - if world.check_beatable_only: + if world.accessibility == 'none': perform_access_check = not world.has_beaten_game(maximum_exploration_state) for item_to_place in items_to_place: @@ -197,7 +197,7 @@ def fill_restrictive(world, base_state, locations, itempool): if spot_to_fill is None: # we filled all reachable spots. Maybe the game can be beaten anyway? if world.can_beat_game(): - if not world.check_beatable_only: + if world.accessibility != 'none': 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) diff --git a/Gui.py b/Gui.py index bcaa486e2e..e02457d15c 100755 --- a/Gui.py +++ b/Gui.py @@ -66,8 +66,6 @@ def guiMain(args=None): retroCheckbutton = Checkbutton(checkBoxFrame, text="Retro mode (universal keys)", variable=retroVar) dungeonItemsVar = IntVar() dungeonItemsCheckbutton = Checkbutton(checkBoxFrame, text="Place Dungeon Items (Compasses/Maps)", onvalue=0, offvalue=1, variable=dungeonItemsVar) - beatableOnlyVar = IntVar() - beatableOnlyCheckbutton = Checkbutton(checkBoxFrame, text="Only ensure seed is beatable, not all items must be reachable", variable=beatableOnlyVar) disableMusicVar = IntVar() disableMusicCheckbutton = Checkbutton(checkBoxFrame, text="Disable game music", variable=disableMusicVar) shuffleGanonVar = IntVar() @@ -85,7 +83,6 @@ def guiMain(args=None): keysanityCheckbutton.pack(expand=True, anchor=W) retroCheckbutton.pack(expand=True, anchor=W) dungeonItemsCheckbutton.pack(expand=True, anchor=W) - beatableOnlyCheckbutton.pack(expand=True, anchor=W) disableMusicCheckbutton.pack(expand=True, anchor=W) shuffleGanonCheckbutton.pack(expand=True, anchor=W) hintsCheckbutton.pack(expand=True, anchor=W) @@ -331,7 +328,6 @@ def guiMain(args=None): guiargs.keysanity = bool(keysanityVar.get()) guiargs.retro = bool(retroVar.get()) guiargs.nodungeonitems = bool(dungeonItemsVar.get()) - guiargs.beatableonly = bool(beatableOnlyVar.get()) guiargs.quickswap = bool(quickSwapVar.get()) guiargs.disablemusic = bool(disableMusicVar.get()) guiargs.shuffleganon = bool(shuffleGanonVar.get()) @@ -1071,7 +1067,6 @@ def guiMain(args=None): retroVar.set(args.retro) if args.nodungeonitems: dungeonItemsVar.set(int(not args.nodungeonitems)) - beatableOnlyVar.set(int(args.beatableonly)) quickSwapVar.set(int(args.quickswap)) disableMusicVar.set(int(args.disablemusic)) if args.count: diff --git a/ItemList.py b/ItemList.py index 8b0ea681e7..740d5ff1a4 100644 --- a/ItemList.py +++ b/ItemList.py @@ -141,7 +141,11 @@ def generate_itempool(world, player): if world.timer in ['ohko', 'timed-ohko']: world.can_take_damage = False - world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) + if world.goal in ['pedestal', 'triforcehunt']: + world.push_item(world.get_location('Ganon', player), ItemFactory('Nothing', player), False) + else: + world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) + world.get_location('Ganon', player).event = True world.get_location('Ganon', player).locked = True world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False) diff --git a/Main.py b/Main.py index 7d6536f62d..24a4ff9cf1 100644 --- a/Main.py +++ b/Main.py @@ -24,7 +24,7 @@ def main(args, seed=None): start = time.clock() # initialize the world - world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, 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.swords, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.accessibility, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints) logger = logging.getLogger('') if seed is None: random.seed(None) @@ -180,7 +180,7 @@ def gt_filler(world): def copy_world(world): # ToDo: Not good yet - ret = World(world.players, world.shuffle, world.logic, world.mode, world.swords, 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.swords, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.accessibility, 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 = world.required_medallions.copy() ret.swamp_patch_required = world.swamp_patch_required.copy() ret.ganon_at_pyramid = world.ganon_at_pyramid.copy() @@ -277,13 +277,8 @@ def create_playthrough(world): old_world = world world = copy_world(world) - # in treasure hunt and pedestal goals, ganon is invincible - if world.goal in ['pedestal', 'triforcehunt']: - 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 world.check_beatable_only and not world.can_beat_game(): + if world.accessibility == 'none' and not world.can_beat_game(): raise RuntimeError('Cannot beat game. Something went terribly wrong here!') # get locations containing progress items @@ -314,7 +309,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)) if not sphere: 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.accessibility == 'none': raise RuntimeError('Not all progression items reachable. Something went terribly wrong here.') else: break diff --git a/README.md b/README.md index 25bdbd9caf..d1be5fc06b 100644 --- a/README.md +++ b/README.md @@ -287,10 +287,6 @@ In further concert with the Bow changes, all arrows under pots, in chests, and e If not set, Compasses and Maps are removed from the dungeon item pools and replaced by empty chests that may end up anywhere in the world. This may lead to different amount of itempool items being placed in a dungeon than you are used to. -## Only Ensure Seed Beatable - -If set, will only ensure the goal can be achieved, but not necessarily that all locations are reachable. Currently only affects VT25, VT26 and balanced algorithms. - ## Include Ganon's Tower and Pyramid Hole in Shuffle pool If set, Ganon's Tower is included in the dungeon shuffle pool and the Pyramid Hole/Exit pair is included in the Holes shuffle pool. Ganon can not be defeated until the primary goal is fulfilled. @@ -457,10 +453,10 @@ Select the color of Link\'s heart meter. (default: red) Use to select a different sprite sheet to use for Link. Path to a binary file of length 0x7000 containing the sprite data stored at address 0x80000 in the rom. (default: None) ``` ---beatableonly +--accessibility [{items,locations,none}] ``` -Enables the "Only Ensure Seed Beatable" option (default: False) +Sets the item/location accessibility rules. (default: items) ``` --hints diff --git a/Rules.py b/Rules.py index 1db807b8fe..0339ec984f 100644 --- a/Rules.py +++ b/Rules.py @@ -274,7 +274,8 @@ def global_rules(world, player): set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player)) - set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player)) for location in ['Tower of Hera - Boss', 'Tower of Hera - Big Chest', 'Tower of Hera - Compass Chest']: @@ -288,7 +289,8 @@ def global_rules(world, player): set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state.has_key('Small Key (Swamp Palace)', player)) set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player) or item_name(state, 'Swamp Palace - Big Chest', player) == ('Big Key (Swamp Palace)', player)) - set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player)) set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player)) @@ -300,7 +302,8 @@ def global_rules(world, player): set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Prize', player)) set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state.has_key('Small Key (Thieves Town)', player) or item_name(state, 'Thieves\' Town - Big Chest', player) == ('Small Key (Thieves Town)', player)) and state.has('Hammer', player)) - set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state.has_key('Small Key (Thieves Town)', player)) for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves\' Town - Boss']: forbid_item(world.get_location(location, player), 'Big Key (Thieves Town)', player) @@ -312,7 +315,8 @@ def global_rules(world, player): set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) or item_name(state, 'Skull Woods - Big Chest', player) == ('Big Key (Skull Woods)', player)) - set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and state.has_sword(player)) # sword required for curtain set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player)) @@ -376,10 +380,17 @@ def global_rules(world, player): set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player)) set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 3))) - set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + else: + forbid_item(world.get_location('Palace of Darkness - Big Key Chest', player), 'Small Key (Palace of Darkness)', player) set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Harmless Hellway', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 4))) - set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + else: + forbid_item(world.get_location('Palace of Darkness - Harmless Hellway', player), 'Small Key (Palace of Darkness)', player) + set_rule(world.get_entrance('Palace of Darkness Maze Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6)) set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize', player)) @@ -393,7 +404,10 @@ def global_rules(world, player): set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player), ('Small Key (Ganons Tower)', player)] and state.has_key('Small Key (Ganons Tower)', player, 3))) - set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state.has_key('Small Key (Ganons Tower)', player, 3)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state.has_key('Small Key (Ganons Tower)', player, 3)) + else: + forbid_item(world.get_location('Ganons Tower - Map Chest', player), 'Small Key (Ganons Tower)', player) # It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere. We reflect this in the chest requirements. # However we need to leave these at the lower values to derive that with 3 keys it is always possible to reach Bob and Ice Armos. @@ -639,7 +653,8 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player)) - set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player)) for location in ['Tower of Hera - Boss', 'Tower of Hera - Big Chest', 'Tower of Hera - Compass Chest']: @@ -653,7 +668,8 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state.has_key('Small Key (Swamp Palace)', player)) set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player) or item_name(state, 'Swamp Palace - Big Chest', player) == ('Big Key (Swamp Palace)', player)) - set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player)) set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player)) @@ -665,7 +681,8 @@ def inverted_rules(world, player): set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Prize', player)) set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state.has_key('Small Key (Thieves Town)', player) or item_name(state, 'Thieves\' Town - Big Chest', player) == ('Small Key (Thieves Town)', player)) and state.has('Hammer', player)) - set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state.has_key('Small Key (Thieves Town)', player)) for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves\' Town - Boss']: forbid_item(world.get_location(location, player), 'Big Key (Thieves Town)', player) @@ -677,7 +694,9 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) or item_name(state, 'Skull Woods - Big Chest', player) == ('Big Key (Skull Woods)', player)) - set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) + set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and state.has_sword(player)) # sword required for curtain set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player)) @@ -741,10 +760,17 @@ def inverted_rules(world, player): set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player)) set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 3))) - set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + else: + forbid_item(world.get_location('Palace of Darkness - Big Key Chest', player), 'Small Key (Palace of Darkness)', player) set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) or (item_name(state, 'Palace of Darkness - Harmless Hellway', player) in [('Small Key (Palace of Darkness)', player)] and state.has_key('Small Key (Palace of Darkness)', player, 4))) - set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) + else: + forbid_item(world.get_location('Palace of Darkness - Harmless Hellway', player), 'Small Key (Palace of Darkness)', player) + set_rule(world.get_entrance('Palace of Darkness Maze Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6)) set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize', player)) @@ -758,7 +784,10 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player), ('Small Key (Ganons Tower)', player)] and state.has_key('Small Key (Ganons Tower)', player, 3))) - set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state.has_key('Small Key (Ganons Tower)', player, 3)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Ganons Tower - Map Chest', player), lambda state, item: item.name == 'Small Key (Ganons Tower)' and item.player == player and state.has_key('Small Key (Ganons Tower)', player, 3)) + else: + forbid_item(world.get_location('Ganons Tower - Map Chest', player), 'Small Key (Ganons Tower)', player) # It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere. We reflect this in the chest requirements. # However we need to leave these at the lower values to derive that with 3 keys it is always possible to reach Bob and Ice Armos. @@ -958,10 +987,16 @@ def set_trock_key_rules(world, player): # might open all the locked doors in any order so we need maximally restrictive rules. if can_reach_back: set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state.has_key('Small Key (Turtle Rock)', player, 4) or item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) + else: + forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Small Key (Turtle Rock)', player) elif can_reach_front and can_reach_middle: set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, tr_big_key_chest_keys_needed(state))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) + else: + forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Small Key (Turtle Rock)', player) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] @@ -969,14 +1004,20 @@ def set_trock_key_rules(world, player): set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 2)) set_rule(world.get_entrance('Turtle Rock Pokey Room', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 1)) set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, tr_big_key_chest_keys_needed(state))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player and state.has_key('Small Key (Turtle Rock)', player, 2)) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player and state.has_key('Small Key (Turtle Rock)', player, 2)) + else: + forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Small Key (Turtle Rock)', player) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] elif can_reach_big_chest: set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 2) if item_in_locations(state, 'Big Key (Turtle Rock)', player, [('Turtle Rock - Compass Chest', player), ('Turtle Rock - Roller Room - Left', player), ('Turtle Rock - Roller Room - Right', player)]) else state.has_key('Small Key (Turtle Rock)', player, 4)) set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state.has_key('Small Key (Turtle Rock)', player, 4) or item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) + else: + forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Small Key (Turtle Rock)', player) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] @@ -985,7 +1026,8 @@ def set_trock_key_rules(world, player): else: set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 2) if item_in_locations(state, 'Big Key (Turtle Rock)', player, [('Turtle Rock - Compass Chest', player), ('Turtle Rock - Roller Room - Left', player), ('Turtle Rock - Roller Room - Right', player)]) else state.has_key('Small Key (Turtle Rock)', player, 4)) set_rule(world.get_location('Turtle Rock - Big Key Chest', player), lambda state: (state.has_key('Small Key (Turtle Rock)', player, 4) or item_name(state, 'Turtle Rock - Big Key Chest', player) == ('Small Key (Turtle Rock)', player))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) + if world.accessibility != 'locations': + set_always_allow(world.get_location('Turtle Rock - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Turtle Rock)' and item.player == player) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] From b8ea2eb4b12ef165246b5e1c8427834413d957d3 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Tue, 6 Aug 2019 21:36:45 -0400 Subject: [PATCH 21/52] Fix random weapons for Standard Mode Now works roughly like VT, except that swords are somwhat more common and bombs-only escape is not supported (because I don't want to make 'Bombs (10)' an advancement item) Swordless Standard Mode should also work now Assured and Vanilla weapons still need to be implemented. --- ItemList.py | 20 +++-------------- Rom.py | 34 ++++++++++++++++++++--------- Rules.py | 63 ++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 72 insertions(+), 45 deletions(-) diff --git a/ItemList.py b/ItemList.py index 740d5ff1a4..205a41afbc 100644 --- a/ItemList.py +++ b/ItemList.py @@ -387,13 +387,6 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r if swords == 'swordless': pool.extend(diff.swordless) - elif mode == 'standard': - if want_progressives(): - placed_items.append(('Link\'s Uncle', 'Progressive Sword')) - pool.extend(diff.progressivesword) - else: - placed_items.append(('Link\'s Uncle', 'Fighter Sword')) - pool.extend(diff.basicsword) else: if want_progressives(): pool.extend(diff.progressivesword) @@ -557,14 +550,6 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s itemtotal = itemtotal + 1 if mode == 'standard': - if progressive == 'off': - placed_items.append(('Link\'s Uncle', 'Fighter Sword')) - pool.extend(['Fighter Sword'] * max((customitemarray[32] - 1), 0)) - pool.extend(['Progressive Sword'] * customitemarray[36]) - else: - placed_items.append(('Link\'s Uncle', 'Progressive Sword')) - pool.extend(['Fighter Sword'] * customitemarray[32]) - pool.extend(['Progressive Sword'] * max((customitemarray[36] - 1), 0)) if retro: key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) placed_items.append((key_location, 'Small Key (Universal)')) @@ -572,10 +557,11 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s else: pool.extend(['Small Key (Universal)'] * customitemarray[68]) else: - pool.extend(['Fighter Sword'] * customitemarray[32]) - pool.extend(['Progressive Sword'] * customitemarray[36]) pool.extend(['Small Key (Universal)'] * customitemarray[68]) + pool.extend(['Fighter Sword'] * customitemarray[32]) + pool.extend(['Progressive Sword'] * customitemarray[36]) + if shuffle == 'insanity_legacy': placed_items.append(('Link\'s House', 'Magic Mirror')) placed_items.append(('Sanctuary', 'Moon Pearl')) diff --git a/Rom.py b/Rom.py index 580c23f76c..3d88033da6 100644 --- a/Rom.py +++ b/Rom.py @@ -551,7 +551,13 @@ def patch_rom(world, player, rom): # set open mode: if world.mode in ['open', 'inverted']: rom.write_byte(0x180032, 0x01) # open mode + if world.mode == 'inverted': + set_inverted_mode(world, rom) + elif world.mode == 'standard': + rom.write_byte(0x180032, 0x00) # standard mode + uncle_location = world.get_location('Link\'s Uncle', player) + if uncle_location.item is None or uncle_location.item.name not in ['Master Sword', 'Tempered Sword', 'Fighter Sword', 'Golden Sword', 'Progressive Sword']: # disable sword sprite from uncle rom.write_bytes(0x6D263, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) rom.write_bytes(0x6D26B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) @@ -563,10 +569,6 @@ def patch_rom(world, player, rom): rom.write_bytes(0x6D2EB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E]) rom.write_bytes(0x6D31B, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) rom.write_bytes(0x6D323, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) - if world.mode == 'inverted': - set_inverted_mode(world, rom) - elif world.mode == 'standard': - rom.write_byte(0x180032, 0x00) # standard mode # set light cones rom.write_byte(0x180038, 0x01 if world.sewer_light_cone else 0x00) @@ -866,12 +868,6 @@ def patch_rom(world, player, rom): rom.write_bytes(0x180080, [50, 50, 70, 70]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10) rom.write_byte(0x18004D, 0x00) # Escape assist (off) - rom.write_byte(0x18004E, 0x00) # escape fills - rom.write_int16(0x180183, 0) # rupee fill (for bow if rupee arrows enabled) - rom.write_bytes(0x180185, [0x00, 0x00, 0x00]) # uncle item refills - rom.write_bytes(0x180188, [0x00, 0x00, 0x00]) # zelda item refills - rom.write_bytes(0x18018B, [0x00, 0x00, 0x00]) # uncle item refills - if world.goal in ['pedestal', 'triforcehunt']: rom.write_byte(0x18003E, 0x01) # make ganon invincible @@ -950,6 +946,24 @@ def patch_rom(world, player, rom): rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E]) rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E]) + rom.write_byte(0x18004E, 0) # Escape Fill (nothing) + rom.write_int16(0x180183, 300) # Escape fill rupee bow + rom.write_bytes(0x180185, [0,0,0]) # Uncle respawn refills (magic, bombs, arrows) + rom.write_bytes(0x180188, [0,0,0]) # Zelda respawn refills (magic, bombs, arrows) + rom.write_bytes(0x18018B, [0,0,0]) # Mantle respawn refills (magic, bombs, arrows) + if world.mode == 'standard': + if uncle_location.item is not None and uncle_location.item.name in ['Bow', 'Progressive Bow']: + rom.write_byte(0x18004E, 1) # Escape Fill (arrows) + rom.write_int16(0x180183, 300) # Escape fill rupee bow + rom.write_bytes(0x180185, [0,0,70]) # Uncle respawn refills (magic, bombs, arrows) + rom.write_bytes(0x180188, [0,0,10]) # Zelda respawn refills (magic, bombs, arrows) + rom.write_bytes(0x18018B, [0,0,10]) # Mantle respawn refills (magic, bombs, arrows) + elif uncle_location.item is not None and uncle_location.item.name in ['Cane of Somaria', 'Cane of Byrna', 'Fire Rod']: + rom.write_byte(0x18004E, 4) # Escape Fill (magic) + rom.write_bytes(0x180185, [0x80,0,0]) # Uncle respawn refills (magic, bombs, arrows) + rom.write_bytes(0x180188, [0x20,0,0]) # Zelda respawn refills (magic, bombs, arrows) + rom.write_bytes(0x18018B, [0x20,0,0]) # Mantle respawn refills (magic, bombs, arrows) + # 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[player] else 0x00) diff --git a/Rules.py b/Rules.py index 0339ec984f..116c414864 100644 --- a/Rules.py +++ b/Rules.py @@ -1,5 +1,6 @@ import collections import logging +from BaseClasses import CollectionState def set_rules(world, player): @@ -7,17 +8,17 @@ def set_rules(world, player): if world.logic == 'nologic': logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!') if world.mode != 'inverted': - world.get_region('Links House', player).can_reach = lambda state: True - world.get_region('Sanctuary', player).can_reach = lambda state: True + world.get_region('Links House', player).can_reach_private = lambda state: True + world.get_region('Sanctuary', player).can_reach_private = lambda state: True old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) return else: - world.get_region('Inverted Links House', player).can_reach = lambda state: True - world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach = lambda state: True + world.get_region('Inverted Links House', player).can_reach_private = lambda state: True + world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True if world.shuffle != 'vanilla': old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) return if world.mode != 'inverted': global_rules(world, player) @@ -90,6 +91,9 @@ def forbid_item(location, item, player): old_rule = location.item_rule location.item_rule = lambda i: (i.name != item or i.player != player) and old_rule(i) +def add_item_rule(location, rule): + old_rule = location.item_rule + location.item_rule = lambda item: rule(item) and old_rule(item) def item_in_locations(state, item, player, locations): for location in locations: @@ -111,15 +115,20 @@ def global_rules(world, player): forbid_item(location, 'Triforce Piece', player) # ganon can only carry triforce - world.get_location('Ganon', player).item_rule = lambda item: item.name == 'Triforce' and item.player == player + add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) - # these are default save&quit points and always accessible - world.get_region('Links House', player).can_reach = lambda state: True - world.get_region('Sanctuary', player).can_reach = lambda state: True + if world.mode == 'standard': + world.get_region('Hyrule Castle Secret Entrance', player).can_reach_private = lambda state: True + old_rule = world.get_region('Links House', player).can_reach + world.get_region('Links House', player).can_reach_private = lambda state: state.can_reach('Sanctuary', 'Region', player) or old_rule(state) + else: + # these are default save&quit points and always accessible + world.get_region('Links House', player).can_reach_private = lambda state: True + world.get_region('Sanctuary', player).can_reach_private = lambda state: True # we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled! old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) # overworld requirements set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has_Boots(player)) @@ -454,14 +463,20 @@ def global_rules(world, player): set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player)) def inverted_rules(world, player): - world.get_location('Ganon', player).item_rule = lambda item: item.name == 'Triforce' and item.player == player - world.get_region('Inverted Links House', player).can_reach = lambda state: True - world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach = lambda state: True + if world.goal == 'triforcehunt': + for location in world.get_locations(): + if location.player != player: + forbid_item(location, 'Triforce Piece', player) + + add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) + + world.get_region('Inverted Links House', player).can_reach_private = lambda state: True + world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True # we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled! if world.shuffle != 'vanilla': old_rule = world.get_region('Old Man House', player).can_reach - world.get_region('Old Man House', player).can_reach = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state) # overworld requirements set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player)) set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has_Pearl(player)) @@ -930,9 +945,21 @@ def swordless_rules(world, player): def standard_rules(world, player): - for loc in ['Sanctuary', 'Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', - 'Sewers - Secret Room - Right']: - add_rule(world.get_location(loc, player), lambda state: state.can_kill_most_things(player) and state.has_key('Small Key (Escape)', player)) + add_rule(world.get_entrance('Sewers Door', player), lambda state: state.can_kill_most_things(player)) + + set_rule(world.get_entrance('Hyrule Castle Exit (East)', player), lambda state: state.can_reach('Sanctuary', 'Region', player)) + set_rule(world.get_entrance('Hyrule Castle Exit (West)', player), lambda state: state.can_reach('Sanctuary', 'Region', player)) + + # ensures the required weapon for escape lands on uncle (unless player has it pre-equipped) + add_rule(world.get_location('Secret Passage', player), lambda state: state.can_kill_most_things(player)) + add_rule(world.get_location('Hyrule Castle - Map Chest', player), lambda state: state.can_kill_most_things(player)) + + def uncle_item_rule(item): + copy_state = CollectionState(world) + copy_state.collect(item) + return copy_state.can_reach('Sanctuary', 'Region', player) + + add_item_rule(world.get_location('Link\'s Uncle', player), uncle_item_rule) # easiest way to enforce key placement not relevant for open set_rule(world.get_location('Sewers - Dark Cross', player), lambda state: state.can_kill_most_things(player)) From 996bf8495c6373e1c36f830fa05dfab5403ed166 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 10 Aug 2019 15:30:14 -0400 Subject: [PATCH 22/52] Implement new weapons modes This also includes some partial additional cleanup of the item pool. --- BaseClasses.py | 9 ++++- ItemList.py | 97 ++++++++++++++++++++++++++------------------------ Main.py | 1 + Rom.py | 12 +++++++ Rules.py | 1 + 5 files changed, 73 insertions(+), 47 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 6e2335d423..cdee826a8a 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -3,7 +3,7 @@ from enum import Enum, unique import logging import json from collections import OrderedDict -from _vendor.collections_extended import bag, setlist +from _vendor.collections_extended import bag from Utils import int16_as_bytes class World(object): @@ -24,6 +24,7 @@ class World(object): self.shops = [] self.itempool = [] self.seed = None + self.precollected_items = [] self.state = CollectionState(self) self.required_medallions = dict([(player, ['Ether', 'Quake']) for player in range(1, players + 1)]) self._cached_entrances = None @@ -195,6 +196,10 @@ class World(object): 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 and location.item.player == player] + def push_precollected(self, item): + self.precollected_items.append(item) + self.state.collect(item, True) + def push_item(self, location, item, collect=True): if not isinstance(location, Location): raise RuntimeError('Cannot assign item %s to location %s (player %d).' % (item, location, item.player)) @@ -302,6 +307,8 @@ class CollectionState(object): self.path = {} self.locations_checked = set() self.stale = {player: True for player in range(1, parent.players + 1)} + for item in parent.precollected_items: + self.collect(item, True) def update_reachable_regions(self, player): player_regions = [region for region in self.world.regions if region.player == player] diff --git a/ItemList.py b/ItemList.py index 205a41afbc..f72d217999 100644 --- a/ItemList.py +++ b/ItemList.py @@ -21,43 +21,24 @@ basicgloves = ['Power Glove', 'Titans Mitts'] normalbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)'] hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)', 'Bottle (Good Bee)'] -normalbaseitems = (['Silver Arrows', 'Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrows (10)', 'Bombs (3)'] + +normalbaseitems = (['Silver Arrows', 'Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrows (10)', 'Bombs (10)'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24) normalfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Arrows (10)'] * 6 + ['Bombs (3)'] * 6 -normalsecond15extra = ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] + ['Bombs (10)'] +normalsecond15extra = ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', 'Rupee (1)', 'Rupees (5)'] normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)'] normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 -hardbaseitems = ['Silver Arrows', 'Single Arrow', 'Bombs (10)'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 6 + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 7 + ['Bombs (3)'] * 4 -hardfirst20extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Bombs (3)'] * 5 + ['Rupees (5)'] * 10 + ['Arrows (10)', 'Rupee (1)'] -hardsecond10extra = ['Rupees (5)'] * 5 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] -hardthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Rupees (5)'] * 3 -hardfourth10extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 7 + ['Rupees (5)'] -hardfinal20extra = ['Rupees (20)'] * 18 + ['Rupees (5)'] * 2 - -expertbaseitems = (['Rupees (300)'] * 4 + ['Single Arrow', 'Silver Arrows', 'Boss Heart Container', 'Rupee (1)', 'Bombs (10)'] + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 2 + - ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2) -expertfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Rupees (5)'] * 12 -expertsecond15extra = ['Rupees (5)'] * 10 + ['Rupees (20)'] * 5 -expertthird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] * 3 + ['Rupee (1)'] -expertfourth5extra = ['Rupees (5)'] * 5 -expertfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 - Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivesword', 'basicsword', 'timedohko', 'timedother', - 'triforcehunt', 'triforce_pieces_required', 'retro', 'conditional_extras', + 'triforcehunt', 'triforce_pieces_required', 'retro', 'extras', 'progressive_sword_limit', 'progressive_shield_limit', 'progressive_armor_limit', 'progressive_bottle_limit']) total_items_to_place = 153 -def no_conditional_extras(*_args): - return [] - - difficulties = { 'normal': Difficulty( baseitems = normalbaseitems, @@ -76,7 +57,6 @@ difficulties = { triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, retro = ['Small Key (Universal)'] * 17 + ['Rupees (20)'] * 10, - conditional_extras = no_conditional_extras, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 4, progressive_shield_limit = 3, @@ -84,7 +64,7 @@ difficulties = { progressive_bottle_limit = 4, ), 'hard': Difficulty( - baseitems = hardbaseitems, + baseitems = normalbaseitems, bottles = hardbottles, bottle_count = 4, same_bottle = False, @@ -95,27 +75,26 @@ difficulties = { swordless = ['Rupees (20)'] * 4, progressivesword = ['Progressive Sword'] * 3, basicsword = ['Master Sword', 'Master Sword', 'Tempered Sword'], - timedohko = ['Green Clock'] * 20, + timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, - conditional_extras = no_conditional_extras, - extras = [hardfirst20extra, hardsecond10extra, hardthird10extra, hardfourth10extra, hardfinal20extra], + extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 3, progressive_shield_limit = 2, progressive_armor_limit = 1, progressive_bottle_limit = 4, ), 'expert': Difficulty( - baseitems = expertbaseitems, + baseitems = normalbaseitems, bottles = hardbottles, bottle_count = 4, same_bottle = False, progressiveshield = ['Progressive Shield'] * 3, basicshield = ['Progressive Shield'] * 3, #only the first one will upgrade, making this equivalent to two blue shields - progressivearmor = [], - basicarmor = [], + progressivearmor = ['Progressive Armor'] * 2, # neither will count + basicarmor = ['Progressive Armor'] * 2, # neither will count swordless = ['Rupees (20)'] * 4, progressivesword = ['Progressive Sword'] * 3, basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'], @@ -124,8 +103,7 @@ difficulties = { triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, - conditional_extras = no_conditional_extras, - extras = [expertfirst15extra, expertsecond15extra, expertthird10extra, expertfourth5extra, expertfinal25extra], + extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 2, progressive_shield_limit = 1, progressive_armor_limit = 0, @@ -169,11 +147,13 @@ def generate_itempool(world, player): # set up item pool if world.custom: - (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.swords, world.retro, world.customitemarray) + (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.swords, world.retro, world.customitemarray) world.rupoor_cost = min(world.customitemarray[67], 9999) 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.swords, world.retro) + (pool, placed_items, precollected_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.swords, world.retro) world.itempool += ItemFactory(pool, player) + for item in precollected_items: + world.push_precollected(ItemFactory(item, player)) for (location, item) in placed_items: world.push_item(world.get_location(location, player), ItemFactory(item, player), False) world.get_location(location, player).event = True @@ -301,7 +281,7 @@ def fill_prizes(world, attempts=15): random.shuffle(prize_locs) fill_restrictive(world, all_state, prize_locs, prizepool) except FillError as e: - logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times" % (e, attempts)) + logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times", e, attempts) for location in empty_crystal_locations: location.item = None continue @@ -335,6 +315,7 @@ def set_up_shops(world, player): def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro): pool = [] placed_items = [] + precollected_items = [] clock_mode = None treasure_hunt_count = None treasure_hunt_icon = None @@ -387,6 +368,31 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r if swords == 'swordless': pool.extend(diff.swordless) + elif swords == 'assured': + precollected_items.append('Fighter Sword') + if want_progressives(): + pool.extend(diff.progressivesword) + pool.extend(['Rupees (100)']) + else: + pool.extend(diff.basicsword) + pool.extend(['Rupees (100)']) + elif swords == 'vanilla': + swords_to_use = [] + if want_progressives(): + swords_to_use.extend(diff.progressivesword) + swords_to_use.extend(['Progressive Sword']) + else: + swords_to_use.extend(diff.basicsword) + swords_to_use.extend(['Fighter Sword']) + random.shuffle(swords_to_use) + + placed_items.append(('Link\'s Uncle', swords_to_use.pop())) + placed_items.append(('Blacksmith', swords_to_use.pop())) + placed_items.append(('Pyramid Fairy - Left', swords_to_use.pop())) + if goal != 'pedestal': + placed_items.append(('Master Sword Pedestal', swords_to_use.pop())) + else: + placed_items.append(('Master Sword Pedestal', 'Triforce')) else: if want_progressives(): pool.extend(diff.progressivesword) @@ -411,16 +417,12 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r treasure_hunt_count = diff.triforce_pieces_required treasure_hunt_icon = 'Triforce Piece' - cond_extras = diff.conditional_extras(timer, goal, mode, pool, placed_items) - pool.extend(cond_extras) - extraitems -= len(cond_extras) - for extra in diff.extras: if extraitems > 0: pool.extend(extra) extraitems -= len(extra) - if goal == 'pedestal': + if goal == 'pedestal' and swords != 'vanilla': placed_items.append(('Master Sword Pedestal', 'Triforce')) if retro: pool = [item.replace('Single Arrow','Rupees (5)') for item in pool] @@ -433,11 +435,12 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r placed_items.append((key_location, 'Small Key (Universal)')) else: pool.extend(['Small Key (Universal)']) - return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) + return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray): pool = [] placed_items = [] + precollected_items = [] clock_mode = None treasure_hunt_count = None treasure_hunt_icon = None @@ -561,7 +564,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s pool.extend(['Fighter Sword'] * customitemarray[32]) pool.extend(['Progressive Sword'] * customitemarray[36]) - + if shuffle == 'insanity_legacy': placed_items.append(('Link\'s House', 'Magic Mirror')) placed_items.append(('Sanctuary', 'Moon Pearl')) @@ -576,7 +579,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s if itemtotal < total_items_to_place: pool.extend(['Nothing'] * (total_items_to_place - itemtotal)) - return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) + return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) # A quick test to ensure all combinations generate the correct amount of items. def test(): @@ -592,13 +595,15 @@ def test(): count = len(out[0]) + len(out[1]) correct_count = total_items_to_place - if goal in ['pedestal']: + if goal == 'pedestal' and swords != 'vanilla': # pedestal goals generate one extra item correct_count += 1 if retro: correct_count += 28 - - assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, retro)) + try: + assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro)) + except AssertionError as e: + print(e) if __name__ == '__main__': test() diff --git a/Main.py b/Main.py index 24a4ff9cf1..8109615228 100644 --- a/Main.py +++ b/Main.py @@ -247,6 +247,7 @@ def copy_world(world): # copy progress items in state ret.state.prog_items = world.state.prog_items.copy() + ret.precollected_items = world.precollected_items.copy() ret.state.stale = {player: True for player in range(1, world.players + 1)} for player in range(1, world.players + 1): diff --git a/Rom.py b/Rom.py index 3d88033da6..95f22fb18e 100644 --- a/Rom.py +++ b/Rom.py @@ -858,6 +858,18 @@ def patch_rom(world, player, rom): rom.write_byte(0x18302C, 0x18) # starting max health rom.write_byte(0x18302D, 0x18) # starting current health rom.write_byte(0x183039, 0x68) # starting abilities, bit array + + for item in world.precollected_items: + if item.player != player: + continue + + if item.name == 'Fighter Sword': + rom.write_byte(0x183000+0x19, 0x01) + rom.write_byte(0x0271A6+0x19, 0x01) + rom.write_byte(0x180043, 0x01) # special starting sword byte + else: + raise RuntimeError("Unsupported pre-collected item: {}".format(item)) + rom.write_byte(0x18004A, 0x00 if world.mode != 'inverted' else 0x01) # Inverted mode rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier rom.write_byte(0x2AF79, 0xD0 if world.mode != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both) diff --git a/Rules.py b/Rules.py index 116c414864..b193f4d92f 100644 --- a/Rules.py +++ b/Rules.py @@ -957,6 +957,7 @@ def standard_rules(world, player): def uncle_item_rule(item): copy_state = CollectionState(world) copy_state.collect(item) + copy_state.sweep_for_events() return copy_state.can_reach('Sanctuary', 'Region', player) add_item_rule(world.get_location('Link\'s Uncle', player), uncle_item_rule) From 418568df60fd22ef681565c775c0253969be49b8 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 10 Aug 2019 19:37:26 -0400 Subject: [PATCH 23/52] Add Item functionality setting and progressive bow Progressive bows still have issue for silvers hint --- BaseClasses.py | 19 ++++++++++++------- EntranceRandomizer.py | 7 +++++++ ItemList.py | 35 +++++++++++++++++++++++++++++------ Main.py | 6 +++--- README.md | 6 ++++++ Rom.py | 30 +++++++++++++++++++----------- 6 files changed, 76 insertions(+), 27 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index cdee826a8a..1195010caa 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -8,13 +8,14 @@ from Utils import int16_as_bytes class World(object): - def __init__(self, players, shuffle, logic, mode, swords, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, accessibility, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints): + def __init__(self, players, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, place_dungeon_items, accessibility, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints): self.players = players self.shuffle = shuffle self.logic = logic self.mode = mode self.swords = swords self.difficulty = difficulty + self.difficulty_adjustments = difficulty_adjustments self.timer = timer self.progressive = progressive self.goal = goal @@ -166,9 +167,9 @@ class World(object): elif 'Bow' in item.name: if ret.has('Silver Arrows', item.player): pass - elif ret.has('Bow', item.player): + elif ret.has('Bow', item.player) and self.difficulty_requirements.progressive_bow_limit >= 2: ret.prog_items.add(('Silver Arrows', item.player)) - else: + elif self.difficulty_requirements.progressive_bow_limit >= 1: ret.prog_items.add(('Bow', item.player)) elif item.name.startswith('Bottle'): if ret.bottle_count(item.player) < self.difficulty_requirements.progressive_bottle_limit: @@ -403,10 +404,11 @@ class CollectionState(object): def heart_count(self, player): # Warning: This only considers items that are marked as advancement items + diff = self.world.difficulty_requirements return ( - self.item_count('Boss Heart Container', player) + min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) + self.item_count('Sanctuary Heart Container', player) - + self.item_count('Piece of Heart', player) // 4 + + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 + 3 # starting hearts ) @@ -420,9 +422,9 @@ class CollectionState(object): elif self.has('Half Magic', player): basemagic = 16 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_adjustments == 'hard' and not fullrefill: basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player)) - elif self.world.difficulty == 'expert' and not fullrefill: + elif self.world.difficulty_adjustments == 'expert' and not fullrefill: basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count(player)) else: basemagic = basemagic + basemagic * self.bottle_count(player) @@ -1019,6 +1021,7 @@ class Spoiler(object): 'shuffle': self.world.shuffle, 'algorithm': self.world.algorithm, 'difficulty': self.world.difficulty, + 'difficulty_mode': self.world.difficulty_adjustments, 'timer': self.world.timer, 'progressive': self.world.progressive, 'accessibility': self.world.accessibility, @@ -1052,6 +1055,8 @@ class Spoiler(object): outfile.write('Logic: %s\n' % self.metadata['logic']) outfile.write('Mode: %s\n' % self.metadata['mode']) outfile.write('Goal: %s\n' % self.metadata['goal']) + outfile.write('Difficulty: %s\n' % self.metadata['difficulty']) + outfile.write('Item Functionality: %s\n' % self.metadata['difficulty_mode']) outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle']) outfile.write('Filling Algorithm: %s\n' % self.metadata['algorithm']) outfile.write('Accessibility: %s\n' % self.metadata['accessibility']) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 1803dd73ad..71c739e79b 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -74,6 +74,13 @@ def start(): Hard: A harder setting with less equipment and reduced health. Expert: A harder yet setting with minimum equipment and health. ''') + parser.add_argument('--item_functionality', default='normal', const='normal', nargs='?', choices=['normal', 'hard', 'expert'], + help='''\ + Select limits on item functionality to increase difficulty. (default: %(default)s) + Normal: Normal functionality. + Hard: Reduced functionality. + Expert: Greatly reduced functionality. + ''') parser.add_argument('--timer', default='none', const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'], help='''\ Select game timer setting. Affects available itempool. (default: %(default)s) diff --git a/ItemList.py b/ItemList.py index f72d217999..d8df5fbd2a 100644 --- a/ItemList.py +++ b/ItemList.py @@ -13,7 +13,7 @@ from Items import ItemFactory #This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space. #Some basic items that various modes require are placed here, including pendants and crystals. Medallion requirements for the two relevant entrances are also decided. -alwaysitems = ['Bombos', 'Book of Mudora', 'Bow', 'Cane of Somaria', 'Ether', 'Fire Rod', 'Flippers', 'Ocarina', 'Hammer', 'Hookshot', 'Ice Rod', 'Lamp', +alwaysitems = ['Bombos', 'Book of Mudora', 'Cane of Somaria', 'Ether', 'Fire Rod', 'Flippers', 'Ocarina', 'Hammer', 'Hookshot', 'Ice Rod', 'Lamp', 'Cape', 'Magic Powder', 'Mushroom', 'Pegasus Boots', 'Quake', 'Shovel', 'Bug Catching Net', 'Cane of Byrna', 'Blue Boomerang', 'Red Boomerang'] progressivegloves = ['Progressive Glove'] * 2 basicgloves = ['Power Glove', 'Titans Mitts'] @@ -21,7 +21,7 @@ basicgloves = ['Power Glove', 'Titans Mitts'] normalbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)'] hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)', 'Bottle (Good Bee)'] -normalbaseitems = (['Silver Arrows', 'Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrows (10)', 'Bombs (10)'] + +normalbaseitems = (['Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrows (10)', 'Bombs (10)'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24) normalfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Arrows (10)'] * 6 + ['Bombs (3)'] * 6 normalsecond15extra = ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] @@ -32,10 +32,11 @@ normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', - 'progressivesword', 'basicsword', 'timedohko', 'timedother', + 'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother', 'triforcehunt', 'triforce_pieces_required', 'retro', 'extras', 'progressive_sword_limit', 'progressive_shield_limit', - 'progressive_armor_limit', 'progressive_bottle_limit']) + 'progressive_armor_limit', 'progressive_bottle_limit', + 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) total_items_to_place = 153 @@ -52,6 +53,7 @@ difficulties = { swordless = ['Rupees (20)'] * 4, progressivesword = ['Progressive Sword'] * 3, basicsword = ['Master Sword', 'Tempered Sword', 'Golden Sword'], + basicbow = ['Bow', 'Silver Arrows'], timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, @@ -61,7 +63,10 @@ difficulties = { progressive_sword_limit = 4, progressive_shield_limit = 3, progressive_armor_limit = 2, + progressive_bow_limit = 2, progressive_bottle_limit = 4, + boss_heart_container_limit = 255, + heart_piece_limit = 255, ), 'hard': Difficulty( baseitems = normalbaseitems, @@ -71,10 +76,11 @@ difficulties = { progressiveshield = ['Progressive Shield'] * 3, basicshield = ['Blue Shield', 'Red Shield', 'Red Shield'], progressivearmor = ['Progressive Armor'] * 2, - basicarmor = ['Progressive Armor'] * 2, #only the first one will upgrade, making this equivalent to two blue mail + basicarmor = ['Progressive Armor'] * 2, # neither will count swordless = ['Rupees (20)'] * 4, progressivesword = ['Progressive Sword'] * 3, basicsword = ['Master Sword', 'Master Sword', 'Tempered Sword'], + basicbow = ['Bow'] * 2, timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, @@ -83,8 +89,11 @@ difficulties = { extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 3, progressive_shield_limit = 2, - progressive_armor_limit = 1, + progressive_armor_limit = 0, + progressive_bow_limit = 1, progressive_bottle_limit = 4, + boss_heart_container_limit = 6, + heart_piece_limit = 16, ), 'expert': Difficulty( baseitems = normalbaseitems, @@ -98,6 +107,7 @@ difficulties = { swordless = ['Rupees (20)'] * 4, progressivesword = ['Progressive Sword'] * 3, basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'], + basicbow = ['Bow'] * 2, timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, @@ -107,7 +117,10 @@ difficulties = { progressive_sword_limit = 2, progressive_shield_limit = 1, progressive_armor_limit = 0, + progressive_bow_limit = 1, progressive_bottle_limit = 4, + boss_heart_container_limit = 2, + heart_piece_limit = 8, ), } @@ -366,8 +379,18 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r else: pool.extend(diff.basicarmor) + if swords != 'swordless': + if want_progressives(): + pool.extend(['Progressive Bow'] * 2) + else: + pool.extend(diff.basicbow) + if swords == 'swordless': pool.extend(diff.swordless) + if want_progressives(): + pool.extend(['Progressive Bow'] * 2) + else: + pool.extend(['Bow', 'Silver Arrows']) elif swords == 'assured': precollected_items.append('Fighter Sword') if want_progressives(): diff --git a/Main.py b/Main.py index 8109615228..69cb831c37 100644 --- a/Main.py +++ b/Main.py @@ -24,7 +24,7 @@ def main(args, seed=None): start = time.clock() # initialize the world - world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.accessibility, 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.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.accessibility, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints) logger = logging.getLogger('') if seed is None: random.seed(None) @@ -117,7 +117,7 @@ def main(args, seed=None): else: sprite = 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_%s' % (world.logic, world.difficulty, world.difficulty_adjustments, 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) use_enemizer = args.enemizercli and (args.shufflebosses != 'none' or args.shuffleenemies or args.enemy_health != 'default' or args.enemy_health != 'default' or args.enemy_damage or args.shufflepalette or args.shufflepots) @@ -180,7 +180,7 @@ def gt_filler(world): def copy_world(world): # ToDo: Not good yet - ret = World(world.players, world.shuffle, world.logic, world.mode, world.swords, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.accessibility, 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.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.accessibility, 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 = world.required_medallions.copy() ret.swamp_patch_required = world.swamp_patch_required.copy() ret.ganon_at_pyramid = world.ganon_at_pyramid.copy() diff --git a/README.md b/README.md index d1be5fc06b..b0628d1462 100644 --- a/README.md +++ b/README.md @@ -348,6 +348,12 @@ Select the game completion goal. (default: ganon) Select the game difficulty. Affects available itempool. (default: normal) +``` +--item_functionality [{normal,hard,expert}] +``` + +Select limits on item functionality to increase difficulty. (default: normal) + ``` --timer [{none,display,timed,timed-ohko,ohko,timed-countdown}] ``` diff --git a/Rom.py b/Rom.py index 95f22fb18e..f5bca52cfe 100644 --- a/Rom.py +++ b/Rom.py @@ -580,8 +580,9 @@ def patch_rom(world, player, rom): GREEN_CLOCK = ItemFactory('Green Clock', player).code rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on - # handle difficulty - if world.difficulty == 'hard': + + # handle difficulty_adjustments + if world.difficulty_adjustments == 'hard': # Powdered Fairies Prize rom.write_byte(0x36DD0, 0xD8) # One Heart # potion heal amount @@ -599,9 +600,7 @@ def patch_rom(world, player, rom): rom.write_int16(0x180036, world.rupoor_cost) # Set stun items rom.write_byte(0x180180, 0x02) # Hookshot only - # Make silver arrows only usable against Ganon - rom.write_byte(0x180181, 0x01) - elif world.difficulty == 'expert': + elif world.difficulty_adjustments == 'expert': # Powdered Fairies Prize rom.write_byte(0x36DD0, 0xD8) # One Heart # potion heal amount @@ -619,8 +618,6 @@ def patch_rom(world, player, rom): rom.write_int16(0x180036, world.rupoor_cost) # Set stun items rom.write_byte(0x180180, 0x00) # Nothing - # Make silver arrows only usable against Ganon - rom.write_byte(0x180181, 0x01) else: # Powdered Fairies Prize rom.write_byte(0x36DD0, 0xE3) # fairy @@ -638,20 +635,28 @@ def patch_rom(world, player, rom): rom.write_int16(0x180036, world.rupoor_cost) # Set stun items rom.write_byte(0x180180, 0x03) # All standard items - # Make silver arrows freely usable - rom.write_byte(0x180181, 0x00) #Set overflow items for progressive equipment if world.timer in ['timed', 'timed-countdown', 'timed-ohko']: overflow_replacement = GREEN_CLOCK else: overflow_replacement = GREEN_TWENTY_RUPEES + rom.write_byte(0x180181, 0x00) # Make silver arrows freely usable rom.write_byte(0x180182, 0x01) # auto equip silvers on pickup #Byrna residual magic cost rom.write_bytes(0x45C42, [0x04, 0x02, 0x01]) difficulty = world.difficulty_requirements + + if difficulty.progressive_bow_limit < 2 and world.swords == 'swordless': + # TODO: write 2 to progressive bow limit byte + rom.write_byte(0x180181, 0x01) # Make silver arrows work on on ganon + else: + # TODO: write difficulty.progressive_bow_limit to progressive bow limit byte + pass + + #Set overflow items for progressive equipment rom.write_bytes(0x180090, [difficulty.progressive_sword_limit, overflow_replacement, @@ -686,7 +691,7 @@ def patch_rom(world, player, rom): random.shuffle(packs) prizes[:56] = [drop for pack in packs for drop in pack] - if world.difficulty in ['hard', 'expert']: + if world.difficulty_adjustments in ['hard', 'expert']: prize_replacements = {0xE0: 0xDF, # Fairy -> heart 0xE3: 0xD8} # Big magic -> small magic prizes = [prize_replacements.get(prize, prize) for prize in prizes] @@ -735,6 +740,9 @@ def patch_rom(world, player, rom): 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees 0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade + 0x58, 0x01, 0x36 if world.retro else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode) + 0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20 + 0x17, difficulty.heart_piece_limit, 0x47, 0xff, # piece of heart -> green 20 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel ]) @@ -804,7 +812,7 @@ def patch_rom(world, player, rom): rom.write_int32(0x180200, -100 * 60 * 60 * 60) # red clock adjustment time (in frames, sint32) rom.write_int32(0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32) rom.write_int32(0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32) - if world.difficulty == 'normal': + if world.difficulty_adjustments == 'normal': rom.write_int32(0x18020C, (10 + ERtimeincrease) * 60 * 60) # starting time (in frames, sint32) else: rom.write_int32(0x18020C, int((5 + ERtimeincrease / 2) * 60 * 60)) # starting time (in frames, sint32) From 0377d6b7bce012355f856b966b2890f78855f070 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 11 Aug 2019 08:55:38 -0400 Subject: [PATCH 24/52] Finish support for variable crystal requirements Note: We still do not have anything to reveal required Ganon crystal counts in inverted mode. For non-inverted it is revealed at a sign on the pyramid, which might be less than ideal. --- EntranceRandomizer.py | 17 +++++++++++++++++ Main.py | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 71c739e79b..46908a9b8a 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -162,6 +162,23 @@ def start(): The dungeon variants only mix up dungeons and keep the rest of the overworld vanilla. ''') + parser.add_argument('--crystals_ganon', default='7', const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'], + help='''\ + How many crystals are needed to defeat ganon. Any other + requirements for ganon for the selected goal still apply. + This setting does not apply when the all dungeons goal is + selected. (default: %(default)s) + Random: Picks a random value between 0 and 7 (inclusive). + 0-7: Number of crystals needed + ''') + parser.add_argument('--crystals_gt', default='7', const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'], + help='''\ + How many crystals are needed to open GT. For inverted mode + this applies to the castle tower door instead. (default: %(default)s) + Random: Picks a random value between 0 and 7 (inclusive). + 0-7: Number of crystals needed + ''') + parser.add_argument('--rom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.') parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.') parser.add_argument('--seed', help='Define seed number to generate.', type=int) diff --git a/Main.py b/Main.py index 69cb831c37..2ff95183c1 100644 --- a/Main.py +++ b/Main.py @@ -33,6 +33,9 @@ def main(args, seed=None): world.seed = int(seed) random.seed(world.seed) + world.crystals_needed_for_ganon = random.randint(0, 7) if args.crystals_ganon == 'random' else int(args.crystals_ganon) + world.crystals_needed_for_gt = random.randint(0, 7) if args.crystals_gt == 'random' else int(args.crystals_gt) + 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) @@ -200,6 +203,8 @@ def copy_world(world): ret.difficulty_requirements = world.difficulty_requirements ret.fix_fake_world = world.fix_fake_world ret.lamps_needed_for_dark_rooms = world.lamps_needed_for_dark_rooms + ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon + ret.crystals_needed_for_gt = world.crystals_needed_for_gt if world.mode != 'inverted': for player in range(1, world.players + 1): From c042442d98c37e84e5444c467a09e237e22272e6 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Thu, 8 Aug 2019 00:05:58 -0400 Subject: [PATCH 25/52] Pre bomb TR doors in inverted --- BaseClasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 1195010caa..be6397fdd6 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -50,7 +50,7 @@ class World(object): self.rupoor_cost = 10 self.aga_randomness = True self.lock_aga_door_in_escape = False - self.fix_trock_doors = self.shuffle != 'vanilla' + self.fix_trock_doors = self.shuffle != 'vanilla' or self.mode == 'inverted' self.save_and_quit_from_boss = True self.accessibility = accessibility self.fix_skullwoods_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] From 46be1fb4825e474300404242503e89c78bc64fe3 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Thu, 8 Aug 2019 00:48:27 -0400 Subject: [PATCH 26/52] Fix Old Man Cave Exit region connections --- EntranceShuffle.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 5718dd864a..d57b941a37 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -3288,8 +3288,8 @@ default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'), ('Old Man Cave (West)', 'Old Man Cave'), ('Old Man Cave (East)', 'Old Man Cave'), - ('Old Man Cave Exit (West)', 'Death Mountain'), - ('Old Man Cave Exit (East)', 'Light World'), + ('Old Man Cave Exit (West)', 'Light World'), + ('Old Man Cave Exit (East)', 'Death Mountain'), ('Old Man House (Bottom)', 'Old Man House'), ('Old Man House Exit (Bottom)', 'Death Mountain'), ('Old Man House (Top)', 'Old Man House Back'), @@ -3499,8 +3499,8 @@ inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing' ('Inverted Dark Sanctuary', 'Inverted Dark Sanctuary'), ('Old Man Cave (West)', 'Bumper Cave'), ('Old Man Cave (East)', 'Death Mountain Return Cave'), - ('Old Man Cave Exit (West)', 'Dark Death Mountain'), - ('Old Man Cave Exit (East)', 'East Dark World'), + ('Old Man Cave Exit (West)', 'West Dark World'), + ('Old Man Cave Exit (East)', 'Dark Death Mountain'), ('Dark Death Mountain Fairy', 'Old Man Cave'), ('Bumper Cave (Bottom)', 'Old Man Cave'), ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'), From 990eab4e5543006ff0895cea3631c4b9b1de4e34 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 17 Aug 2019 15:11:25 -0400 Subject: [PATCH 27/52] Fix broken retro mode --- Main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Main.py b/Main.py index 2ff95183c1..786a91a1a5 100644 --- a/Main.py +++ b/Main.py @@ -263,6 +263,7 @@ def copy_world(world): def copy_dynamic_regions_and_locations(world, ret): for region in world.dynamic_regions: new_reg = Region(region.name, region.type, region.hint_text, region.player) + new_reg.world = ret ret.regions.append(new_reg) ret.dynamic_regions.append(new_reg) @@ -273,9 +274,11 @@ def copy_dynamic_regions_and_locations(world, ret): ret.shops.append(new_reg.shop) for location in world.dynamic_locations: - 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, location.parent_region.player) + new_loc = Location(location.player, location.name, location.address, location.crystal, location.hint_text, new_reg) new_reg.locations.append(new_loc) + + ret.clear_location_cache() def create_playthrough(world): From 72d4e6c76a23d1b7c1f449135c10c7730fbf852e Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 17 Aug 2019 15:12:12 -0400 Subject: [PATCH 28/52] Fix standard mode for madness+ --- EntranceShuffle.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index d57b941a37..8e84207fa2 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -520,7 +520,7 @@ def link_entrances(world, player): dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', - 'Lumberjack Tree Cave', 'Hyrule Castle Secret Entrance Stairs'] + list(Old_Man_Entrances) + 'Lumberjack Tree Cave'] + list(Old_Man_Entrances) dw_doors = list(DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] random.shuffle(lw_doors) @@ -530,7 +530,7 @@ def link_entrances(world, player): dw_entrances.append('Skull Woods Second Section Door (East)') dw_entrances.append('Skull Woods First Section Door') - lw_entrances.extend(['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)']) + lw_entrances.extend(['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave']) lw_entrances_must_exits = list(LW_Dungeon_Entrances_Must_Exit) @@ -556,10 +556,11 @@ def link_entrances(world, player): # cannot move uncle cave connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) - connect_entrance(world, lw_doors.pop(), 'Hyrule Castle Secret Entrance Exit', player) + connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player) else: lw_hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) + lw_doors.append('Hyrule Castle Secret Entrance Stairs') lw_entrances.append('Hyrule Castle Secret Entrance Stairs') if not world.shuffle_ganon: @@ -608,11 +609,11 @@ def link_entrances(world, player): if world.mode == 'standard': # must connect front of hyrule castle to do escape connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) - random.shuffle(lw_entrances) - connect_exit(world, 'Hyrule Castle Exit (South)', lw_entrances.pop(), player) + connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player) mandatory_light_world.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) else: lw_doors.append('Hyrule Castle Entrance (South)') + lw_entrances.append('Hyrule Castle Entrance (South)') caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) # now let's deal with mandatory reachable stuff @@ -757,10 +758,10 @@ def link_entrances(world, player): elif world.shuffle == 'insanity': # beware ye who enter here - entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)'] + entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)'] - doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Secret Entrance Stairs'] + Old_Man_Entrances +\ + doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\ DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] +\ LW_Single_Cave_Doors + DW_Single_Cave_Doors @@ -795,10 +796,11 @@ def link_entrances(world, player): # cannot move uncle cave connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) - connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit', player) + connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player) else: hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_targets.append('Hyrule Castle Secret Entrance') + doors.append('Hyrule Castle Secret Entrance Stairs') entrances.append('Hyrule Castle Secret Entrance Stairs') caves.append('Hyrule Castle Secret Entrance Exit') @@ -826,10 +828,11 @@ def link_entrances(world, player): if world.mode == 'standard': # must connect front of hyrule castle to do escape connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) - connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop(), player) + connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player) caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) else: doors.append('Hyrule Castle Entrance (South)') + entrances.append('Hyrule Castle Entrance (South)') caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) # now let's deal with mandatory reachable stuff @@ -903,10 +906,10 @@ def link_entrances(world, player): world.fix_fake_world = False # beware ye who enter here - entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)'] + entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)'] - doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Secret Entrance Stairs'] + Old_Man_Entrances +\ + doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\ DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] random.shuffle(doors) @@ -928,10 +931,11 @@ def link_entrances(world, player): # cannot move uncle cave connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player) - connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit', player) + connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player) else: hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_targets.append('Hyrule Castle Secret Entrance') + doors.append('Hyrule Castle Secret Entrance Stairs') entrances.append('Hyrule Castle Secret Entrance Stairs') caves.append('Hyrule Castle Secret Entrance Exit') @@ -959,10 +963,11 @@ def link_entrances(world, player): if world.mode == 'standard': # must connect front of hyrule castle to do escape connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) - connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop(), player) + connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player) caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) else: doors.append('Hyrule Castle Entrance (South)') + entrances.append('Hyrule Castle Entrance (South)') caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) # now let's deal with mandatory reachable stuff From c0acfdd81eafc236f2cf3002fcedb685280357cc Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Wed, 21 Aug 2019 22:40:19 -0400 Subject: [PATCH 29/52] New silver arrow hints Supporting progressive bows. --- Rom.py | 29 +++++++++++++++++++++++++++-- Text.py | 14 +++++++++----- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Rom.py b/Rom.py index f5bca52cfe..785182b213 100644 --- a/Rom.py +++ b/Rom.py @@ -434,6 +434,14 @@ class Sprite(object): def patch_rom(world, player, rom): random.seed(world.rom_seeds[player]) + + # progressive bow silver arrow hint hack + prog_bow_locs = world.find_items('Progressive Bow', player) + if len(prog_bow_locs) > 1: + # only pick a distingushed bow if we have at least two + distinguished_prog_bow_loc = random.choice(prog_bow_locs) + distinguished_prog_bow_loc.item.code = 0x65 + # patch items for location in world.get_locations(): if location.player != player: @@ -1321,11 +1329,28 @@ def write_strings(rom, world, player): for location in hint_locations: 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', player) random.shuffle(silverarrows) 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_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint + + prog_bow_locs = world.find_items('Progressive Bow', player) + distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None) + if distinguished_prog_bow_loc: + prog_bow_locs.remove(distinguished_prog_bow_loc) + silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) + tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint + + if any(prog_bow_locs): + silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my')) + tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint + + + silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' + crystal5 = world.find_items('Crystal 5', player)[0] crystal6 = world.find_items('Crystal 6', player)[0] diff --git a/Text.py b/Text.py index 7cafee7d72..a5e03bdaf9 100644 --- a/Text.py +++ b/Text.py @@ -535,7 +535,7 @@ class MultiByteCoreTextMapper(object): "{INTRO}": [0x6E, 0x00, 0x77, 0x07, 0x7A, 0x03, 0x6B, 0x02, 0x67], "{NOTEXT}": [0x6E, 0x00, 0x6B, 0x04], "{IBOX}": [0x6B, 0x02, 0x77, 0x07, 0x7A, 0x03], - "{C:GREEN}": [0x77, 0x07], + "{C:GREEN}": [0x77, 0x07], "{C:YELLOW}": [0x77, 0x02], } @@ -552,10 +552,10 @@ class MultiByteCoreTextMapper(object): linespace = wrap line = lines.pop(0) if line.startswith('{'): - if line == '{PAGEBREAK}': - if lineindex % 3 != 0: - # insert a wait for keypress, unless we just did so - outbuf.append(0x7E) + if line == '{PAGEBREAK}': + if lineindex % 3 != 0: + # insert a wait for keypress, unless we just did so + outbuf.append(0x7E) lineindex = 0 outbuf.extend(cls.special_commands[line]) continue @@ -1885,5 +1885,9 @@ class TextTable(object): text['fish_money'] = CompressedTextMapper.convert("It's a secret to everyone.") text['sign_ganons_tower'] = CompressedTextMapper.convert("You need all 7 crystals to enter.") text['sign_ganon'] = CompressedTextMapper.convert("You need all 7 crystals to beat Ganon.") + text['ganon_phase_3_no_bow'] = CompressedTextMapper.convert("You have no bow. Dingus!") + text['ganon_phase_3_no_silvers_alt'] = CompressedTextMapper.convert("You can't best me without silver arrows!") + text['ganon_phase_3_no_silvers'] = CompressedTextMapper.convert("You can't best me without silver arrows!") + text['ganon_phase_3_silvers'] = CompressedTextMapper.convert("Oh no! Silver! My one true weakness!") text['end_pad_data'] = bytearray([0xfb]) text['terminator'] = bytearray([0xFF, 0xFF]) From d8c28b733aa059ee4198e163a7c3583ba487a673 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Sun, 18 Aug 2019 15:22:13 -0400 Subject: [PATCH 30/52] Change to Bosses.py for inverted enemizer compatibility --- Bosses.py | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/Bosses.py b/Bosses.py index 8cdaee5a51..30cd56dada 100644 --- a/Bosses.py +++ b/Bosses.py @@ -141,21 +141,39 @@ def place_bosses(world, player): if world.boss_shuffle == 'none': return # Most to least restrictive order - boss_locations = [ - ['Ganons Tower', 'top'], - ['Tower of Hera', None], - ['Skull Woods', None], - ['Ganons Tower', 'middle'], - ['Eastern Palace', None], - ['Desert Palace', None], - ['Palace of Darkness', None], - ['Swamp Palace', None], - ['Thieves Town', None], - ['Ice Palace', None], - ['Misery Mire', None], - ['Turtle Rock', None], - ['Ganons Tower', 'bottom'], - ] + if world.mode != 'inverted': + boss_locations = [ + ['Ganons Tower', 'top'], + ['Tower of Hera', None], + ['Skull Woods', None], + ['Ganons Tower', 'middle'], + ['Eastern Palace', None], + ['Desert Palace', None], + ['Palace of Darkness', None], + ['Swamp Palace', None], + ['Thieves Town', None], + ['Ice Palace', None], + ['Misery Mire', None], + ['Turtle Rock', None], + ['Ganons Tower', 'bottom'], + ] + else: + boss_locations = [ + ['Inverted Ganons Tower', 'top'], + ['Tower of Hera', None], + ['Skull Woods', None], + ['Inverted Ganons Tower', 'middle'], + ['Eastern Palace', None], + ['Desert Palace', None], + ['Palace of Darkness', None], + ['Swamp Palace', None], + ['Thieves Town', None], + ['Ice Palace', None], + ['Misery Mire', None], + ['Turtle Rock', None], + ['Inverted Ganons Tower', 'bottom'], + ] + all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']] From 3d64e2bef3940143176e365769a526856a195cc1 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Fri, 23 Aug 2019 10:18:37 -0400 Subject: [PATCH 31/52] Put player on HC ledge after dying to Ganon in inverted --- EntranceShuffle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 8e84207fa2..0fafb1d236 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1749,9 +1749,9 @@ def link_inverted_entrances(world, player): world.powder_patch_required[player] = True # check for ganon location - if world.get_entrance('Inverted Pyramid Hole', player).connected_region.name != 'Hyrule Castle Ledge': + if world.get_entrance('Inverted Pyramid Hole', player).connected_region.name != 'Pyramid': world.ganon_at_pyramid[player] = False - + # check for Ganon's Tower location if world.get_entrance('Inverted Ganons Tower', player).connected_region.name != 'Ganons Tower (Entrance)': world.ganonstower_vanilla[player] = False From fe1505408ad04e36073506ce75dd803b45ea8a88 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Fri, 23 Aug 2019 21:46:49 -0400 Subject: [PATCH 32/52] Fix mirror bonking in inverted reverted a change and fixed some inverted writes I messed up initially --- Rom.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 785182b213..0a9f249d68 100644 --- a/Rom.py +++ b/Rom.py @@ -891,7 +891,7 @@ def patch_rom(world, player, rom): rom.write_byte(0x2AF79, 0xD0 if world.mode != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both) rom.write_byte(0x3A943, 0xD0 if world.mode != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) rom.write_byte(0x3A96D, 0xF0 if world.mode != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) - rom.write_byte(0x3A9A7, 0xD0 if world.mode != 'inverted' else 0xF0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) + rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) rom.write_bytes(0x180080, [50, 50, 70, 70]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10) @@ -1445,11 +1445,14 @@ def set_inverted_mode(world, rom): rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals rom.write_byte(snes_to_pc(0x02B34D), 0xF0) rom.write_byte(snes_to_pc(0x06DB78), 0x8B) + rom.write_byte(snes_to_pc(0x05AF79), 0xF0) rom.write_byte(snes_to_pc(0x0DB3C5), 0xC6) rom.write_byte(snes_to_pc(0x07A3F4), 0xF0) # duck rom.write_int16s(snes_to_pc(0x02E849), [0x0043, 0x0056, 0x0058, 0x006C, 0x006F, 0x0070, 0x007B, 0x007F, 0x001B]) # dw flute rom.write_int16(snes_to_pc(0x02E8D5), 0x07C8) rom.write_int16(snes_to_pc(0x02E8F7), 0x01F8) + rom.write_byte(0x7A943, 0xF0) + rom.write_byte(0x7A96D, 0xD0) rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof # the following bytes should only be written in vanilla # or they'll overwrite the randomizer's shuffles From ec9709f0093da837a6e6f2de462adb92fb8892fa Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 24 Aug 2019 15:35:23 -0400 Subject: [PATCH 33/52] Preopen GT for 0 crystals --- Rom.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Rom.py b/Rom.py index 0a9f249d68..2dcc4f16d4 100644 --- a/Rom.py +++ b/Rom.py @@ -860,6 +860,7 @@ def patch_rom(world, player, rom): rom.write_byte(0x50599, 0x00) # disable below ganon chest rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest rom.write_byte(0x18008B, 0x00) # Pyramid Hole not pre-opened + rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt == 0 else 0x00) # Pyramid Hole pre-opened if crystal requirement is 0 rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness From eb1b5053e56ab8b0b6d0061e12b1dbf69da864bb Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 24 Aug 2019 15:35:58 -0400 Subject: [PATCH 34/52] update json spoiler meta section --- BaseClasses.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index be6397fdd6..792804a68c 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1013,24 +1013,17 @@ class Spoiler(object): from Main import __version__ as ERVersion self.metadata = {'version': ERVersion, - 'seed': self.world.seed, 'logic': self.world.logic, 'mode': self.world.mode, - 'swords': self.world.swords, + 'weapons': self.world.swords, 'goal': self.world.goal, 'shuffle': self.world.shuffle, - 'algorithm': self.world.algorithm, - 'difficulty': self.world.difficulty, - 'difficulty_mode': self.world.difficulty_adjustments, - 'timer': self.world.timer, - 'progressive': self.world.progressive, + 'item_pool': self.world.difficulty, + 'item_functionality': self.world.difficulty_adjustments, 'accessibility': self.world.accessibility, - 'dungeonitems': self.world.place_dungeon_items, - 'quickswap': self.world.quickswap, - 'fastmenu': self.world.fastmenu, - 'disable_music': self.world.disable_music, + 'hints': self.world.hints, 'keysanity': self.world.keysanity, - 'players': self.world.players} + } def to_json(self): self.parse_data() From 6a6058adb31bb9548c2786a938e16f320a80aa20 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 24 Aug 2019 15:36:54 -0400 Subject: [PATCH 35/52] Update goal sign/ganon taunts --- Rom.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index 2dcc4f16d4..2794a703cd 100644 --- a/Rom.py +++ b/Rom.py @@ -1363,6 +1363,9 @@ def write_strings(rom, world, player): tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt == 1 else 'You need %d crystals to enter.') % world.crystals_needed_for_gt tt['sign_ganon'] = ('You need %d crystal to beat Ganon.' if world.crystals_needed_for_ganon == 1 else 'You need %d crystals to beat Ganon.') % world.crystals_needed_for_ganon + if world.goal in ['dungeons']: + tt['sign_ganon'] = 'You need to complete all the dungeons.' + tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)] tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)] tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)] @@ -1371,13 +1374,19 @@ def write_strings(rom, world, player): tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[random.randint(0, len(Sahasrahla2_texts) - 1)] tt['blind_by_the_light'] = Blind_texts[random.randint(0, len(Blind_texts) - 1)] - if world.goal in ['pedestal', 'triforcehunt']: - tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me!' + if world.goal in ['triforcehunt']: + tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' + tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invuinvincible!' + elif world.goal in ['pedestal']: + tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.' + tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' + tt['sign_ganon'] = 'You need to get to the pedestal... Ganon is invincible!' else: tt['ganon_fall_in'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)] tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!' 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)] pedestalitem = world.get_location('Master Sword Pedestal', player).item From e29f39c58531fa3968db75ff2c8ad34cbc8e8881 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 24 Aug 2019 15:38:22 -0400 Subject: [PATCH 36/52] Mark as pre-release --- Main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Main.py b/Main.py index 786a91a1a5..bba6c61a7a 100644 --- a/Main.py +++ b/Main.py @@ -18,7 +18,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute from ItemList import generate_itempool, difficulties, fill_prizes from Utils import output_path -__version__ = '0.6.2' +__version__ = '0.6.3-pre' def main(args, seed=None): start = time.clock() From df6bf6f99c779ce0ad65d2083c38e5b224b61cdb Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sat, 24 Aug 2019 15:53:21 -0400 Subject: [PATCH 37/52] Fix errors --- BaseClasses.py | 16 ++++++++-------- Rom.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 792804a68c..4715fff635 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1044,20 +1044,20 @@ class Spoiler(object): def to_file(self, filename): self.parse_data() with open(filename, 'w') as outfile: - outfile.write('ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (self.metadata['version'], self.metadata['seed'])) + outfile.write('ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (self.metadata['version'], self.world.seed)) outfile.write('Logic: %s\n' % self.metadata['logic']) outfile.write('Mode: %s\n' % self.metadata['mode']) outfile.write('Goal: %s\n' % self.metadata['goal']) - outfile.write('Difficulty: %s\n' % self.metadata['difficulty']) - outfile.write('Item Functionality: %s\n' % self.metadata['difficulty_mode']) + outfile.write('Difficulty: %s\n' % self.metadata['item_pool']) + outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality']) outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle']) - outfile.write('Filling Algorithm: %s\n' % self.metadata['algorithm']) + outfile.write('Filling Algorithm: %s\n' % self.world.algorithm) outfile.write('Accessibility: %s\n' % self.metadata['accessibility']) - 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('Menu speed: %s\n' % self.metadata['fastmenu']) + outfile.write('Maps and Compasses in Dungeons: %s\n' % ('Yes' if self.world.place_dungeon_items else 'No')) + outfile.write('L\\R Quickswap enabled: %s\n' % ('Yes' if self.world.quickswap else 'No')) + outfile.write('Menu speed: %s\n' % self.world.fastmenu) outfile.write('Keysanity enabled: %s\n' % ('Yes' if self.metadata['keysanity'] else 'No')) - outfile.write('Players: %d' % self.metadata['players']) + outfile.write('Players: %d' % self.world.players) if self.entrances: outfile.write('\n\nEntrances:\n\n') outfile.write('\n'.join(['%s%s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players >1 else '', entry['entrance'], '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', entry['exit']) for entry in self.entrances.values()])) diff --git a/Rom.py b/Rom.py index 2794a703cd..357d7e5449 100644 --- a/Rom.py +++ b/Rom.py @@ -1026,7 +1026,7 @@ def patch_rom(world, player, rom): # set rom name # 21 bytes from Main import __version__ - rom.name = bytearray('ER_{0}_{1:09}\0'.format(__version__,world.seed), 'utf8') + rom.name = bytearray('ER_{0}_{1:09}\0'.format(__version__[0:7],world.seed), 'utf8') assert len(rom.name) <= 21 rom.write_bytes(0x7FC0, rom.name) From 7dfff45a84cedec72f6b4a0305e4a13293676eaf Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 25 Aug 2019 22:22:31 -0400 Subject: [PATCH 38/52] Fix typo --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 357d7e5449..f7e0e1b27f 100644 --- a/Rom.py +++ b/Rom.py @@ -1377,7 +1377,7 @@ def write_strings(rom, world, player): if world.goal in ['triforcehunt']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' - tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invuinvincible!' + tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!' elif world.goal in ['pedestal']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' From 05e5d6227db22363f2970c3bd875b623c632e60e Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 25 Aug 2019 22:24:35 -0400 Subject: [PATCH 39/52] Remove text for faster fairy potion fill --- Text.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Text.py b/Text.py index a5e03bdaf9..517ccf1819 100644 --- a/Text.py +++ b/Text.py @@ -1432,6 +1432,7 @@ class TextTable(object): 'desert_thief_question_yes', 'desert_thief_after_item_get', 'desert_thief_reassure', + 'pond_item_bottle_filled' ] for msg in messages_to_zero: From 12d7459a60dd6d00762b823b9ddf1cc3e252172a Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 25 Aug 2019 22:28:12 -0400 Subject: [PATCH 40/52] Implement progressive bow limit --- Rom.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Rom.py b/Rom.py index f7e0e1b27f..d79b84a864 100644 --- a/Rom.py +++ b/Rom.py @@ -657,20 +657,17 @@ def patch_rom(world, player, rom): difficulty = world.difficulty_requirements - if difficulty.progressive_bow_limit < 2 and world.swords == 'swordless': - # TODO: write 2 to progressive bow limit byte - rom.write_byte(0x180181, 0x01) # Make silver arrows work on on ganon - else: - # TODO: write difficulty.progressive_bow_limit to progressive bow limit byte - pass - - #Set overflow items for progressive equipment rom.write_bytes(0x180090, [difficulty.progressive_sword_limit, overflow_replacement, difficulty.progressive_shield_limit, overflow_replacement, difficulty.progressive_armor_limit, overflow_replacement, - difficulty.progressive_bottle_limit, overflow_replacement]) + difficulty.progressive_bottle_limit, overflow_replacement, + difficulty.progressive_bow_limit, overflow_replacement]) + + if difficulty.progressive_bow_limit < 2 and world.swords == 'swordless': + rom.write_bytes(0x180098, [2, overflow_replacement]) + rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon # set up game internal RNG seed for i in range(1024): From fd1074d58af354f8430f91da819f517e08af79f7 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Sun, 25 Aug 2019 00:54:08 -0400 Subject: [PATCH 41/52] Adjust swordless rules for inverted --- Rules.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Rules.py b/Rules.py index b193f4d92f..18dcff8cae 100644 --- a/Rules.py +++ b/Rules.py @@ -932,17 +932,23 @@ def open_rules(world, player): def swordless_rules(world, player): - set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Agahnim 1', player), lambda state: (state.has('Hammer', player) or state.has('Fire Rod', player) or state.can_shoot_arrows(player) or state.has('Cane of Somaria', player)) and state.has_key('Small Key (Agahnims Tower)', player, 2)) set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) - set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player) and state.has_Mirror(player)) - set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) #in swordless mode bombos pads are present in the relevant parts of ice palace set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon, player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop + if world.mode != 'inverted': + set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player) and state.has_Mirror(player)) + else: + # only need ddm access for aga tower in inverted + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) def standard_rules(world, player): add_rule(world.get_entrance('Sewers Door', player), lambda state: state.can_kill_most_things(player)) From 2bd2ae80de7feb5053364ff9d370d00fb4f9bb39 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 25 Aug 2019 22:35:03 -0400 Subject: [PATCH 42/52] Remove obsolete comment --- Rules.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Rules.py b/Rules.py index 18dcff8cae..fb609fb84a 100644 --- a/Rules.py +++ b/Rules.py @@ -34,7 +34,6 @@ def set_rules(world, player): raise NotImplementedError('Not implemented yet') if world.swords == 'swordless': - # FIXME: !!! Does not handle inverted properly swordless_rules(world, player) if world.logic == 'noglitches': From 895d274b02f64e140d79f1c60746dbd0a45b6d3f Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Sun, 25 Aug 2019 22:36:19 -0400 Subject: [PATCH 43/52] New music muting mechanism --- Rom.py | 59 +--------------------------------------------------------- 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/Rom.py b/Rom.py index d79b84a864..01d25247e0 100644 --- a/Rom.py +++ b/Rom.py @@ -1098,64 +1098,7 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr rom.write_byte(0x18004B, 0x01 if quickswap else 0x00) - music_volumes = [ - (0x00, [0xD373B, 0xD375B, 0xD90F8]), - (0x14, [0xDA710, 0xDA7A4, 0xDA7BB, 0xDA7D2]), - (0x3C, [0xD5954, 0xD653B, 0xDA736, 0xDA752, 0xDA772, 0xDA792]), - (0x50, [0xD5B47, 0xD5B5E]), - (0x54, [0xD4306]), - (0x64, [0xD6878, 0xD6883, 0xD6E48, 0xD6E76, 0xD6EFB, 0xD6F2D, 0xDA211, 0xDA35B, 0xDA37B, 0xDA38E, 0xDA39F, 0xDA5C3, 0xDA691, 0xDA6A8, 0xDA6DF]), - (0x78, [0xD2349, 0xD3F45, 0xD42EB, 0xD48B9, 0xD48FF, 0xD543F, 0xD5817, 0xD5957, 0xD5ACB, 0xD5AE8, 0xD5B4A, 0xDA5DE, 0xDA608, 0xDA635, - 0xDA662, 0xDA71F, 0xDA7AF, 0xDA7C6, 0xDA7DD]), - (0x82, [0xD2F00, 0xDA3D5]), - (0xA0, [0xD249C, 0xD24CD, 0xD2C09, 0xD2C53, 0xD2CAF, 0xD2CEB, 0xD2D91, 0xD2EE6, 0xD38ED, 0xD3C91, 0xD3CD3, 0xD3CE8, 0xD3F0C, - 0xD3F82, 0xD405F, 0xD4139, 0xD4198, 0xD41D5, 0xD41F6, 0xD422B, 0xD4270, 0xD42B1, 0xD4334, 0xD4371, 0xD43A6, 0xD43DB, - 0xD441E, 0xD4597, 0xD4B3C, 0xD4BAB, 0xD4C03, 0xD4C53, 0xD4C7F, 0xD4D9C, 0xD5424, 0xD65D2, 0xD664F, 0xD6698, 0xD66FF, - 0xD6985, 0xD6C5C, 0xD6C6F, 0xD6C8E, 0xD6CB4, 0xD6D7D, 0xD827D, 0xD960C, 0xD9828, 0xDA233, 0xDA3A2, 0xDA49E, 0xDA72B, - 0xDA745, 0xDA765, 0xDA785, 0xDABF6, 0xDAC0D, 0xDAEBE, 0xDAFAC]), - (0xAA, [0xD9A02, 0xD9BD6]), - (0xB4, [0xD21CD, 0xD2279, 0xD2E66, 0xD2E70, 0xD2EAB, 0xD3B97, 0xD3BAC, 0xD3BE8, 0xD3C0D, 0xD3C39, 0xD3C68, 0xD3C9F, 0xD3CBC, - 0xD401E, 0xD4290, 0xD443E, 0xD456F, 0xD47D3, 0xD4D43, 0xD4DCC, 0xD4EBA, 0xD4F0B, 0xD4FE5, 0xD5012, 0xD54BC, 0xD54D5, - 0xD54F0, 0xD5509, 0xD57D8, 0xD59B9, 0xD5A2F, 0xD5AEB, 0xD5E5E, 0xD5FE9, 0xD658F, 0xD674A, 0xD6827, 0xD69D6, 0xD69F5, - 0xD6A05, 0xD6AE9, 0xD6DCF, 0xD6E20, 0xD6ECB, 0xD71D4, 0xD71E6, 0xD7203, 0xD721E, 0xD8724, 0xD8732, 0xD9652, 0xD9698, - 0xD9CBC, 0xD9DC0, 0xD9E49, 0xDAA68, 0xDAA77, 0xDAA88, 0xDAA99, 0xDAF04]), - (0x8c, [0xD1D28, 0xD1D41, 0xD1D5C, 0xD1D77, 0xD1EEE, 0xD311D, 0xD31D1, 0xD4148, 0xD5543, 0xD5B6F, 0xD65B3, 0xD6760, 0xD6B6B, - 0xD6DF6, 0xD6E0D, 0xD73A1, 0xD814C, 0xD825D, 0xD82BE, 0xD8340, 0xD8394, 0xD842C, 0xD8796, 0xD8903, 0xD892A, 0xD91E8, - 0xD922B, 0xD92E0, 0xD937E, 0xD93C1, 0xDA958, 0xDA971, 0xDA98C, 0xDA9A7]), - (0xC8, [0xD1D92, 0xD1DBD, 0xD1DEB, 0xD1F5D, 0xD1F9F, 0xD1FBD, 0xD1FDC, 0xD1FEA, 0xD20CA, 0xD21BB, 0xD22C9, 0xD2754, 0xD284C, - 0xD2866, 0xD2887, 0xD28A0, 0xD28BA, 0xD28DB, 0xD28F4, 0xD293E, 0xD2BF3, 0xD2C1F, 0xD2C69, 0xD2CA1, 0xD2CC5, 0xD2D05, - 0xD2D73, 0xD2DAF, 0xD2E3D, 0xD2F36, 0xD2F46, 0xD2F6F, 0xD2FCF, 0xD2FDF, 0xD302B, 0xD3086, 0xD3099, 0xD30A5, 0xD30CD, - 0xD30F6, 0xD3154, 0xD3184, 0xD333A, 0xD33D9, 0xD349F, 0xD354A, 0xD35E5, 0xD3624, 0xD363C, 0xD3672, 0xD3691, 0xD36B4, - 0xD36C6, 0xD3724, 0xD3767, 0xD38CB, 0xD3B1D, 0xD3B2F, 0xD3B55, 0xD3B70, 0xD3B81, 0xD3BBF, 0xD3F65, 0xD3FA6, 0xD404F, - 0xD4087, 0xD417A, 0xD41A0, 0xD425C, 0xD4319, 0xD433C, 0xD43EF, 0xD440C, 0xD4452, 0xD4494, 0xD44B5, 0xD4512, 0xD45D1, - 0xD45EF, 0xD4682, 0xD46C3, 0xD483C, 0xD4848, 0xD4855, 0xD4862, 0xD486F, 0xD487C, 0xD4A1C, 0xD4A3B, 0xD4A60, 0xD4B27, - 0xD4C7A, 0xD4D12, 0xD4D81, 0xD4E90, 0xD4ED6, 0xD4EE2, 0xD5005, 0xD502E, 0xD503C, 0xD5081, 0xD51B1, 0xD51C7, 0xD51CF, - 0xD51EF, 0xD520C, 0xD5214, 0xD5231, 0xD5257, 0xD526D, 0xD5275, 0xD52AF, 0xD52BD, 0xD52CD, 0xD52DB, 0xD549C, 0xD5801, - 0xD58A4, 0xD5A68, 0xD5A7F, 0xD5C12, 0xD5D71, 0xD5E10, 0xD5E9A, 0xD5F8B, 0xD5FA4, 0xD651A, 0xD6542, 0xD65ED, 0xD661D, - 0xD66D7, 0xD6776, 0xD68BD, 0xD68E5, 0xD6956, 0xD6973, 0xD69A8, 0xD6A51, 0xD6A86, 0xD6B96, 0xD6C3E, 0xD6D4A, 0xD6E9C, - 0xD6F80, 0xD717E, 0xD7190, 0xD71B9, 0xD811D, 0xD8139, 0xD816B, 0xD818A, 0xD819E, 0xD81BE, 0xD829C, 0xD82E1, 0xD8306, - 0xD830E, 0xD835E, 0xD83AB, 0xD83CA, 0xD83F0, 0xD83F8, 0xD844B, 0xD8479, 0xD849E, 0xD84CB, 0xD84EB, 0xD84F3, 0xD854A, - 0xD8573, 0xD859D, 0xD85B4, 0xD85CE, 0xD862A, 0xD8681, 0xD87E3, 0xD87FF, 0xD887B, 0xD88C6, 0xD88E3, 0xD8944, 0xD897B, - 0xD8C97, 0xD8CA4, 0xD8CB3, 0xD8CC2, 0xD8CD1, 0xD8D01, 0xD917B, 0xD918C, 0xD919A, 0xD91B5, 0xD91D0, 0xD91DD, 0xD9220, - 0xD9273, 0xD9284, 0xD9292, 0xD92AD, 0xD92C8, 0xD92D5, 0xD9311, 0xD9322, 0xD9330, 0xD934B, 0xD9366, 0xD9373, 0xD93B6, - 0xD97A6, 0xD97C2, 0xD97DC, 0xD97FB, 0xD9811, 0xD98FF, 0xD996F, 0xD99A8, 0xD99D5, 0xD9A30, 0xD9A4E, 0xD9A6B, 0xD9A88, - 0xD9AF7, 0xD9B1D, 0xD9B43, 0xD9B7C, 0xD9BA9, 0xD9C84, 0xD9C8D, 0xD9CAC, 0xD9CE8, 0xD9CF3, 0xD9CFD, 0xD9D46, 0xDA35E, - 0xDA37E, 0xDA391, 0xDA478, 0xDA4C3, 0xDA4D7, 0xDA4F6, 0xDA515, 0xDA6E2, 0xDA9C2, 0xDA9ED, 0xDAA1B, 0xDAA57, 0xDABAF, - 0xDABC9, 0xDABE2, 0xDAC28, 0xDAC46, 0xDAC63, 0xDACB8, 0xDACEC, 0xDAD08, 0xDAD25, 0xDAD42, 0xDAD5F, 0xDAE17, 0xDAE34, - 0xDAE51, 0xDAF2E, 0xDAF55, 0xDAF6B, 0xDAF81, 0xDB14F, 0xDB16B, 0xDB180, 0xDB195, 0xDB1AA]), - (0xD2, [0xD2B88, 0xD364A, 0xD369F, 0xD3747]), - (0xDC, [0xD213F, 0xD2174, 0xD229E, 0xD2426, 0xD4731, 0xD4753, 0xD4774, 0xD4795, 0xD47B6, 0xD4AA5, 0xD4AE4, 0xD4B96, 0xD4CA5, - 0xD5477, 0xD5A3D, 0xD6566, 0xD672C, 0xD67C0, 0xD69B8, 0xD6AB1, 0xD6C05, 0xD6DB3, 0xD71AB, 0xD8E2D, 0xD8F0D, 0xD94E0, - 0xD9544, 0xD95A8, 0xD9982, 0xD9B56, 0xDA694, 0xDA6AB, 0xDAE88, 0xDAEC8, 0xDAEE6, 0xDB1BF]), - (0xE6, [0xD210A, 0xD22DC, 0xD2447, 0xD5A4D, 0xD5DDC, 0xDA251, 0xDA26C]), - (0xF0, [0xD945E, 0xD967D, 0xD96C2, 0xD9C95, 0xD9EE6, 0xDA5C6]), - (0xFA, [0xD2047, 0xD24C2, 0xD24EC, 0xD25A4, 0xD51A8, 0xD51E6, 0xD524E, 0xD529E, 0xD6045, 0xD81DE, 0xD821E, 0xD94AA, 0xD9A9E, - 0xD9AE4, 0xDA289]), - (0xFF, [0xD2085, 0xD21C5, 0xD5F28]) - ] - for volume, addresses in music_volumes: - for address in addresses: - rom.write_byte(address, volume if not disable_music else 0x00) + rom.write_byte(0x18021A, 1 if disable_music else 0x00) # restore Mirror sound effect volumes (for existing seeds that lack it) rom.write_byte(0xD3E04, 0xC8) From ab99e8c223183e4e24e271f2743982b57b08cac4 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Wed, 28 Aug 2019 21:12:44 -0400 Subject: [PATCH 44/52] Triforce Hunt turn-in logic --- BaseClasses.py | 2 +- ItemList.py | 14 ++++++++++++++ Main.py | 5 +++++ Rom.py | 2 ++ Text.py | 1 + 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 4715fff635..ca6044c148 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -263,7 +263,7 @@ class World(object): def has_beaten_game(self, state, player=None): if player: - return state.has('Triforce', player) or (self.goal in ['triforcehunt'] and (state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > self.treasure_hunt_count)) + return state.has('Triforce', player) else: return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1))) diff --git a/ItemList.py b/ItemList.py index d8df5fbd2a..a25423fb3f 100644 --- a/ItemList.py +++ b/ItemList.py @@ -137,6 +137,20 @@ def generate_itempool(world, player): else: world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) + if world.goal in ['triforcehunt']: + region = world.get_region('Hyrule Castle Courtyard', player) + + loc = Location(player, "Murahdahla", parent=region) + loc.access_rule = lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > state.world.treasure_hunt_count + region.locations.append(loc) + world.dynamic_locations.append(loc) + + world.clear_location_cache() + + world.push_item(loc, ItemFactory('Triforce', player), False) + loc.event = True + loc.locked = True + world.get_location('Ganon', player).event = True world.get_location('Ganon', player).locked = True world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False) diff --git a/Main.py b/Main.py index bba6c61a7a..9442c8abc0 100644 --- a/Main.py +++ b/Main.py @@ -276,6 +276,11 @@ def copy_dynamic_regions_and_locations(world, ret): for location in world.dynamic_locations: new_reg = ret.get_region(location.parent_region.name, location.parent_region.player) new_loc = Location(location.player, location.name, location.address, location.crystal, location.hint_text, new_reg) + # todo: this is potentially dangerous. later refactor so we + # can apply dynamic region rules on top of copied world like other rules + new_loc.access_rule = location.access_rule + new_loc.always_allow = location.always_allow + new_loc.item_rule = location.item_rule new_reg.locations.append(new_loc) ret.clear_location_cache() diff --git a/Rom.py b/Rom.py index 01d25247e0..2db6843845 100644 --- a/Rom.py +++ b/Rom.py @@ -837,6 +837,7 @@ def patch_rom(world, player, rom): # set up goals for treasure hunt rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon == 'Triforce Piece' else [0x0D, 0x28]) rom.write_byte(0x180167, world.treasure_hunt_count % 256) + rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled) # TODO: a proper race rom mode should be implemented, that changes the following flag, and rummages the table (or uses the future encryption feature, etc) rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed @@ -1318,6 +1319,7 @@ def write_strings(rom, world, player): tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!' + tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n{PAUSE3}\n… … …\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % world.treasure_hunt_count elif world.goal in ['pedestal']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' diff --git a/Text.py b/Text.py index 517ccf1819..ac25428738 100644 --- a/Text.py +++ b/Text.py @@ -1890,5 +1890,6 @@ class TextTable(object): text['ganon_phase_3_no_silvers_alt'] = CompressedTextMapper.convert("You can't best me without silver arrows!") text['ganon_phase_3_no_silvers'] = CompressedTextMapper.convert("You can't best me without silver arrows!") text['ganon_phase_3_silvers'] = CompressedTextMapper.convert("Oh no! Silver! My one true weakness!") + text['murahdahla'] = CompressedTextMapper.convert("Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n{PAUSE3}\n… … …\nWait! you can see me? I knew I should have\nhidden in a hollow tree.") text['end_pad_data'] = bytearray([0xfb]) text['terminator'] = bytearray([0xFF, 0xFF]) From 0a759f18d653f2575aa145ff9da4a5aee9069df4 Mon Sep 17 00:00:00 2001 From: cassidoxa <43386495+cassidoxa@users.noreply.github.com> Date: Wed, 28 Aug 2019 21:13:35 -0400 Subject: [PATCH 45/52] Undo write causing map tile glitch in EP area (#9) --- Rom.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Rom.py b/Rom.py index 2db6843845..f7960bfa50 100644 --- a/Rom.py +++ b/Rom.py @@ -1403,8 +1403,6 @@ def set_inverted_mode(world, rom): rom.write_int16s(snes_to_pc(0x02E849), [0x0043, 0x0056, 0x0058, 0x006C, 0x006F, 0x0070, 0x007B, 0x007F, 0x001B]) # dw flute rom.write_int16(snes_to_pc(0x02E8D5), 0x07C8) rom.write_int16(snes_to_pc(0x02E8F7), 0x01F8) - rom.write_byte(0x7A943, 0xF0) - rom.write_byte(0x7A96D, 0xD0) rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof # the following bytes should only be written in vanilla # or they'll overwrite the randomizer's shuffles From 80217b269e3eae009d0772147ef0ef8cce6feae6 Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Wed, 28 Aug 2019 21:38:08 -0400 Subject: [PATCH 46/52] Fix inverted triforce hunt --- ItemList.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ItemList.py b/ItemList.py index a25423fb3f..6dac7a010e 100644 --- a/ItemList.py +++ b/ItemList.py @@ -138,7 +138,10 @@ def generate_itempool(world, player): world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) if world.goal in ['triforcehunt']: - region = world.get_region('Hyrule Castle Courtyard', player) + if world.mode == 'inverted': + region = world.get_region('Light World',player) + else: + region = world.get_region('Hyrule Castle Courtyard', player) loc = Location(player, "Murahdahla", parent=region) loc.access_rule = lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > state.world.treasure_hunt_count From 7249429f69ee2aaafb08a7fb4c7ace7af2c7b26d Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Mon, 2 Sep 2019 15:33:34 -0400 Subject: [PATCH 47/52] Fix silvers hint --- Rom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index f7960bfa50..04c0a5d5b1 100644 --- a/Rom.py +++ b/Rom.py @@ -1278,17 +1278,18 @@ def write_strings(rom, world, player): random.shuffle(silverarrows) silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint + tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint prog_bow_locs = world.find_items('Progressive Bow', player) distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None) if distinguished_prog_bow_loc: prog_bow_locs.remove(distinguished_prog_bow_loc) silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) - tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint + tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint if any(prog_bow_locs): silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my')) - tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint + tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' From f2c62e87ef7af9561d1cce810ed736c93b864aca Mon Sep 17 00:00:00 2001 From: Kevin Cathcart Date: Mon, 2 Sep 2019 15:34:52 -0400 Subject: [PATCH 48/52] Update flavor text --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 04c0a5d5b1..843cabf297 100644 --- a/Rom.py +++ b/Rom.py @@ -1320,7 +1320,7 @@ def write_strings(rom, world, player): tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!' - tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n{PAUSE3}\n… … …\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % world.treasure_hunt_count + tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % world.treasure_hunt_count elif world.goal in ['pedestal']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' From f593370ec042643fc8a4be623c086b5a5dfbca4e Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Sat, 31 Aug 2019 17:41:33 -0400 Subject: [PATCH 49/52] Prevent placing incorrect bosses in GT in inverted enemizer --- Bosses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Bosses.py b/Bosses.py index 30cd56dada..2713c92c6b 100644 --- a/Bosses.py +++ b/Bosses.py @@ -119,11 +119,11 @@ def can_place_boss(world, boss, dungeon_name, level=None): if world.swords in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace': return False - if dungeon_name == 'Ganons Tower' and level == 'top': + if dungeon_name in ['Ganons Tower', 'Inverted Ganons Tower'] and level == 'top': if boss in ["Armos Knights", "Arrghus", "Blind", "Trinexx", "Lanmolas"]: return False - if dungeon_name == 'Ganons Tower' and level == 'middle': + if dungeon_name in ['Ganons Tower', 'Inverted Ganons Tower'] and level == 'middle': if boss in ["Blind"]: return False From d393657eac69a6630b424908cda676ea6eed6bc8 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Sat, 31 Aug 2019 17:54:20 -0400 Subject: [PATCH 50/52] Fix mixup of DM Return caves East and West --- EntranceShuffle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 0fafb1d236..1cd053bad6 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -3511,8 +3511,8 @@ inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing' ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'), ('Bumper Cave Exit (Top)', 'Death Mountain Return Ledge'), ('Bumper Cave Exit (Bottom)', 'Light World'), - ('Death Mountain Return Cave (East)', 'Bumper Cave'), - ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave'), + ('Death Mountain Return Cave (West)', 'Bumper Cave'), + ('Death Mountain Return Cave (East)', 'Death Mountain Return Cave'), ('Death Mountain Return Cave Exit (West)', 'Death Mountain'), ('Death Mountain Return Cave Exit (East)', 'Death Mountain'), ('Hookshot Cave Exit (South)', 'Dark Death Mountain'), From ec865e67e645eb24ce7490461630066321085da5 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Wed, 11 Sep 2019 12:25:46 -0400 Subject: [PATCH 51/52] Fix inverted retro take anys Removed 'Dark Sanctuary Hint' from take any pool if mode is inverted --- ItemList.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ItemList.py b/ItemList.py index 6dac7a010e..27eb4568ff 100644 --- a/ItemList.py +++ b/ItemList.py @@ -233,6 +233,9 @@ take_any_locations = [ 'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint'] def set_up_take_anys(world, player): + if world.mode == 'inverted' and 'Dark Sanctuary Hint' in take_any_locations: + take_any_locations.remove('Dark Sanctuary Hint') + regions = random.sample(take_any_locations, 5) old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player) From 99a4ea17b0896ff0795d14bfaedf3035319d5b0d Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Wed, 11 Sep 2019 14:24:56 -0400 Subject: [PATCH 52/52] Fix swordless vanilla shuffle TR keys issue --- Rules.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Rules.py b/Rules.py index fb609fb84a..c72d79d42c 100644 --- a/Rules.py +++ b/Rules.py @@ -33,9 +33,6 @@ def set_rules(world, player): else: raise NotImplementedError('Not implemented yet') - if world.swords == 'swordless': - swordless_rules(world, player) - if world.logic == 'noglitches': no_glitches_rules(world, player) elif world.logic == 'minorglitches': @@ -457,6 +454,9 @@ def global_rules(world, player): set_rule(world.get_entrance('Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic. + if world.swords == 'swordless': + swordless_rules(world, player) + set_trock_key_rules(world, player) set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player)) @@ -843,6 +843,9 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic. + if world.swords == 'swordless': + swordless_rules(world, player) + set_trock_key_rules(world, player) set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player))