Links Awakening: Implement New Game (#1334)

Adds Link's Awakening: DX. Fully imports and forks LADXR, with permission - https://github.com/daid/LADXR
This commit is contained in:
zig-for
2023-03-21 01:26:03 +09:00
committed by GitHub
parent 67bf12369a
commit 81a239325d
180 changed files with 24191 additions and 2 deletions

View File

@@ -0,0 +1,284 @@
from . import overworld
from . import dungeon1
from . import dungeon2
from . import dungeon3
from . import dungeon4
from . import dungeon5
from . import dungeon6
from . import dungeon7
from . import dungeon8
from . import dungeonColor
from .requirements import AND, OR, COUNT, COUNTS, FOUND, RequirementsSettings
from .location import Location
from ..locations.items import *
from ..locations.keyLocation import KeyLocation
from ..worldSetup import WorldSetup
from .. import itempool
from .. import mapgen
class Logic:
def __init__(self, configuration_options, *, world_setup):
self.world_setup = world_setup
r = RequirementsSettings(configuration_options)
if configuration_options.overworld == "dungeondive":
world = overworld.DungeonDiveOverworld(configuration_options, r)
elif configuration_options.overworld == "random":
world = mapgen.LogicGenerator(configuration_options, world_setup, r, world_setup.map)
else:
world = overworld.World(configuration_options, world_setup, r)
if configuration_options.overworld == "nodungeons":
world.updateIndoorLocation("d1", dungeon1.NoDungeon1(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d2", dungeon2.NoDungeon2(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d3", dungeon3.NoDungeon3(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d4", dungeon4.NoDungeon4(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d5", dungeon5.NoDungeon5(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d6", dungeon6.NoDungeon6(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d7", dungeon7.NoDungeon7(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d8", dungeon8.NoDungeon8(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d0", dungeonColor.NoDungeonColor(configuration_options, world_setup, r).entrance)
elif configuration_options.overworld != "random":
world.updateIndoorLocation("d1", dungeon1.Dungeon1(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d2", dungeon2.Dungeon2(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d3", dungeon3.Dungeon3(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d4", dungeon4.Dungeon4(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d5", dungeon5.Dungeon5(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d6", dungeon6.Dungeon6(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d7", dungeon7.Dungeon7(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d8", dungeon8.Dungeon8(configuration_options, world_setup, r).entrance)
world.updateIndoorLocation("d0", dungeonColor.DungeonColor(configuration_options, world_setup, r).entrance)
if configuration_options.overworld != "random":
for k in world.overworld_entrance.keys():
assert k in world_setup.entrance_mapping, k
for k in world_setup.entrance_mapping.keys():
assert k in world.overworld_entrance, k
for entrance, indoor in world_setup.entrance_mapping.items():
exterior = world.overworld_entrance[entrance]
if world.indoor_location[indoor] is not None:
exterior.location.connect(world.indoor_location[indoor], exterior.requirement)
if exterior.enterIsSet():
exterior.location.connect(world.indoor_location[indoor], exterior.one_way_enter_requirement, one_way=True)
if exterior.exitIsSet():
world.indoor_location[indoor].connect(exterior.location, exterior.one_way_exit_requirement, one_way=True)
egg_trigger = AND(OCARINA, SONG1)
if configuration_options.logic == 'glitched' or configuration_options.logic == 'hell':
egg_trigger = OR(AND(OCARINA, SONG1), BOMB)
if world_setup.goal == "seashells":
world.nightmare.connect(world.egg, COUNT(SEASHELL, 20))
elif world_setup.goal in ("raft", "bingo", "bingo-full"):
world.nightmare.connect(world.egg, egg_trigger)
else:
goal = int(world_setup.goal)
if goal < 0:
world.nightmare.connect(world.egg, None)
elif goal == 0:
world.nightmare.connect(world.egg, egg_trigger)
elif goal == 8:
world.nightmare.connect(world.egg, AND(egg_trigger, INSTRUMENT1, INSTRUMENT2, INSTRUMENT3, INSTRUMENT4, INSTRUMENT5, INSTRUMENT6, INSTRUMENT7, INSTRUMENT8))
else:
world.nightmare.connect(world.egg, AND(egg_trigger, COUNTS([INSTRUMENT1, INSTRUMENT2, INSTRUMENT3, INSTRUMENT4, INSTRUMENT5, INSTRUMENT6, INSTRUMENT7, INSTRUMENT8], goal)))
# if configuration_options.dungeon_items == 'keysy':
# for n in range(9):
# for count in range(9):
# world.start.add(KeyLocation("KEY%d" % (n + 1)))
# world.start.add(KeyLocation("NIGHTMARE_KEY%d" % (n + 1)))
self.world = world
self.start = world.start
self.windfish = world.windfish
self.location_list = []
self.iteminfo_list = []
self.__location_set = set()
self.__recursiveFindAll(self.start)
del self.__location_set
for ii in self.iteminfo_list:
ii.configure(configuration_options)
def dumpFlatRequirements(self):
def __rec(location, req):
if hasattr(location, "flat_requirements"):
new_flat_requirements = requirements.mergeFlat(location.flat_requirements, requirements.flatten(req))
if new_flat_requirements == location.flat_requirements:
return
location.flat_requirements = new_flat_requirements
else:
location.flat_requirements = requirements.flatten(req)
for connection, requirement in location.simple_connections:
__rec(connection, AND(req, requirement) if req else requirement)
for connection, requirement in location.gated_connections:
__rec(connection, AND(req, requirement) if req else requirement)
__rec(self.start, None)
for ii in self.iteminfo_list:
print(ii)
for fr in ii._location.flat_requirements:
print(" " + ", ".join(sorted(map(str, fr))))
def __recursiveFindAll(self, location):
if location in self.__location_set:
return
self.location_list.append(location)
self.__location_set.add(location)
for ii in location.items:
self.iteminfo_list.append(ii)
for connection, requirement in location.simple_connections:
self.__recursiveFindAll(connection)
for connection, requirement in location.gated_connections:
self.__recursiveFindAll(connection)
class MultiworldLogic:
def __init__(self, settings, rnd=None, *, world_setups=None):
assert rnd or world_setups
self.worlds = []
self.start = Location()
self.location_list = [self.start]
self.iteminfo_list = []
for n in range(settings.multiworld):
options = settings.multiworld_settings[n]
world = None
if world_setups:
world = Logic(options, world_setup=world_setups[n])
else:
for cnt in range(1000): # Try the world setup in case entrance randomization generates unsolvable logic
world_setup = WorldSetup()
world_setup.randomize(options, rnd)
world = Logic(options, world_setup=world_setup)
if options.entranceshuffle not in ("advanced", "expert", "insanity") or len(world.iteminfo_list) == sum(itempool.ItemPool(options, rnd).toDict().values()):
break
for ii in world.iteminfo_list:
ii.world = n
req_done_set = set()
for loc in world.location_list:
loc.simple_connections = [(target, addWorldIdToRequirements(req_done_set, n, req)) for target, req in loc.simple_connections]
loc.gated_connections = [(target, addWorldIdToRequirements(req_done_set, n, req)) for target, req in loc.gated_connections]
loc.items = [MultiworldItemInfoWrapper(n, options, ii) for ii in loc.items]
self.iteminfo_list += loc.items
self.worlds.append(world)
self.start.simple_connections += world.start.simple_connections
self.start.gated_connections += world.start.gated_connections
self.start.items += world.start.items
world.start.items.clear()
self.location_list += world.location_list
self.entranceMapping = None
class MultiworldMetadataWrapper:
def __init__(self, world, metadata):
self.world = world
self.metadata = metadata
@property
def name(self):
return self.metadata.name
@property
def area(self):
return "P%d %s" % (self.world + 1, self.metadata.area)
class MultiworldItemInfoWrapper:
def __init__(self, world, configuration_options, target):
self.world = world
self.world_count = configuration_options.multiworld
self.target = target
self.dungeon_items = configuration_options.dungeon_items
self.MULTIWORLD_OPTIONS = None
self.item = None
@property
def nameId(self):
return self.target.nameId
@property
def forced_item(self):
if self.target.forced_item is None:
return None
if "_W" in self.target.forced_item:
return self.target.forced_item
return "%s_W%d" % (self.target.forced_item, self.world)
@property
def room(self):
return self.target.room
@property
def metadata(self):
return MultiworldMetadataWrapper(self.world, self.target.metadata)
@property
def MULTIWORLD(self):
return self.target.MULTIWORLD
def read(self, rom):
world = rom.banks[0x3E][0x3300 + self.target.room] if self.target.MULTIWORLD else self.world
return "%s_W%d" % (self.target.read(rom), world)
def getOptions(self):
if self.MULTIWORLD_OPTIONS is None:
options = self.target.getOptions()
if self.target.MULTIWORLD and len(options) > 1:
self.MULTIWORLD_OPTIONS = []
for n in range(self.world_count):
self.MULTIWORLD_OPTIONS += ["%s_W%d" % (t, n) for t in options if n == self.world or self.canMultiworld(t)]
else:
self.MULTIWORLD_OPTIONS = ["%s_W%d" % (t, self.world) for t in options]
return self.MULTIWORLD_OPTIONS
def patch(self, rom, option):
idx = option.rfind("_W")
world = int(option[idx+2:])
option = option[:idx]
if not self.target.MULTIWORLD:
assert self.world == world
self.target.patch(rom, option)
else:
self.target.patch(rom, option, multiworld=world)
# Return true if the item is allowed to be placed in any world, or false if it is
# world specific for this check.
def canMultiworld(self, option):
if self.dungeon_items in {'', 'smallkeys'}:
if option.startswith("MAP"):
return False
if option.startswith("COMPASS"):
return False
if option.startswith("STONE_BEAK"):
return False
if self.dungeon_items in {'', 'localkeys'}:
if option.startswith("KEY"):
return False
if self.dungeon_items in {'', 'localkeys', 'localnightmarekey', 'smallkeys'}:
if option.startswith("NIGHTMARE_KEY"):
return False
return True
@property
def location(self):
return self.target.location
def __repr__(self):
return "W%d:%s" % (self.world, repr(self.target))
def addWorldIdToRequirements(req_done_set, world, req):
if req is None:
return None
if isinstance(req, str):
return "%s_W%d" % (req, world)
if req in req_done_set:
return req
return req.copyWithModifiedItemNames(lambda item: "%s_W%d" % (item, world))

View File

@@ -0,0 +1,46 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon1:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=1)
entrance.add(DungeonChest(0x113), DungeonChest(0x115), DungeonChest(0x10E))
Location(dungeon=1).add(DroppedKey(0x116)).connect(entrance, OR(BOMB, r.push_hardhat)) # hardhat beetles (can kill with bomb)
Location(dungeon=1).add(DungeonChest(0x10D)).connect(entrance, OR(r.attack_hookshot_powder, SHIELD)) # moldorm spawn chest
stalfos_keese_room = Location(dungeon=1).add(DungeonChest(0x114)).connect(entrance, r.attack_hookshot) # 2 stalfos 2 keese room
Location(dungeon=1).add(DungeonChest(0x10C)).connect(entrance, BOMB) # hidden seashell room
dungeon1_upper_left = Location(dungeon=1).connect(entrance, AND(KEY1, FOUND(KEY1, 3)))
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=1).add(OwlStatue(0x103), OwlStatue(0x104)).connect(dungeon1_upper_left, STONE_BEAK1)
feather_chest = Location(dungeon=1).add(DungeonChest(0x11D)).connect(dungeon1_upper_left, SHIELD) # feather location, behind spike enemies. can shield bump into pit (only shield works)
boss_key = Location(dungeon=1).add(DungeonChest(0x108)).connect(entrance, AND(FEATHER, KEY1, FOUND(KEY1, 3))) # boss key
dungeon1_right_side = Location(dungeon=1).connect(entrance, AND(KEY1, FOUND(KEY1, 3)))
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=1).add(OwlStatue(0x10A)).connect(dungeon1_right_side, STONE_BEAK1)
Location(dungeon=1).add(DungeonChest(0x10A)).connect(dungeon1_right_side, OR(r.attack_hookshot, SHIELD)) # three of a kind, shield stops the suit from changing
dungeon1_miniboss = Location(dungeon=1).connect(dungeon1_right_side, AND(r.miniboss_requirements[world_setup.miniboss_mapping[0]], FEATHER))
dungeon1_boss = Location(dungeon=1).connect(dungeon1_miniboss, NIGHTMARE_KEY1)
Location(dungeon=1).add(HeartContainer(0x106), Instrument(0x102)).connect(dungeon1_boss, r.boss_requirements[world_setup.boss_mapping[0]])
if options.logic not in ('normal', 'casual'):
stalfos_keese_room.connect(entrance, r.attack_hookshot_powder) # stalfos jump away when you press a button.
if options.logic == 'glitched' or options.logic == 'hell':
boss_key.connect(entrance, FEATHER) # super jump
dungeon1_miniboss.connect(dungeon1_right_side, r.miniboss_requirements[world_setup.miniboss_mapping[0]]) # damage boost or buffer pause over the pit to cross or mushroom
if options.logic == 'hell':
feather_chest.connect(dungeon1_upper_left, SWORD) # keep slashing the spiked beetles until they keep moving 1 pixel close towards you and the pit, to get them to fall
boss_key.connect(entrance, FOUND(KEY1,3)) # damage boost off the hardhat to cross the pit
self.entrance = entrance
class NoDungeon1:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=1)
Location(dungeon=1).add(HeartContainer(0x106), Instrument(0x102)).connect(entrance, r.boss_requirements[
world_setup.boss_mapping[0]])
self.entrance = entrance

View File

@@ -0,0 +1,62 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon2:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=2)
Location(dungeon=2).add(DungeonChest(0x136)).connect(entrance, POWER_BRACELET) # chest at entrance
dungeon2_l2 = Location(dungeon=2).connect(entrance, AND(KEY2, FOUND(KEY2, 5))) # towards map chest
dungeon2_map_chest = Location(dungeon=2).add(DungeonChest(0x12E)).connect(dungeon2_l2, AND(r.attack_hookshot_powder, OR(FEATHER, HOOKSHOT))) # map chest
dungeon2_r2 = Location(dungeon=2).connect(entrance, r.fire)
Location(dungeon=2).add(DroppedKey(0x132)).connect(dungeon2_r2, r.attack_skeleton)
Location(dungeon=2).add(DungeonChest(0x137)).connect(dungeon2_r2, AND(KEY2, FOUND(KEY2, 5), OR(r.rear_attack, r.rear_attack_range))) # compass chest
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=2).add(OwlStatue(0x133)).connect(dungeon2_r2, STONE_BEAK2)
dungeon2_r3 = Location(dungeon=2).add(DungeonChest(0x138)).connect(dungeon2_r2, r.attack_hookshot) # first chest with key, can hookshot the switch in previous room
dungeon2_r4 = Location(dungeon=2).add(DungeonChest(0x139)).connect(dungeon2_r3, FEATHER) # button spawn chest
if options.logic == "casual":
shyguy_key_drop = Location(dungeon=2).add(DroppedKey(0x134)).connect(dungeon2_r3, AND(FEATHER, OR(r.rear_attack, r.rear_attack_range))) # shyguy drop key
else:
shyguy_key_drop = Location(dungeon=2).add(DroppedKey(0x134)).connect(dungeon2_r3, OR(r.rear_attack, AND(FEATHER, r.rear_attack_range))) # shyguy drop key
dungeon2_r5 = Location(dungeon=2).connect(dungeon2_r4, AND(KEY2, FOUND(KEY2, 3))) # push two blocks together room with owl statue
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=2).add(OwlStatue(0x12F)).connect(dungeon2_r5, STONE_BEAK2) # owl statue is before miniboss
miniboss = Location(dungeon=2).add(DungeonChest(0x126)).add(DungeonChest(0x121)).connect(dungeon2_r5, AND(FEATHER, r.miniboss_requirements[world_setup.miniboss_mapping[1]])) # post hinox
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=2).add(OwlStatue(0x129)).connect(miniboss, STONE_BEAK2) # owl statue after the miniboss
dungeon2_ghosts_room = Location(dungeon=2).connect(miniboss, AND(KEY2, FOUND(KEY2, 5)))
dungeon2_ghosts_chest = Location(dungeon=2).add(DungeonChest(0x120)).connect(dungeon2_ghosts_room, OR(r.fire, BOW)) # bracelet chest
dungeon2_r6 = Location(dungeon=2).add(DungeonChest(0x122)).connect(miniboss, POWER_BRACELET)
dungeon2_boss_key = Location(dungeon=2).add(DungeonChest(0x127)).connect(dungeon2_r6, AND(r.attack_hookshot_powder, OR(BOW, BOMB, MAGIC_ROD, AND(OCARINA, SONG1), POWER_BRACELET)))
dungeon2_pre_stairs_boss = Location(dungeon=2).connect(dungeon2_r6, AND(POWER_BRACELET, KEY2, FOUND(KEY2, 5)))
dungeon2_post_stairs_boss = Location(dungeon=2).connect(dungeon2_pre_stairs_boss, POWER_BRACELET)
dungeon2_pre_boss = Location(dungeon=2).connect(dungeon2_post_stairs_boss, FEATHER)
# If we can get here, we have everything for the boss. So this is also the goal room.
dungeon2_boss = Location(dungeon=2).add(HeartContainer(0x12B), Instrument(0x12a)).connect(dungeon2_pre_boss, AND(NIGHTMARE_KEY2, r.boss_requirements[world_setup.boss_mapping[1]]))
if options.logic == 'glitched' or options.logic == 'hell':
dungeon2_ghosts_chest.connect(dungeon2_ghosts_room, SWORD) # use sword to spawn ghosts on other side of the room so they run away (logically irrelevant because of torches at start)
dungeon2_r6.connect(miniboss, FEATHER) # superjump to staircase next to hinox.
if options.logic == 'hell':
dungeon2_map_chest.connect(dungeon2_l2, AND(r.attack_hookshot_powder, PEGASUS_BOOTS)) # use boots to jump over the pits
dungeon2_r4.connect(dungeon2_r3, OR(PEGASUS_BOOTS, HOOKSHOT)) # can use both pegasus boots bonks or hookshot spam to cross the pit room
dungeon2_r4.connect(shyguy_key_drop, r.rear_attack_range, one_way=True) # adjust for alternate requirements for dungeon2_r4
miniboss.connect(dungeon2_r5, AND(PEGASUS_BOOTS, r.miniboss_requirements[world_setup.miniboss_mapping[1]])) # use boots to dash over the spikes in the 2d section
dungeon2_pre_stairs_boss.connect(dungeon2_r6, AND(HOOKSHOT, OR(BOW, BOMB, MAGIC_ROD, AND(OCARINA, SONG1)), FOUND(KEY2, 5))) # hookshot clip through the pot using both pol's voice
dungeon2_post_stairs_boss.connect(dungeon2_pre_stairs_boss, OR(BOMB, AND(PEGASUS_BOOTS, FEATHER))) # use a bomb to lower the last platform, or boots + feather to cross over top (only relevant in hell logic)
dungeon2_pre_boss.connect(dungeon2_post_stairs_boss, AND(PEGASUS_BOOTS, HOOKSHOT)) # boots bonk off bottom wall + hookshot spam across the two 1 tile pits vertically
self.entrance = entrance
class NoDungeon2:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=2)
Location(dungeon=2).add(DungeonChest(0x136)).connect(entrance, POWER_BRACELET) # chest at entrance
Location(dungeon=2).add(HeartContainer(0x12B), Instrument(0x12a)).connect(entrance, r.boss_requirements[
world_setup.boss_mapping[1]])
self.entrance = entrance

View File

@@ -0,0 +1,89 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon3:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=3)
dungeon3_reverse_eye = Location(dungeon=3).add(DungeonChest(0x153)).connect(entrance, PEGASUS_BOOTS) # Right side reverse eye
area2 = Location(dungeon=3).connect(entrance, POWER_BRACELET)
Location(dungeon=3).add(DungeonChest(0x151)).connect(area2, r.attack_hookshot_powder) # First chest with key
area2.add(DungeonChest(0x14F)) # Second chest with slime
area3 = Location(dungeon=3).connect(area2, OR(r.attack_hookshot_powder, PEGASUS_BOOTS)) # need to kill slimes to continue or pass through left path
dungeon3_zol_stalfos = Location(dungeon=3).add(DungeonChest(0x14E)).connect(area3, AND(PEGASUS_BOOTS, r.attack_skeleton)) # 3th chest requires killing the slime behind the crystal pillars
# now we can go 4 directions,
area_up = Location(dungeon=3).connect(area3, AND(KEY3, FOUND(KEY3, 8)))
dungeon3_north_key_drop = Location(dungeon=3).add(DroppedKey(0x154)).connect(area_up, r.attack_skeleton) # north key drop
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=3).add(OwlStatue(0x154)).connect(area_up, STONE_BEAK3)
dungeon3_raised_blocks_north = Location(dungeon=3).add(DungeonChest(0x14C)) # chest locked behind raised blocks near staircase
dungeon3_raised_blocks_east = Location(dungeon=3).add(DungeonChest(0x150)) # chest locked behind raised blocks next to slime chest
area_up.connect(dungeon3_raised_blocks_north, r.attack_hookshot, one_way=True) # hit switch to reach north chest
area_up.connect(dungeon3_raised_blocks_east, r.attack_hookshot, one_way=True) # hit switch to reach east chest
area_left = Location(dungeon=3).connect(area3, AND(KEY3, FOUND(KEY3, 8)))
area_left_key_drop = Location(dungeon=3).add(DroppedKey(0x155)).connect(area_left, r.attack_hookshot) # west key drop (no longer requires feather to get across hole), can use boomerang to knock owls into pit
area_down = Location(dungeon=3).connect(area3, AND(KEY3, FOUND(KEY3, 8)))
dungeon3_south_key_drop = Location(dungeon=3).add(DroppedKey(0x158)).connect(area_down, r.attack_hookshot) # south keydrop, can use boomerang to knock owls into pit
area_right = Location(dungeon=3).connect(area3, AND(KEY3, FOUND(KEY3, 4))) # We enter the top part of the map here.
Location(dungeon=3).add(DroppedKey(0x14D)).connect(area_right, r.attack_hookshot_powder) # key after the stairs.
dungeon3_nightmare_key_chest = Location(dungeon=3).add(DungeonChest(0x147)).connect(area_right, AND(BOMB, FEATHER, PEGASUS_BOOTS)) # nightmare key chest
dungeon3_post_dodongo_chest = Location(dungeon=3).add(DungeonChest(0x146)).connect(area_right, AND(r.attack_hookshot_powder, r.miniboss_requirements[world_setup.miniboss_mapping[2]])) # boots after the miniboss
compass_chest = Location(dungeon=3).add(DungeonChest(0x142)).connect(area_right, OR(SWORD, BOMB, AND(SHIELD, r.attack_hookshot_powder))) # bomb only activates with sword, bomb or shield
dungeon3_3_bombite_room = Location(dungeon=3).add(DroppedKey(0x141)).connect(compass_chest, BOMB) # 3 bombite room
Location(dungeon=3).add(DroppedKey(0x148)).connect(area_right, r.attack_no_boomerang) # 2 zol 2 owl drop key
Location(dungeon=3).add(DungeonChest(0x144)).connect(area_right, r.attack_skeleton) # map chest
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=3).add(OwlStatue(0x140), OwlStatue(0x147)).connect(area_right, STONE_BEAK3)
towards_boss1 = Location(dungeon=3).connect(area_right, AND(KEY3, FOUND(KEY3, 5)))
towards_boss2 = Location(dungeon=3).connect(towards_boss1, AND(KEY3, FOUND(KEY3, 6)))
towards_boss3 = Location(dungeon=3).connect(towards_boss2, AND(KEY3, FOUND(KEY3, 7)))
towards_boss4 = Location(dungeon=3).connect(towards_boss3, AND(KEY3, FOUND(KEY3, 8)))
# Just the whole area before the boss, requirements for the boss itself and the rooms before it are the same.
pre_boss = Location(dungeon=3).connect(towards_boss4, AND(r.attack_no_boomerang, FEATHER, PEGASUS_BOOTS))
pre_boss.add(DroppedKey(0x15B))
boss = Location(dungeon=3).add(HeartContainer(0x15A), Instrument(0x159)).connect(pre_boss, AND(NIGHTMARE_KEY3, r.boss_requirements[world_setup.boss_mapping[2]]))
if options.logic == 'hard' or options.logic == 'glitched' or options.logic == 'hell':
dungeon3_3_bombite_room.connect(area_right, BOOMERANG) # 3 bombite room from the left side, grab item with boomerang
dungeon3_reverse_eye.connect(entrance, HOOKSHOT) # hookshot the chest to get to the right side
dungeon3_north_key_drop.connect(area_up, POWER_BRACELET) # use pots to kill the enemies
dungeon3_south_key_drop.connect(area_down, POWER_BRACELET) # use pots to kill enemies
if options.logic == 'glitched' or options.logic == 'hell':
area2.connect(dungeon3_raised_blocks_east, AND(r.attack_hookshot_powder, FEATHER), one_way=True) # use superjump to get over the bottom left block
area3.connect(dungeon3_raised_blocks_north, AND(OR(PEGASUS_BOOTS, HOOKSHOT), FEATHER), one_way=True) # use shagjump (unclipped superjump next to movable block) from north wall to get on the blocks. Instead of boots can also get to that area with a hookshot clip past the movable block
area3.connect(dungeon3_zol_stalfos, HOOKSHOT, one_way=True) # hookshot clip through the northern push block next to raised blocks chest to get to the zol
dungeon3_nightmare_key_chest.connect(area_right, AND(FEATHER, BOMB)) # superjump to right side 3 gap via top wall and jump the 2 gap
dungeon3_post_dodongo_chest.connect(area_right, AND(FEATHER, FOUND(KEY3, 6))) # superjump from keyblock path. use 2 keys to open enough blocks TODO: nag messages to skip a key
if options.logic == 'hell':
area2.connect(dungeon3_raised_blocks_east, AND(PEGASUS_BOOTS, OR(BOW, MAGIC_ROD)), one_way=True) # use boots superhop to get over the bottom left block
area3.connect(dungeon3_raised_blocks_north, AND(PEGASUS_BOOTS, OR(BOW, MAGIC_ROD)), one_way=True) # use boots superhop off top wall or left wall to get on raised blocks
area_up.connect(dungeon3_zol_stalfos, AND(FEATHER, OR(BOW, MAGIC_ROD, SWORD)), one_way=True) # use superjump near top blocks chest to get to zol without boots, keep wall clip on right wall to get a clip on left wall or use obstacles
area_left_key_drop.connect(area_left, SHIELD) # knock everything into the pit including the teleporting owls
dungeon3_south_key_drop.connect(area_down, SHIELD) # knock everything into the pit including the teleporting owls
dungeon3_nightmare_key_chest.connect(area_right, AND(FEATHER, SHIELD)) # superjump into jumping stalfos and shield bump to right ledge
dungeon3_nightmare_key_chest.connect(area_right, AND(BOMB, PEGASUS_BOOTS, HOOKSHOT)) # boots bonk across the pits with pit buffering and hookshot to the chest
compass_chest.connect(dungeon3_3_bombite_room, OR(BOW, MAGIC_ROD, AND(OR(FEATHER, PEGASUS_BOOTS), OR(SWORD, MAGIC_POWDER))), one_way=True) # 3 bombite room from the left side, use a bombite to blow open the wall without bombs
pre_boss.connect(towards_boss4, AND(r.attack_no_boomerang, FEATHER, POWER_BRACELET)) # use bracelet super bounce glitch to pass through first part underground section
pre_boss.connect(towards_boss4, AND(r.attack_no_boomerang, PEGASUS_BOOTS, "MEDICINE2")) # use medicine invulnerability to pass through the 2d section with a boots bonk to reach the staircase
self.entrance = entrance
class NoDungeon3:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=3)
Location(dungeon=3).add(HeartContainer(0x15A), Instrument(0x159)).connect(entrance, AND(POWER_BRACELET, r.boss_requirements[
world_setup.boss_mapping[2]]))
self.entrance = entrance

View File

@@ -0,0 +1,81 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon4:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=4)
entrance.add(DungeonChest(0x179)) # stone slab chest
entrance.add(DungeonChest(0x16A)) # map chest
right_of_entrance = Location(dungeon=4).add(DungeonChest(0x178)).connect(entrance, AND(SHIELD, r.attack_hookshot_powder)) # 1 zol 2 spike beetles 1 spark chest
Location(dungeon=4).add(DungeonChest(0x17B)).connect(right_of_entrance, AND(SHIELD, SWORD)) # room with key chest
rightside_crossroads = Location(dungeon=4).connect(entrance, AND(FEATHER, PEGASUS_BOOTS)) # 2 key chests on the right.
pushable_block_chest = Location(dungeon=4).add(DungeonChest(0x171)).connect(rightside_crossroads, BOMB) # lower chest
puddle_crack_block_chest = Location(dungeon=4).add(DungeonChest(0x165)).connect(rightside_crossroads, OR(BOMB, FLIPPERS)) # top right chest
double_locked_room = Location(dungeon=4).connect(right_of_entrance, AND(KEY4, FOUND(KEY4, 5)), one_way=True)
right_of_entrance.connect(double_locked_room, KEY4, one_way=True)
after_double_lock = Location(dungeon=4).connect(double_locked_room, AND(KEY4, FOUND(KEY4, 4), OR(FEATHER, FLIPPERS)), one_way=True)
double_locked_room.connect(after_double_lock, AND(KEY4, FOUND(KEY4, 2), OR(FEATHER, FLIPPERS)), one_way=True)
dungeon4_puddle_before_crossroads = Location(dungeon=4).add(DungeonChest(0x175)).connect(after_double_lock, FLIPPERS)
north_crossroads = Location(dungeon=4).connect(after_double_lock, AND(FEATHER, PEGASUS_BOOTS))
before_miniboss = Location(dungeon=4).connect(north_crossroads, AND(KEY4, FOUND(KEY4, 3)))
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=4).add(OwlStatue(0x16F)).connect(before_miniboss, STONE_BEAK4)
sidescroller_key = Location(dungeon=4).add(DroppedKey(0x169)).connect(before_miniboss, AND(r.attack_hookshot_powder, FLIPPERS)) # key that drops in the hole and needs swim to get
center_puddle_chest = Location(dungeon=4).add(DungeonChest(0x16E)).connect(before_miniboss, FLIPPERS) # chest with 50 rupees
left_water_area = Location(dungeon=4).connect(before_miniboss, OR(FEATHER, FLIPPERS)) # area left with zol chest and 5 symbol puzzle (water area)
left_water_area.add(DungeonChest(0x16D)) # gel chest
left_water_area.add(DungeonChest(0x168)) # key chest near the puzzle
miniboss = Location(dungeon=4).connect(before_miniboss, AND(KEY4, FOUND(KEY4, 5), r.miniboss_requirements[world_setup.miniboss_mapping[3]]))
terrace_zols_chest = Location(dungeon=4).connect(before_miniboss, FLIPPERS) # flippers to move around miniboss through 5 tile room
miniboss = Location(dungeon=4).connect(terrace_zols_chest, POWER_BRACELET, one_way=True) # reach flippers chest through the miniboss room
terrace_zols_chest.add(DungeonChest(0x160)) # flippers chest
terrace_zols_chest.connect(left_water_area, r.attack_hookshot_powder, one_way=True) # can move from flippers chest south to push the block to left area
to_the_nightmare_key = Location(dungeon=4).connect(left_water_area, AND(FEATHER, OR(FLIPPERS, PEGASUS_BOOTS))) # 5 symbol puzzle (does not need flippers with boots + feather)
to_the_nightmare_key.add(DungeonChest(0x176))
before_boss = Location(dungeon=4).connect(before_miniboss, AND(r.attack_hookshot, FLIPPERS, KEY4, FOUND(KEY4, 5)))
boss = Location(dungeon=4).add(HeartContainer(0x166), Instrument(0x162)).connect(before_boss, AND(NIGHTMARE_KEY4, r.boss_requirements[world_setup.boss_mapping[3]]))
if options.logic == 'hard' or options.logic == 'glitched' or options.logic == 'hell':
sidescroller_key.connect(before_miniboss, AND(FEATHER, BOOMERANG)) # grab the key jumping over the water and boomerang downwards
sidescroller_key.connect(before_miniboss, AND(POWER_BRACELET, FLIPPERS)) # kill the zols with the pots in the room to spawn the key
rightside_crossroads.connect(entrance, FEATHER) # jump across the corners
puddle_crack_block_chest.connect(rightside_crossroads, FEATHER) # jump around the bombable block
north_crossroads.connect(entrance, FEATHER) # jump across the corners
after_double_lock.connect(entrance, FEATHER) # jump across the corners
dungeon4_puddle_before_crossroads.connect(after_double_lock, FEATHER) # With a tight jump feather is enough to cross the puddle without flippers
center_puddle_chest.connect(before_miniboss, FEATHER) # With a tight jump feather is enough to cross the puddle without flippers
miniboss = Location(dungeon=4).connect(terrace_zols_chest, None, one_way=True) # reach flippers chest through the miniboss room without pulling the lever
to_the_nightmare_key.connect(left_water_area, FEATHER) # With a tight jump feather is enough to reach the top left switch without flippers, or use flippers for puzzle and boots to get through 2d section
before_boss.connect(left_water_area, FEATHER) # jump to the bottom right corner of boss door room
if options.logic == 'glitched' or options.logic == 'hell':
pushable_block_chest.connect(rightside_crossroads, FLIPPERS) # sideways block push to skip bombs
sidescroller_key.connect(before_miniboss, AND(FEATHER, OR(r.attack_hookshot_powder, POWER_BRACELET))) # superjump into the hole to grab the key while falling into the water
miniboss.connect(before_miniboss, FEATHER) # use jesus jump to transition over the water left of miniboss
if options.logic == 'hell':
rightside_crossroads.connect(entrance, AND(PEGASUS_BOOTS, HOOKSHOT)) # pit buffer into the wall of the first pit, then boots bonk across the center, hookshot to get to the rightmost pit to a second villa buffer on the rightmost pit
pushable_block_chest.connect(rightside_crossroads, OR(PEGASUS_BOOTS, FEATHER)) # use feather to water clip into the top right corner of the bombable block, and sideways block push to gain access. Can boots bonk of top right wall, then water buffer to top of chest and boots bonk to water buffer next to chest
after_double_lock.connect(double_locked_room, AND(FOUND(KEY4, 4), PEGASUS_BOOTS), one_way=True) # use boots bonks to cross the water gaps
north_crossroads.connect(entrance, AND(PEGASUS_BOOTS, HOOKSHOT)) # pit buffer into wall of the first pit, then boots bonk towards the top and hookshot spam to get across (easier with Piece of Power)
after_double_lock.connect(entrance, PEGASUS_BOOTS) # boots bonk + pit buffer to the bottom
dungeon4_puddle_before_crossroads.connect(after_double_lock, AND(PEGASUS_BOOTS, HOOKSHOT)) # boots bonk across the water bottom wall to the bottom left corner, then hookshot up
to_the_nightmare_key.connect(left_water_area, AND(FLIPPERS, PEGASUS_BOOTS)) # Use flippers for puzzle and boots bonk to get through 2d section
before_boss.connect(left_water_area, PEGASUS_BOOTS) # boots bonk across bottom wall then boots bonk to the platform before boss door
self.entrance = entrance
class NoDungeon4:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=4)
Location(dungeon=4).add(HeartContainer(0x166), Instrument(0x162)).connect(entrance, r.boss_requirements[
world_setup.boss_mapping[3]])
self.entrance = entrance

View File

@@ -0,0 +1,89 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon5:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=5)
start_hookshot_chest = Location(dungeon=5).add(DungeonChest(0x1A0)).connect(entrance, HOOKSHOT)
compass = Location(dungeon=5).add(DungeonChest(0x19E)).connect(entrance, r.attack_hookshot_powder)
fourth_stalfos_area = Location(dungeon=5).add(DroppedKey(0x181)).connect(compass, AND(SWORD, FEATHER)) # crystal rocks can only be broken by sword
area2 = Location(dungeon=5).connect(entrance, KEY5)
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=5).add(OwlStatue(0x19A)).connect(area2, STONE_BEAK5)
Location(dungeon=5).add(DungeonChest(0x19B)).connect(area2, r.attack_hookshot_powder) # map chest
blade_trap_chest = Location(dungeon=5).add(DungeonChest(0x197)).connect(area2, HOOKSHOT) # key chest on the left
post_gohma = Location(dungeon=5).connect(area2, AND(HOOKSHOT, r.miniboss_requirements[world_setup.miniboss_mapping[4]], KEY5, FOUND(KEY5,2))) # staircase after gohma
staircase_before_boss = Location(dungeon=5).connect(post_gohma, AND(HOOKSHOT, FEATHER)) # bottom right section pits room before boss door. Path via gohma
after_keyblock_boss = Location(dungeon=5).connect(staircase_before_boss, AND(KEY5, FOUND(KEY5, 3))) # top right section pits room before boss door
after_stalfos = Location(dungeon=5).add(DungeonChest(0x196)).connect(area2, AND(SWORD, BOMB)) # Need to defeat master stalfos once for this empty chest; l2 sword beams kill but obscure
if options.owlstatues == "both" or options.owlstatues == "dungeon":
butterfly_owl = Location(dungeon=5).add(OwlStatue(0x18A)).connect(after_stalfos, AND(FEATHER, STONE_BEAK5))
else:
butterfly_owl = None
after_stalfos.connect(staircase_before_boss, AND(FEATHER, r.attack_hookshot_powder), one_way=True) # pathway from stalfos to staircase: past butterfly room and push the block
north_of_crossroads = Location(dungeon=5).connect(after_stalfos, FEATHER)
first_bridge_chest = Location(dungeon=5).add(DungeonChest(0x18E)).connect(north_of_crossroads, OR(HOOKSHOT, AND(FEATHER, PEGASUS_BOOTS))) # south of bridge
north_bridge_chest = Location(dungeon=5).add(DungeonChest(0x188)).connect(north_of_crossroads, HOOKSHOT) # north bridge chest 50 rupees
east_bridge_chest = Location(dungeon=5).add(DungeonChest(0x18F)).connect(north_of_crossroads, HOOKSHOT) # east bridge chest small key
third_arena = Location(dungeon=5).connect(north_of_crossroads, AND(SWORD, BOMB)) # can beat 3rd m.stalfos
stone_tablet = Location(dungeon=5).add(DungeonChest(0x183)).connect(north_of_crossroads, AND(POWER_BRACELET, r.attack_skeleton)) # stone tablet
boss_key = Location(dungeon=5).add(DungeonChest(0x186)).connect(after_stalfos, AND(FLIPPERS, HOOKSHOT)) # nightmare key
before_boss = Location(dungeon=5).connect(after_keyblock_boss, HOOKSHOT)
boss = Location(dungeon=5).add(HeartContainer(0x185), Instrument(0x182)).connect(before_boss, AND(r.boss_requirements[world_setup.boss_mapping[4]], NIGHTMARE_KEY5))
# When we can reach the stone tablet chest, we can also reach the final location of master stalfos
m_stalfos_drop = Location(dungeon=5).add(HookshotDrop()).connect(third_arena, AND(FEATHER, SWORD, BOMB)) # can reach fourth arena from entrance with feather and sword
if options.logic == 'hard' or options.logic == 'glitched' or options.logic == 'hell':
blade_trap_chest.connect(area2, AND(FEATHER, r.attack_hookshot_powder)) # jump past the blade traps
boss_key.connect(after_stalfos, AND(FLIPPERS, FEATHER, PEGASUS_BOOTS)) # boots jump across
after_stalfos.connect(after_keyblock_boss, AND(FEATHER, r.attack_hookshot_powder)) # circumvent stalfos by going past gohma and backwards from boss door
if butterfly_owl:
butterfly_owl.connect(after_stalfos, AND(PEGASUS_BOOTS, STONE_BEAK5)) # boots charge + bonk to cross 2d bridge
after_stalfos.connect(staircase_before_boss, AND(PEGASUS_BOOTS, r.attack_hookshot_powder), one_way=True) # pathway from stalfos to staircase: boots charge + bonk to cross bridge, past butterfly room and push the block
staircase_before_boss.connect(post_gohma, AND(PEGASUS_BOOTS, HOOKSHOT)) # boots bonk in 2d section to skip feather
north_of_crossroads.connect(after_stalfos, HOOKSHOT) # hookshot to the right block to cross pits
first_bridge_chest.connect(north_of_crossroads, FEATHER) # tight jump from bottom wall clipped to make it over the pits
after_keyblock_boss.connect(after_stalfos, AND(FEATHER, r.attack_hookshot_powder)) # jump from bottom left to top right, skipping the keyblock
before_boss.connect(after_stalfos, AND(FEATHER, PEGASUS_BOOTS, r.attack_hookshot_powder)) # cross pits room from bottom left to top left with boots jump
if options.logic == 'glitched' or options.logic == 'hell':
start_hookshot_chest.connect(entrance, FEATHER) # 1 pit buffer to clip bottom wall and jump across the pits
post_gohma.connect(area2, HOOKSHOT) # glitch through the blocks/pots with hookshot. Zoomerang can be used but has no logical implications because of 2d section requiring hookshot
north_bridge_chest.connect(north_of_crossroads, FEATHER) # 1 pit buffer to clip bottom wall and jump across the pits
east_bridge_chest.connect(first_bridge_chest, FEATHER) # 1 pit buffer to clip bottom wall and jump across the pits
#after_stalfos.connect(staircase_before_boss, AND(FEATHER, OR(SWORD, BOW, MAGIC_ROD))) # use the keyblock to get a wall clip in right wall to perform a superjump over the pushable block TODO: nagmessages
after_stalfos.connect(staircase_before_boss, AND(PEGASUS_BOOTS, FEATHER, OR(SWORD, BOW, MAGIC_ROD))) # charge a boots dash in bottom right corner to the right, jump before hitting the wall and use weapon to the left side before hitting the wall
if options.logic == 'hell':
start_hookshot_chest.connect(entrance, PEGASUS_BOOTS) # use pit buffer to clip into the bottom wall and boots bonk off the wall again
fourth_stalfos_area.connect(compass, AND(PEGASUS_BOOTS, SWORD)) # do an incredibly hard boots bonk setup to get across the hanging platforms in the 2d section
blade_trap_chest.connect(area2, AND(PEGASUS_BOOTS, r.attack_hookshot_powder)) # boots bonk + pit buffer past the blade traps
post_gohma.connect(area2, AND(PEGASUS_BOOTS, FEATHER, POWER_BRACELET, r.attack_hookshot_powder)) # use boots jump in room with 2 zols + flying arrows to pit buffer above pot, then jump across. Sideways block push + pick up pots to reach post_gohma
staircase_before_boss.connect(post_gohma, AND(PEGASUS_BOOTS, FEATHER)) # to pass 2d section, tight jump on left screen: hug left wall on little platform, then dash right off platform and jump while in midair to bonk against right wall
after_stalfos.connect(staircase_before_boss, AND(FEATHER, SWORD)) # unclipped superjump in bottom right corner of staircase before boss room, jumping left over the pushable block. reverse is push block
after_stalfos.connect(area2, SWORD) # knock master stalfos down 255 times (about 23 minutes)
north_bridge_chest.connect(north_of_crossroads, PEGASUS_BOOTS) # boots bonk across the pits with pit buffering
first_bridge_chest.connect(north_of_crossroads, PEGASUS_BOOTS) # get to first chest via the north chest with pit buffering
east_bridge_chest.connect(first_bridge_chest, PEGASUS_BOOTS) # boots bonk across the pits with pit buffering
third_arena.connect(north_of_crossroads, SWORD) # can beat 3rd m.stalfos with 255 sword spins
m_stalfos_drop.connect(third_arena, AND(FEATHER, SWORD)) # beat master stalfos by knocking it down 255 times x 4 (takes about 1.5h total)
m_stalfos_drop.connect(third_arena, AND(PEGASUS_BOOTS, SWORD)) # can reach fourth arena from entrance with pegasus boots and sword
boss_key.connect(after_stalfos, FLIPPERS) # pit buffer across
if butterfly_owl:
after_keyblock_boss.connect(butterfly_owl, STONE_BEAK5, one_way=True) # pit buffer from top right to bottom in right pits room
before_boss.connect(after_stalfos, AND(FEATHER, SWORD)) # cross pits room from bottom left to top left by unclipped superjump on bottom wall on top of side wall, then jump across
self.entrance = entrance
class NoDungeon5:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=5)
Location(dungeon=5).add(HeartContainer(0x185), Instrument(0x182)).connect(entrance, r.boss_requirements[
world_setup.boss_mapping[4]])
self.entrance = entrance

View File

@@ -0,0 +1,65 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon6:
def __init__(self, options, world_setup, r, *, raft_game_chest=True):
entrance = Location(dungeon=6)
Location(dungeon=6).add(DungeonChest(0x1CF)).connect(entrance, OR(BOMB, BOW, MAGIC_ROD, COUNT(POWER_BRACELET, 2))) # 50 rupees
Location(dungeon=6).add(DungeonChest(0x1C9)).connect(entrance, COUNT(POWER_BRACELET, 2)) # 100 rupees start
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=6).add(OwlStatue(0x1BB)).connect(entrance, STONE_BEAK6)
# Power bracelet chest
bracelet_chest = Location(dungeon=6).add(DungeonChest(0x1CE)).connect(entrance, AND(BOMB, FEATHER))
# left side
Location(dungeon=6).add(DungeonChest(0x1C0)).connect(entrance, AND(POWER_BRACELET, OR(BOMB, BOW, MAGIC_ROD))) # 3 wizrobes raised blocks dont need to hit the switch
left_side = Location(dungeon=6).add(DungeonChest(0x1B9)).add(DungeonChest(0x1B3)).connect(entrance, AND(POWER_BRACELET, OR(BOMB, BOOMERANG)))
Location(dungeon=6).add(DroppedKey(0x1B4)).connect(left_side, OR(BOMB, BOW, MAGIC_ROD)) # 2 wizrobe drop key
top_left = Location(dungeon=6).add(DungeonChest(0x1B0)).connect(left_side, COUNT(POWER_BRACELET, 2)) # top left chest horseheads
if raft_game_chest:
Location().add(Chest(0x06C)).connect(top_left, POWER_BRACELET) # seashell chest in raft game
# right side
to_miniboss = Location(dungeon=6).connect(entrance, KEY6)
miniboss = Location(dungeon=6).connect(to_miniboss, AND(BOMB, r.miniboss_requirements[world_setup.miniboss_mapping[5]]))
lower_right_side = Location(dungeon=6).add(DungeonChest(0x1BE)).connect(entrance, AND(OR(BOMB, BOW, MAGIC_ROD), COUNT(POWER_BRACELET, 2))) # waterway key
medicine_chest = Location(dungeon=6).add(DungeonChest(0x1D1)).connect(lower_right_side, FEATHER) # ledge chest medicine
if options.owlstatues == "both" or options.owlstatues == "dungeon":
lower_right_owl = Location(dungeon=6).add(OwlStatue(0x1D7)).connect(lower_right_side, AND(POWER_BRACELET, STONE_BEAK6))
center_1 = Location(dungeon=6).add(DroppedKey(0x1C3)).connect(miniboss, AND(COUNT(POWER_BRACELET, 2), FEATHER)) # tile room key drop
center_2_and_upper_right_side = Location(dungeon=6).add(DungeonChest(0x1B1)).connect(center_1, KEY6) # top right chest horseheads
boss_key = Location(dungeon=6).add(DungeonChest(0x1B6)).connect(center_2_and_upper_right_side, AND(KEY6, HOOKSHOT))
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=6).add(OwlStatue(0x1B6)).connect(boss_key, STONE_BEAK6)
boss = Location(dungeon=6).add(HeartContainer(0x1BC), Instrument(0x1b5)).connect(center_1, AND(NIGHTMARE_KEY6, r.boss_requirements[world_setup.boss_mapping[5]]))
if options.logic == 'hard' or options.logic == 'glitched' or options.logic == 'hell':
bracelet_chest.connect(entrance, BOMB) # get through 2d section by "fake" jumping to the ladders
center_1.connect(miniboss, AND(COUNT(POWER_BRACELET, 2), PEGASUS_BOOTS)) # use a boots dash to get over the platforms
if options.logic == 'glitched' or options.logic == 'hell':
entrance.connect(left_side, AND(POWER_BRACELET, FEATHER), one_way=True) # path from entrance to left_side: use superjumps to pass raised blocks
lower_right_side.connect(center_2_and_upper_right_side, AND(FEATHER, OR(SWORD, BOW, MAGIC_ROD)), one_way=True) # path from lower_right_side to center_2: superjump from waterway towards dodongos. superjump next to corner block, so weapons added
center_2_and_upper_right_side.connect(center_1, AND(POWER_BRACELET, FEATHER), one_way=True) # going backwards from dodongos, use a shaq jump to pass by keyblock at tile room
boss_key.connect(lower_right_side, FEATHER) # superjump from waterway to the left. POWER_BRACELET is implied from lower_right_side
if options.logic == 'hell':
entrance.connect(left_side, AND(POWER_BRACELET, PEGASUS_BOOTS, OR(BOW, MAGIC_ROD)), one_way=True) # can boots superhop off the top right corner in 3 wizrobe raised blocks room
medicine_chest.connect(lower_right_side, AND(PEGASUS_BOOTS, OR(MAGIC_ROD, BOW))) # can boots superhop off the top wall with bow or magic rod
center_1.connect(miniboss, AND(COUNT(POWER_BRACELET, 2))) # use a double damage boost from the sparks to get across (first one is free, second one needs to buffer while in midair for spark to get close enough)
lower_right_side.connect(center_2_and_upper_right_side, FEATHER, one_way=True) # path from lower_right_side to center_2: superjump from waterway towards dodongos. superjump next to corner block is super tight to get enough horizontal distance
self.entrance = entrance
class NoDungeon6:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=6)
Location(dungeon=6).add(HeartContainer(0x1BC), Instrument(0x1b5)).connect(entrance, r.boss_requirements[
world_setup.boss_mapping[5]])
self.entrance = entrance

View File

@@ -0,0 +1,65 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon7:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=7)
first_key = Location(dungeon=7).add(DroppedKey(0x210)).connect(entrance, r.attack_hookshot_powder)
topright_pillar_area = Location(dungeon=7).connect(entrance, KEY7)
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=7).add(OwlStatue(0x216)).connect(topright_pillar_area, STONE_BEAK7)
topright_pillar = Location(dungeon=7).add(DungeonChest(0x212)).connect(topright_pillar_area, POWER_BRACELET) # map chest
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=7).add(OwlStatue(0x204)).connect(topright_pillar_area, STONE_BEAK7)
topright_pillar_area.add(DungeonChest(0x209)) # stone slab chest can be reached by dropping down a hole
three_of_a_kind_north = Location(dungeon=7).add(DungeonChest(0x211)).connect(topright_pillar_area, OR(r.attack_hookshot, AND(FEATHER, SHIELD))) # compass chest; path without feather with hitting switch by falling on the raised blocks. No bracelet because ball does not reset
bottomleftF2_area = Location(dungeon=7).connect(topright_pillar_area, r.attack_hookshot) # area with hinox, be able to hit a switch to reach that area
topleftF1_chest = Location(dungeon=7).add(DungeonChest(0x201)) # top left chest on F1
bottomleftF2_area.connect(topleftF1_chest, None, one_way = True) # drop down in left most holes of hinox room or tile room
Location(dungeon=7).add(DroppedKey(0x21B)).connect(bottomleftF2_area, r.attack_hookshot) # hinox drop key
# Most of the dungeon can be accessed at this point.
if options.owlstatues == "both" or options.owlstatues == "dungeon":
bottomleft_owl = Location(dungeon=7).add(OwlStatue(0x21C)).connect(bottomleftF2_area, AND(BOMB, STONE_BEAK7))
nightmare_key = Location(dungeon=7).add(DungeonChest(0x224)).connect(bottomleftF2_area, r.miniboss_requirements[world_setup.miniboss_mapping[6]]) # nightmare key after the miniboss
mirror_shield_chest = Location(dungeon=7).add(DungeonChest(0x21A)).connect(bottomleftF2_area, r.attack_hookshot) # mirror shield chest, need to be able to hit a switch to reach or
bottomleftF2_area.connect(mirror_shield_chest, AND(KEY7, FOUND(KEY7, 3)), one_way = True) # reach mirror shield chest from hinox area by opening keyblock
toprightF1_chest = Location(dungeon=7).add(DungeonChest(0x204)).connect(bottomleftF2_area, r.attack_hookshot) # chest on the F1 right ledge. Added attack_hookshot since switch needs to be hit to get back up
final_pillar_area = Location(dungeon=7).add(DungeonChest(0x21C)).connect(bottomleftF2_area, AND(BOMB, HOOKSHOT)) # chest that needs to spawn to get to the last pillar
final_pillar = Location(dungeon=7).connect(final_pillar_area, POWER_BRACELET) # decouple chest from pillar
beamos_horseheads_area = Location(dungeon=7).connect(final_pillar, NIGHTMARE_KEY7) # area behind boss door
beamos_horseheads = Location(dungeon=7).add(DungeonChest(0x220)).connect(beamos_horseheads_area, POWER_BRACELET) # 100 rupee chest / medicine chest (DX) behind boss door
pre_boss = Location(dungeon=7).connect(beamos_horseheads_area, HOOKSHOT) # raised plateau before boss staircase
boss = Location(dungeon=7).add(HeartContainer(0x223), Instrument(0x22c)).connect(pre_boss, r.boss_requirements[world_setup.boss_mapping[6]])
if options.logic == 'glitched' or options.logic == 'hell':
topright_pillar_area.connect(entrance, AND(FEATHER, SWORD)) # superjump in the center to get on raised blocks, superjump in switch room to right side to walk down. center superjump has to be low so sword added
toprightF1_chest.connect(topright_pillar_area, FEATHER) # superjump from F1 switch room
topleftF2_area = Location(dungeon=7).connect(topright_pillar_area, FEATHER) # superjump in top left pillar room over the blocks from right to left, to reach tile room
topleftF2_area.connect(topleftF1_chest, None, one_way = True) # fall down tile room holes on left side to reach top left chest on ground floor
topleftF1_chest.connect(bottomleftF2_area, AND(PEGASUS_BOOTS, FEATHER), one_way = True) # without hitting the switch, jump on raised blocks at f1 pegs chest (0x209), and boots jump to stairs to reach hinox area
final_pillar_area.connect(bottomleftF2_area, OR(r.attack_hookshot, POWER_BRACELET, AND(FEATHER, SHIELD))) # sideways block push to get to the chest and pillar, kill requirement for 3 of a kind enemies to access chest. Assumes you do not get ball stuck on raised pegs for bracelet path
if options.owlstatues == "both" or options.owlstatues == "dungeon":
bottomleft_owl.connect(bottomleftF2_area, STONE_BEAK7) # sideways block push to get to the owl statue
final_pillar.connect(bottomleftF2_area, BOMB) # bomb trigger pillar
pre_boss.connect(final_pillar, FEATHER) # superjump on top of goomba to extend superjump to boss door plateau
pre_boss.connect(beamos_horseheads_area, None, one_way=True) # can drop down from raised plateau to beamos horseheads area
if options.logic == 'hell':
topright_pillar_area.connect(entrance, FEATHER) # superjump in the center to get on raised blocks, has to be low
topright_pillar_area.connect(entrance, AND(PEGASUS_BOOTS, OR(BOW, MAGIC_ROD))) # boots superhop in the center to get on raised blocks
toprightF1_chest.connect(topright_pillar_area, AND(PEGASUS_BOOTS, OR(BOW, MAGIC_ROD))) # boots superhop from F1 switch room
pre_boss.connect(final_pillar, AND(PEGASUS_BOOTS, OR(BOW, MAGIC_ROD))) # boots superhop on top of goomba to extend superhop to boss door plateau
self.entrance = entrance
class NoDungeon7:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=7)
boss = Location(dungeon=7).add(HeartContainer(0x223), Instrument(0x22c)).connect(entrance, r.boss_requirements[
world_setup.boss_mapping[6]])
self.entrance = entrance

View File

@@ -0,0 +1,107 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class Dungeon8:
def __init__(self, options, world_setup, r, *, back_entrance_heartpiece=True):
entrance = Location(dungeon=8)
entrance_up = Location(dungeon=8).connect(entrance, FEATHER)
entrance_left = Location(dungeon=8).connect(entrance, r.attack_hookshot_no_bomb) # past hinox
# left side
entrance_left.add(DungeonChest(0x24D)) # zamboni room chest
Location(dungeon=8).add(DungeonChest(0x25C)).connect(entrance_left, r.attack_hookshot) # eye magnet chest
vire_drop_key = Location(dungeon=8).add(DroppedKey(0x24C)).connect(entrance_left, r.attack_hookshot_no_bomb) # vire drop key
sparks_chest = Location(dungeon=8).add(DungeonChest(0x255)).connect(entrance_left, OR(HOOKSHOT, FEATHER)) # chest before lvl1 miniboss
Location(dungeon=8).add(DungeonChest(0x246)).connect(entrance_left, MAGIC_ROD) # key chest that spawns after creating fire
# right side
if options.owlstatues == "both" or options.owlstatues == "dungeon":
bottomright_owl = Location(dungeon=8).add(OwlStatue(0x253)).connect(entrance, AND(STONE_BEAK8, FEATHER, POWER_BRACELET)) # Two ways to reach this owl statue, but both require the same (except that one route requires bombs as well)
else:
bottomright_owl = None
slime_chest = Location(dungeon=8).add(DungeonChest(0x259)).connect(entrance, OR(FEATHER, AND(r.attack_hookshot, POWER_BRACELET))) # chest with slime
bottom_right = Location(dungeon=8).add(DroppedKey(0x25A)).connect(entrance, AND(FEATHER, OR(BOMB, AND(r.attack_hookshot_powder, POWER_BRACELET)))) # zamboni key drop; bombs for entrance up through switch room, weapon + bracelet for NW zamboni staircase to bottom right past smasher
bottomright_pot_chest = Location(dungeon=8).add(DungeonChest(0x25F)).connect(bottom_right, POWER_BRACELET) # 4 ropes pot room chest
map_chest = Location(dungeon=8).add(DungeonChest(0x24F)).connect(entrance_up, None) # use the zamboni to get to the push blocks
lower_center = Location(dungeon=8).connect(entrance_up, KEY8)
upper_center = Location(dungeon=8).connect(lower_center, AND(KEY8, FOUND(KEY8, 2)))
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=8).add(OwlStatue(0x245)).connect(upper_center, STONE_BEAK8)
Location(dungeon=8).add(DroppedKey(0x23E)).connect(upper_center, r.attack_skeleton) # 2 gibdos cracked floor; technically possible to use pits to kill but dumb
medicine_chest = Location(dungeon=8).add(DungeonChest(0x235)).connect(upper_center, AND(FEATHER, HOOKSHOT)) # medicine chest
middle_center_1 = Location(dungeon=8).connect(upper_center, BOMB)
middle_center_2 = Location(dungeon=8).connect(middle_center_1, AND(KEY8, FOUND(KEY8, 4)))
middle_center_3 = Location(dungeon=8).connect(middle_center_2, KEY8)
miniboss_entrance = Location(dungeon=8).connect(middle_center_3, AND(HOOKSHOT, KEY8, FOUND(KEY8, 7))) # hookshot to get across to keyblock, 7 to fix keylock issues if keys are used on other keyblocks
miniboss = Location(dungeon=8).connect(miniboss_entrance, AND(FEATHER, r.miniboss_requirements[world_setup.miniboss_mapping[7]])) # feather for 2d section, sword to kill
miniboss.add(DungeonChest(0x237)) # fire rod chest
up_left = Location(dungeon=8).connect(upper_center, AND(r.attack_hookshot_powder, AND(KEY8, FOUND(KEY8, 4))))
entrance_up.connect(up_left, AND(FEATHER, MAGIC_ROD), one_way=True) # alternate path with fire rod through 2d section to nightmare key
up_left.add(DungeonChest(0x240)) # beamos blocked chest
up_left.connect(entrance_left, None, one_way=True) # path from up_left to entrance_left by dropping of the ledge in torch room
Location(dungeon=8).add(DungeonChest(0x23D)).connect(up_left, BOMB) # dodongo chest
up_left.connect(upper_center, None, one_way=True) # use the outside path of the dungeon to get to the right side
if back_entrance_heartpiece:
Location().add(HeartPiece(0x000)).connect(up_left, None) # Outside the dungeon on the platform
Location(dungeon=8).add(DroppedKey(0x241)).connect(up_left, BOW) # lava statue
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=8).add(OwlStatue(0x241)).connect(up_left, STONE_BEAK8)
Location(dungeon=8).add(DungeonChest(0x23A)).connect(up_left, HOOKSHOT) # ledge chest left of boss door
top_left_stairs = Location(dungeon=8).connect(entrance_up, AND(FEATHER, MAGIC_ROD))
top_left_stairs.connect(up_left, None, one_way=True) # jump down from the staircase to the right
nightmare_key = Location(dungeon=8).add(DungeonChest(0x232)).connect(top_left_stairs, AND(FEATHER, SWORD, KEY8, FOUND(KEY8, 7)))
# Bombing from the center dark rooms to the left so you can access more keys.
# The south walls of center dark room can be bombed from lower_center too with bomb and feather for center dark room access from the south, allowing even more access. Not sure if this should be logic since "obscure"
middle_center_2.connect(up_left, AND(BOMB, FEATHER), one_way=True) # does this even skip a key? both middle_center_2 and up_left come from upper_center with 1 extra key
bossdoor = Location(dungeon=8).connect(entrance_up, AND(FEATHER, MAGIC_ROD))
boss = Location(dungeon=8).add(HeartContainer(0x234), Instrument(0x230)).connect(bossdoor, AND(NIGHTMARE_KEY8, r.boss_requirements[world_setup.boss_mapping[7]]))
if options.logic == 'hard' or options.logic == 'glitched' or options.logic == 'hell':
entrance_left.connect(entrance, BOMB) # use bombs to kill vire and hinox
vire_drop_key.connect(entrance_left, BOMB) # use bombs to kill rolling bones and vire
bottom_right.connect(slime_chest, FEATHER) # diagonal jump over the pits to reach rolling rock / zamboni
up_left.connect(lower_center, AND(BOMB, FEATHER)) # blow up hidden walls from peahat room -> dark room -> eye statue room
slime_chest.connect(entrance, AND(r.attack_hookshot_powder, POWER_BRACELET)) # kill vire with powder or bombs
if options.logic == 'glitched' or options.logic == 'hell':
sparks_chest.connect(entrance_left, OR(r.attack_hookshot, FEATHER, PEGASUS_BOOTS)) # 1 pit buffer across the pit. Add requirements for all the options to get to this area
lower_center.connect(entrance_up, None) # sideways block push in peahat room to get past keyblock
miniboss_entrance.connect(lower_center, AND(BOMB, FEATHER, HOOKSHOT)) # blow up hidden wall for darkroom, use feather + hookshot to clip past keyblock in front of stairs
miniboss_entrance.connect(lower_center, AND(BOMB, FEATHER, FOUND(KEY8, 7))) # same as above, but without clipping past the keyblock
up_left.connect(lower_center, FEATHER) # use jesus jump in refill room left of peahats to clip bottom wall and push bottom block left, to get a place to super jump
up_left.connect(upper_center, FEATHER) # from up left you can jesus jump / lava swim around the key door next to the boss.
top_left_stairs.connect(up_left, AND(FEATHER, SWORD)) # superjump
medicine_chest.connect(upper_center, FEATHER) # jesus super jump
up_left.connect(bossdoor, FEATHER, one_way=True) # superjump off the bottom or right wall to jump over to the boss door
if options.logic == 'hell':
if bottomright_owl:
bottomright_owl.connect(entrance, AND(SWORD, POWER_BRACELET, PEGASUS_BOOTS, STONE_BEAK8)) # underground section past mimics, boots bonking across the gap to the ladder
bottomright_pot_chest.connect(entrance, AND(SWORD, POWER_BRACELET, PEGASUS_BOOTS)) # underground section past mimics, boots bonking across the gap to the ladder
entrance.connect(bottomright_pot_chest, AND(FEATHER, SWORD), one_way=True) # use NW zamboni staircase backwards, subpixel manip for superjump past the pots
medicine_chest.connect(upper_center, AND(PEGASUS_BOOTS, HOOKSHOT)) # boots bonk + lava buffer to the bottom wall, then bonk onto the middle section
miniboss.connect(miniboss_entrance, AND(PEGASUS_BOOTS, r.miniboss_requirements[world_setup.miniboss_mapping[7]])) # get through 2d section with boots bonks
top_left_stairs.connect(map_chest, AND(PEGASUS_BOOTS, MAGIC_ROD)) # boots bonk + lava buffer from map chest to entrance_up, then boots bonk through 2d section
nightmare_key.connect(top_left_stairs, AND(PEGASUS_BOOTS, SWORD, FOUND(KEY8, 7))) # use a boots bonk to cross the 2d section + the lava in cueball room
bottom_right.connect(entrance_up, AND(POWER_BRACELET, PEGASUS_BOOTS), one_way=True) # take staircase to NW zamboni room, boots bonk onto the lava and water buffer all the way down to push the zamboni
bossdoor.connect(entrance_up, AND(PEGASUS_BOOTS, MAGIC_ROD)) # boots bonk through 2d section
self.entrance = entrance
class NoDungeon8:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=8)
boss = Location(dungeon=8).add(HeartContainer(0x234)).connect(entrance, r.boss_requirements[
world_setup.boss_mapping[7]])
instrument = Location(dungeon=8).add(Instrument(0x230)).connect(boss, FEATHER) # jump over the lava to get to the instrument
self.entrance = entrance

View File

@@ -0,0 +1,49 @@
from .requirements import *
from .location import Location
from ..locations.all import *
class DungeonColor:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=9)
room2 = Location(dungeon=9).connect(entrance, r.attack_hookshot_powder)
room2.add(DungeonChest(0x314)) # key
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=9).add(OwlStatue(0x308), OwlStatue(0x30F)).connect(room2, STONE_BEAK9)
room2_weapon = Location(dungeon=9).connect(room2, r.attack_hookshot)
room2_weapon.add(DungeonChest(0x311)) # stone beak
room2_lights = Location(dungeon=9).connect(room2, OR(r.attack_hookshot, SHIELD))
room2_lights.add(DungeonChest(0x30F)) # compass chest
room2_lights.add(DroppedKey(0x308))
Location(dungeon=9).connect(room2, AND(KEY9, FOUND(KEY9, 3), r.miniboss_requirements[world_setup.miniboss_mapping["c2"]])).add(DungeonChest(0x302)) # nightmare key after slime mini boss
room3 = Location(dungeon=9).connect(room2, AND(KEY9, FOUND(KEY9, 2), r.miniboss_requirements[world_setup.miniboss_mapping["c1"]])) # After the miniboss
room4 = Location(dungeon=9).connect(room3, POWER_BRACELET) # need to lift a pot to reveal button
room4.add(DungeonChest(0x306)) # map
room4karakoro = Location(dungeon=9).add(DroppedKey(0x307)).connect(room4, r.attack_hookshot) # require item to knock Karakoro enemies into shell
if options.owlstatues == "both" or options.owlstatues == "dungeon":
Location(dungeon=9).add(OwlStatue(0x30A)).connect(room4, STONE_BEAK9)
room5 = Location(dungeon=9).connect(room4, OR(r.attack_hookshot, SHIELD)) # lights room
room6 = Location(dungeon=9).connect(room5, AND(KEY9, FOUND(KEY9, 3))) # room with switch and nightmare door
pre_boss = Location(dungeon=9).connect(room6, OR(r.attack_hookshot, AND(PEGASUS_BOOTS, FEATHER))) # before the boss, require item to hit switch or jump past raised blocks
boss = Location(dungeon=9).connect(pre_boss, AND(NIGHTMARE_KEY9, r.boss_requirements[world_setup.boss_mapping[8]]))
boss.add(TunicFairy(0), TunicFairy(1))
if options.logic == 'hard' or options.logic == 'glitched' or options.logic == 'hell':
room2.connect(entrance, POWER_BRACELET) # throw pots at enemies
pre_boss.connect(room6, FEATHER) # before the boss, jump past raised blocks without boots
if options.logic == 'hell':
room2_weapon.connect(room2, SHIELD) # shield bump karakoro into the holes
room4karakoro.connect(room4, SHIELD) # shield bump karakoro into the holes
self.entrance = entrance
class NoDungeonColor:
def __init__(self, options, world_setup, r):
entrance = Location(dungeon=9)
boss = Location(dungeon=9).connect(entrance, r.boss_requirements[world_setup.boss_mapping[8]])
boss.add(TunicFairy(0), TunicFairy(1))
self.entrance = entrance

View File

@@ -0,0 +1,57 @@
import typing
from .requirements import hasConsumableRequirement, OR
from ..locations.itemInfo import ItemInfo
class Location:
def __init__(self, name=None, dungeon=None):
self.name = name
self.items = [] # type: typing.List[ItemInfo]
self.dungeon = dungeon
self.__connected_to = set()
self.simple_connections = []
self.gated_connections = []
def add(self, *item_infos):
for ii in item_infos:
assert isinstance(ii, ItemInfo)
ii.setLocation(self)
self.items.append(ii)
return self
def connect(self, other, req, *, one_way=False):
assert isinstance(other, Location), type(other)
if isinstance(req, bool):
if req:
self.connect(other, None, one_way=one_way)
return
if other in self.__connected_to:
for idx, data in enumerate(self.gated_connections):
if data[0] == other:
if req is None or data[1] is None:
self.gated_connections[idx] = (other, None)
else:
self.gated_connections[idx] = (other, OR(req, data[1]))
break
for idx, data in enumerate(self.simple_connections):
if data[0] == other:
if req is None or data[1] is None:
self.simple_connections[idx] = (other, None)
else:
self.simple_connections[idx] = (other, OR(req, data[1]))
break
else:
self.__connected_to.add(other)
if hasConsumableRequirement(req):
self.gated_connections.append((other, req))
else:
self.simple_connections.append((other, req))
if not one_way:
other.connect(self, req, one_way=True)
return self
def __repr__(self):
return "<%s:%s:%d:%d:%d>" % (self.__class__.__name__, self.dungeon, len(self.items), len(self.simple_connections), len(self.gated_connections))

View File

@@ -0,0 +1,682 @@
from .requirements import *
from .location import Location
from ..locations.all import *
from ..worldSetup import ENTRANCE_INFO
class World:
def __init__(self, options, world_setup, r):
self.overworld_entrance = {}
self.indoor_location = {}
mabe_village = Location("Mabe Village")
Location().add(HeartPiece(0x2A4)).connect(mabe_village, r.bush) # well
Location().add(FishingMinigame()).connect(mabe_village, AND(r.bush, COUNT("RUPEES", 20))) # fishing game, heart piece is directly done by the minigame.
Location().add(Seashell(0x0A3)).connect(mabe_village, r.bush) # bushes below the shop
Location().add(Seashell(0x0D2)).connect(mabe_village, PEGASUS_BOOTS) # smash into tree next to lv1
Location().add(Song(0x092)).connect(mabe_village, OCARINA) # Marins song
rooster_cave = Location("Rooster Cave")
Location().add(DroppedKey(0x1E4)).connect(rooster_cave, AND(OCARINA, SONG3))
papahl_house = Location("Papahl House")
papahl_house.connect(Location().add(TradeSequenceItem(0x2A6, TRADING_ITEM_RIBBON)), TRADING_ITEM_YOSHI_DOLL)
trendy_shop = Location("Trendy Shop").add(TradeSequenceItem(0x2A0, TRADING_ITEM_YOSHI_DOLL))
#trendy_shop.connect(Location())
self._addEntrance("papahl_house_left", mabe_village, papahl_house, None)
self._addEntrance("papahl_house_right", mabe_village, papahl_house, None)
self._addEntrance("rooster_grave", mabe_village, rooster_cave, COUNT(POWER_BRACELET, 2))
self._addEntranceRequirementExit("rooster_grave", None) # if exiting, you do not need l2 bracelet
self._addEntrance("madambowwow", mabe_village, None, None)
self._addEntrance("ulrira", mabe_village, None, None)
self._addEntrance("mabe_phone", mabe_village, None, None)
self._addEntrance("library", mabe_village, None, None)
self._addEntrance("trendy_shop", mabe_village, trendy_shop, r.bush)
self._addEntrance("d1", mabe_village, None, TAIL_KEY)
self._addEntranceRequirementExit("d1", None) # if exiting, you do not need the key
start_house = Location("Start House").add(StartItem())
self._addEntrance("start_house", mabe_village, start_house, None)
shop = Location("Shop")
Location().add(ShopItem(0)).connect(shop, OR(COUNT("RUPEES", 500), SWORD))
Location().add(ShopItem(1)).connect(shop, OR(COUNT("RUPEES", 1480), SWORD))
self._addEntrance("shop", mabe_village, shop, None)
dream_hut = Location("Dream Hut")
dream_hut_right = Location().add(Chest(0x2BF)).connect(dream_hut, SWORD)
if options.logic != "casual":
dream_hut_right.connect(dream_hut, OR(BOOMERANG, HOOKSHOT, FEATHER))
dream_hut_left = Location().add(Chest(0x2BE)).connect(dream_hut_right, PEGASUS_BOOTS)
self._addEntrance("dream_hut", mabe_village, dream_hut, POWER_BRACELET)
kennel = Location("Kennel").connect(Location().add(Seashell(0x2B2)), SHOVEL) # in the kennel
kennel.connect(Location().add(TradeSequenceItem(0x2B2, TRADING_ITEM_DOG_FOOD)), TRADING_ITEM_RIBBON)
self._addEntrance("kennel", mabe_village, kennel, None)
sword_beach = Location("Sword Beach").add(BeachSword()).connect(mabe_village, OR(r.bush, SHIELD, r.attack_hookshot))
banana_seller = Location("Banana Seller")
banana_seller.connect(Location().add(TradeSequenceItem(0x2FE, TRADING_ITEM_BANANAS)), TRADING_ITEM_DOG_FOOD)
self._addEntrance("banana_seller", sword_beach, banana_seller, r.bush)
boomerang_cave = Location("Boomerang Cave")
if options.boomerang == 'trade':
Location().add(BoomerangGuy()).connect(boomerang_cave, OR(BOOMERANG, HOOKSHOT, MAGIC_ROD, PEGASUS_BOOTS, FEATHER, SHOVEL))
elif options.boomerang == 'gift':
Location().add(BoomerangGuy()).connect(boomerang_cave, None)
self._addEntrance("boomerang_cave", sword_beach, boomerang_cave, BOMB)
self._addEntranceRequirementExit("boomerang_cave", None) # if exiting, you do not need bombs
sword_beach_to_ghost_hut = Location("Sword Beach to Ghost House").add(Chest(0x0E5)).connect(sword_beach, POWER_BRACELET)
ghost_hut_outside = Location("Outside Ghost House").connect(sword_beach_to_ghost_hut, POWER_BRACELET)
ghost_hut_inside = Location("Ghost House").connect(Location().add(Seashell(0x1E3)), POWER_BRACELET)
self._addEntrance("ghost_house", ghost_hut_outside, ghost_hut_inside, None)
## Forest area
forest = Location("Forest").connect(mabe_village, r.bush) # forest stretches all the way from the start town to the witch hut
Location().add(Chest(0x071)).connect(forest, POWER_BRACELET) # chest at start forest with 2 zols
forest_heartpiece = Location("Forest Heart Piece").add(HeartPiece(0x044)) # next to the forest, surrounded by pits
forest.connect(forest_heartpiece, OR(BOOMERANG, FEATHER, HOOKSHOT, ROOSTER), one_way=True)
witch_hut = Location().connect(Location().add(Witch()), TOADSTOOL)
self._addEntrance("witch", forest, witch_hut, None)
crazy_tracy_hut = Location("Outside Crazy Tracy's House").connect(forest, POWER_BRACELET)
crazy_tracy_hut_inside = Location("Crazy Tracy's House")
Location().add(KeyLocation("MEDICINE2")).connect(crazy_tracy_hut_inside, FOUND("RUPEES", 50))
self._addEntrance("crazy_tracy", crazy_tracy_hut, crazy_tracy_hut_inside, None)
start_house.connect(crazy_tracy_hut, SONG2, one_way=True) # Manbo's Mambo into the pond outside Tracy
forest_madbatter = Location("Forest Mad Batter")
Location().add(MadBatter(0x1E1)).connect(forest_madbatter, MAGIC_POWDER)
self._addEntrance("forest_madbatter", forest, forest_madbatter, POWER_BRACELET)
self._addEntranceRequirementExit("forest_madbatter", None) # if exiting, you do not need bracelet
forest_cave = Location("Forest Cave")
Location().add(Chest(0x2BD)).connect(forest_cave, SWORD) # chest in forest cave on route to mushroom
log_cave_heartpiece = Location().add(HeartPiece(0x2AB)).connect(forest_cave, POWER_BRACELET) # piece of heart in the forest cave on route to the mushroom
forest_toadstool = Location().add(Toadstool())
self._addEntrance("toadstool_entrance", forest, forest_cave, None)
self._addEntrance("toadstool_exit", forest_toadstool, forest_cave, None)
hookshot_cave = Location("Hookshot Cave")
hookshot_cave_chest = Location().add(Chest(0x2B3)).connect(hookshot_cave, OR(HOOKSHOT, ROOSTER))
self._addEntrance("hookshot_cave", forest, hookshot_cave, POWER_BRACELET)
swamp = Location("Swamp").connect(forest, AND(OR(MAGIC_POWDER, FEATHER, ROOSTER), r.bush))
swamp.connect(forest, r.bush, one_way=True) # can go backwards past Tarin
swamp.connect(forest_toadstool, OR(FEATHER, ROOSTER))
swamp_chest = Location("Swamp Chest").add(Chest(0x034)).connect(swamp, OR(BOWWOW, HOOKSHOT, MAGIC_ROD, BOOMERANG))
self._addEntrance("d2", swamp, None, OR(BOWWOW, HOOKSHOT, MAGIC_ROD, BOOMERANG))
forest_rear_chest = Location().add(Chest(0x041)).connect(swamp, r.bush) # tail key
self._addEntrance("writes_phone", swamp, None, None)
writes_hut_outside = Location("Outside Write's House").connect(swamp, OR(FEATHER, ROOSTER)) # includes the cave behind the hut
writes_house = Location("Write's House")
writes_house.connect(Location().add(TradeSequenceItem(0x2a8, TRADING_ITEM_BROOM)), TRADING_ITEM_LETTER)
self._addEntrance("writes_house", writes_hut_outside, writes_house, None)
if options.owlstatues == "both" or options.owlstatues == "overworld":
writes_hut_outside.add(OwlStatue(0x11))
writes_cave = Location("Write's Cave")
writes_cave_left_chest = Location().add(Chest(0x2AE)).connect(writes_cave, OR(FEATHER, ROOSTER, HOOKSHOT)) # 1st chest in the cave behind the hut
Location().add(Chest(0x2AF)).connect(writes_cave, POWER_BRACELET) # 2nd chest in the cave behind the hut.
self._addEntrance("writes_cave_left", writes_hut_outside, writes_cave, None)
self._addEntrance("writes_cave_right", writes_hut_outside, writes_cave, None)
graveyard = Location("Graveyard").connect(forest, OR(FEATHER, ROOSTER, POWER_BRACELET)) # whole area from the graveyard up to the moblin cave
if options.owlstatues == "both" or options.owlstatues == "overworld":
graveyard.add(OwlStatue(0x035)) # Moblin cave owl
self._addEntrance("photo_house", graveyard, None, None)
self._addEntrance("d0", graveyard, None, POWER_BRACELET)
self._addEntranceRequirementExit("d0", None) # if exiting, you do not need bracelet
ghost_grave = Location().connect(forest, POWER_BRACELET)
Location().add(Seashell(0x074)).connect(ghost_grave, AND(r.bush, SHOVEL)) # next to grave cave, digging spot
graveyard_cave_left = Location()
graveyard_cave_right = Location().connect(graveyard_cave_left, OR(FEATHER, ROOSTER))
graveyard_heartpiece = Location().add(HeartPiece(0x2DF)).connect(graveyard_cave_right, OR(AND(BOMB, OR(HOOKSHOT, PEGASUS_BOOTS), FEATHER), ROOSTER)) # grave cave
self._addEntrance("graveyard_cave_left", ghost_grave, graveyard_cave_left, POWER_BRACELET)
self._addEntrance("graveyard_cave_right", graveyard, graveyard_cave_right, None)
moblin_cave = Location().connect(Location().add(Chest(0x2E2)), AND(r.attack_hookshot_powder, r.miniboss_requirements[world_setup.miniboss_mapping["moblin_cave"]]))
self._addEntrance("moblin_cave", graveyard, moblin_cave, None)
# "Ukuku Prairie"
ukuku_prairie = Location().connect(mabe_village, POWER_BRACELET).connect(graveyard, POWER_BRACELET)
ukuku_prairie.connect(Location().add(TradeSequenceItem(0x07B, TRADING_ITEM_STICK)), TRADING_ITEM_BANANAS)
ukuku_prairie.connect(Location().add(TradeSequenceItem(0x087, TRADING_ITEM_HONEYCOMB)), TRADING_ITEM_STICK)
self._addEntrance("prairie_left_phone", ukuku_prairie, None, None)
self._addEntrance("prairie_right_phone", ukuku_prairie, None, None)
self._addEntrance("prairie_left_cave1", ukuku_prairie, Location().add(Chest(0x2CD)), None) # cave next to town
self._addEntrance("prairie_left_fairy", ukuku_prairie, None, BOMB)
self._addEntranceRequirementExit("prairie_left_fairy", None) # if exiting, you do not need bombs
prairie_left_cave2 = Location() # Bomb cave
Location().add(Chest(0x2F4)).connect(prairie_left_cave2, PEGASUS_BOOTS)
Location().add(HeartPiece(0x2E5)).connect(prairie_left_cave2, AND(BOMB, PEGASUS_BOOTS))
self._addEntrance("prairie_left_cave2", ukuku_prairie, prairie_left_cave2, BOMB)
self._addEntranceRequirementExit("prairie_left_cave2", None) # if exiting, you do not need bombs
mamu = Location().connect(Location().add(Song(0x2FB)), AND(OCARINA, COUNT("RUPEES", 1480)))
self._addEntrance("mamu", ukuku_prairie, mamu, AND(OR(AND(FEATHER, PEGASUS_BOOTS), ROOSTER), OR(HOOKSHOT, ROOSTER), POWER_BRACELET))
dungeon3_entrance = Location().connect(ukuku_prairie, OR(FEATHER, ROOSTER, FLIPPERS))
self._addEntrance("d3", dungeon3_entrance, None, SLIME_KEY)
self._addEntranceRequirementExit("d3", None) # if exiting, you do not need to open the door
Location().add(Seashell(0x0A5)).connect(dungeon3_entrance, SHOVEL) # above lv3
dungeon3_entrance.connect(ukuku_prairie, None, one_way=True) # jump down ledge back to ukuku_prairie
prairie_island_seashell = Location().add(Seashell(0x0A6)).connect(ukuku_prairie, AND(FLIPPERS, r.bush)) # next to lv3
Location().add(Seashell(0x08B)).connect(ukuku_prairie, r.bush) # next to seashell house
Location().add(Seashell(0x0A4)).connect(ukuku_prairie, PEGASUS_BOOTS) # smash into tree next to phonehouse
self._addEntrance("castle_jump_cave", ukuku_prairie, Location().add(Chest(0x1FD)), OR(AND(FEATHER, PEGASUS_BOOTS), ROOSTER)) # left of the castle, 5 holes turned into 3
Location().add(Seashell(0x0B9)).connect(ukuku_prairie, POWER_BRACELET) # under the rock
left_bay_area = Location()
left_bay_area.connect(ghost_hut_outside, OR(AND(FEATHER, PEGASUS_BOOTS), ROOSTER))
self._addEntrance("prairie_low_phone", left_bay_area, None, None)
Location().add(Seashell(0x0E9)).connect(left_bay_area, r.bush) # same screen as mermaid statue
tiny_island = Location().add(Seashell(0x0F8)).connect(left_bay_area, AND(OR(FLIPPERS, ROOSTER), r.bush)) # tiny island
prairie_plateau = Location() # prairie plateau at the owl statue
if options.owlstatues == "both" or options.owlstatues == "overworld":
prairie_plateau.add(OwlStatue(0x0A8))
Location().add(Seashell(0x0A8)).connect(prairie_plateau, SHOVEL) # at the owl statue
prairie_cave = Location()
prairie_cave_secret_exit = Location().connect(prairie_cave, AND(BOMB, OR(FEATHER, ROOSTER)))
self._addEntrance("prairie_right_cave_top", ukuku_prairie, prairie_cave, None)
self._addEntrance("prairie_right_cave_bottom", left_bay_area, prairie_cave, None)
self._addEntrance("prairie_right_cave_high", prairie_plateau, prairie_cave_secret_exit, None)
bay_madbatter_connector_entrance = Location()
bay_madbatter_connector_exit = Location().connect(bay_madbatter_connector_entrance, FLIPPERS)
bay_madbatter_connector_outside = Location()
bay_madbatter = Location().connect(Location().add(MadBatter(0x1E0)), MAGIC_POWDER)
self._addEntrance("prairie_madbatter_connector_entrance", left_bay_area, bay_madbatter_connector_entrance, AND(OR(FEATHER, ROOSTER), OR(SWORD, MAGIC_ROD, BOOMERANG)))
self._addEntranceRequirementExit("prairie_madbatter_connector_entrance", AND(OR(FEATHER, ROOSTER), r.bush)) # if exiting, you can pick up the bushes by normal means
self._addEntrance("prairie_madbatter_connector_exit", bay_madbatter_connector_outside, bay_madbatter_connector_exit, None)
self._addEntrance("prairie_madbatter", bay_madbatter_connector_outside, bay_madbatter, None)
seashell_mansion = Location()
if options.goal != "seashells":
Location().add(SeashellMansion(0x2E9)).connect(seashell_mansion, COUNT(SEASHELL, 20))
else:
seashell_mansion.add(DroppedKey(0x2E9))
self._addEntrance("seashell_mansion", ukuku_prairie, seashell_mansion, None)
bay_water = Location()
bay_water.connect(ukuku_prairie, FLIPPERS)
bay_water.connect(left_bay_area, FLIPPERS)
fisher_under_bridge = Location().add(TradeSequenceItem(0x2F5, TRADING_ITEM_NECKLACE))
fisher_under_bridge.connect(bay_water, AND(TRADING_ITEM_FISHING_HOOK, FEATHER, FLIPPERS))
bay_water.connect(Location().add(TradeSequenceItem(0x0C9, TRADING_ITEM_SCALE)), AND(TRADING_ITEM_NECKLACE, FLIPPERS))
d5_entrance = Location().connect(bay_water, FLIPPERS)
self._addEntrance("d5", d5_entrance, None, None)
# Richard
richard_house = Location()
richard_cave = Location().connect(richard_house, COUNT(GOLD_LEAF, 5))
richard_cave.connect(richard_house, None, one_way=True) # can exit richard's cave even without leaves
richard_cave_chest = Location().add(Chest(0x2C8)).connect(richard_cave, OR(FEATHER, HOOKSHOT, ROOSTER))
richard_maze = Location()
self._addEntrance("richard_house", ukuku_prairie, richard_house, None)
self._addEntrance("richard_maze", richard_maze, richard_cave, None)
if options.owlstatues == "both" or options.owlstatues == "overworld":
Location().add(OwlStatue(0x0C6)).connect(richard_maze, r.bush)
Location().add(SlimeKey()).connect(richard_maze, AND(r.bush, SHOVEL))
next_to_castle = Location()
if options.tradequest:
ukuku_prairie.connect(next_to_castle, TRADING_ITEM_BANANAS, one_way=True) # can only give bananas from ukuku prairie side
else:
next_to_castle.connect(ukuku_prairie, None)
next_to_castle.connect(ukuku_prairie, FLIPPERS)
self._addEntrance("castle_phone", next_to_castle, None, None)
castle_secret_entrance_left = Location()
castle_secret_entrance_right = Location().connect(castle_secret_entrance_left, FEATHER)
castle_courtyard = Location()
castle_frontdoor = Location().connect(castle_courtyard, r.bush)
castle_frontdoor.connect(ukuku_prairie, "CASTLE_BUTTON") # the button in the castle connector allows access to the castle grounds in ER
self._addEntrance("castle_secret_entrance", next_to_castle, castle_secret_entrance_right, OR(BOMB, BOOMERANG, MAGIC_POWDER, MAGIC_ROD, SWORD))
self._addEntrance("castle_secret_exit", castle_courtyard, castle_secret_entrance_left, None)
Location().add(HeartPiece(0x078)).connect(bay_water, FLIPPERS) # in the moat of the castle
castle_inside = Location()
Location().add(KeyLocation("CASTLE_BUTTON")).connect(castle_inside, None)
castle_top_outside = Location()
castle_top_inside = Location()
self._addEntrance("castle_main_entrance", castle_frontdoor, castle_inside, r.bush)
self._addEntrance("castle_upper_left", castle_top_outside, castle_inside, None)
self._addEntrance("castle_upper_right", castle_top_outside, castle_top_inside, None)
Location().add(GoldLeaf(0x05A)).connect(castle_courtyard, OR(SWORD, BOW, MAGIC_ROD)) # mad bomber, enemy hiding in the 6 holes
crow_gold_leaf = Location().add(GoldLeaf(0x058)).connect(castle_courtyard, AND(POWER_BRACELET, r.attack_hookshot_no_bomb)) # bird on tree, can't kill with bomb cause it flies off. immune to magic_powder
Location().add(GoldLeaf(0x2D2)).connect(castle_inside, r.attack_hookshot_powder) # in the castle, kill enemies
Location().add(GoldLeaf(0x2C5)).connect(castle_inside, AND(BOMB, r.attack_hookshot_powder)) # in the castle, bomb wall to show enemy
kanalet_chain_trooper = Location().add(GoldLeaf(0x2C6)) # in the castle, spinning spikeball enemy
castle_top_inside.connect(kanalet_chain_trooper, AND(POWER_BRACELET, r.attack_hookshot), one_way=True)
animal_village = Location()
animal_village.connect(Location().add(TradeSequenceItem(0x0CD, TRADING_ITEM_FISHING_HOOK)), TRADING_ITEM_BROOM)
cookhouse = Location()
cookhouse.connect(Location().add(TradeSequenceItem(0x2D7, TRADING_ITEM_PINEAPPLE)), TRADING_ITEM_HONEYCOMB)
goathouse = Location()
goathouse.connect(Location().add(TradeSequenceItem(0x2D9, TRADING_ITEM_LETTER)), TRADING_ITEM_HIBISCUS)
mermaid_statue = Location()
mermaid_statue.connect(animal_village, AND(TRADING_ITEM_SCALE, HOOKSHOT))
mermaid_statue.add(TradeSequenceItem(0x297, TRADING_ITEM_MAGNIFYING_GLASS))
self._addEntrance("animal_phone", animal_village, None, None)
self._addEntrance("animal_house1", animal_village, None, None)
self._addEntrance("animal_house2", animal_village, None, None)
self._addEntrance("animal_house3", animal_village, goathouse, None)
self._addEntrance("animal_house4", animal_village, None, None)
self._addEntrance("animal_house5", animal_village, cookhouse, None)
animal_village.connect(bay_water, FLIPPERS)
animal_village.connect(ukuku_prairie, OR(HOOKSHOT, ROOSTER))
animal_village_connector_left = Location()
animal_village_connector_right = Location().connect(animal_village_connector_left, PEGASUS_BOOTS)
self._addEntrance("prairie_to_animal_connector", ukuku_prairie, animal_village_connector_left, OR(BOMB, BOOMERANG, MAGIC_POWDER, MAGIC_ROD, SWORD)) # passage under river blocked by bush
self._addEntrance("animal_to_prairie_connector", animal_village, animal_village_connector_right, None)
if options.owlstatues == "both" or options.owlstatues == "overworld":
animal_village.add(OwlStatue(0x0DA))
Location().add(Seashell(0x0DA)).connect(animal_village, SHOVEL) # owl statue at the water
desert = Location().connect(animal_village, r.bush) # Note: We moved the walrus blocking the desert.
if options.owlstatues == "both" or options.owlstatues == "overworld":
desert.add(OwlStatue(0x0CF))
desert_lanmola = Location().add(AnglerKey()).connect(desert, OR(BOW, SWORD, HOOKSHOT, MAGIC_ROD, BOOMERANG))
animal_village_bombcave = Location()
self._addEntrance("animal_cave", desert, animal_village_bombcave, BOMB)
self._addEntranceRequirementExit("animal_cave", None) # if exiting, you do not need bombs
animal_village_bombcave_heartpiece = Location().add(HeartPiece(0x2E6)).connect(animal_village_bombcave, OR(AND(BOMB, FEATHER, HOOKSHOT), ROOSTER)) # cave in the upper right of animal town
desert_cave = Location()
self._addEntrance("desert_cave", desert, desert_cave, None)
desert.connect(desert_cave, None, one_way=True) # Drop down the sinkhole
Location().add(HeartPiece(0x1E8)).connect(desert_cave, BOMB) # above the quicksand cave
Location().add(Seashell(0x0FF)).connect(desert, POWER_BRACELET) # bottom right corner of the map
armos_maze = Location().connect(animal_village, POWER_BRACELET)
armos_temple = Location()
Location().add(FaceKey()).connect(armos_temple, r.miniboss_requirements[world_setup.miniboss_mapping["armos_temple"]])
if options.owlstatues == "both" or options.owlstatues == "overworld":
armos_maze.add(OwlStatue(0x08F))
self._addEntrance("armos_maze_cave", armos_maze, Location().add(Chest(0x2FC)), None)
self._addEntrance("armos_temple", armos_maze, armos_temple, None)
armos_fairy_entrance = Location().connect(bay_water, FLIPPERS).connect(animal_village, POWER_BRACELET)
self._addEntrance("armos_fairy", armos_fairy_entrance, None, BOMB)
self._addEntranceRequirementExit("armos_fairy", None) # if exiting, you do not need bombs
d6_connector_left = Location()
d6_connector_right = Location().connect(d6_connector_left, OR(AND(HOOKSHOT, OR(FLIPPERS, AND(FEATHER, PEGASUS_BOOTS))), ROOSTER))
d6_entrance = Location()
d6_entrance.connect(bay_water, FLIPPERS, one_way=True)
d6_armos_island = Location().connect(bay_water, FLIPPERS)
self._addEntrance("d6_connector_entrance", d6_armos_island, d6_connector_right, None)
self._addEntrance("d6_connector_exit", d6_entrance, d6_connector_left, None)
self._addEntrance("d6", d6_entrance, None, FACE_KEY)
self._addEntranceRequirementExit("d6", None) # if exiting, you do not need to open the dungeon
windfish_egg = Location().connect(swamp, POWER_BRACELET).connect(graveyard, POWER_BRACELET)
windfish_egg.connect(graveyard, None, one_way=True) # Ledge jump
obstacle_cave_entrance = Location()
obstacle_cave_inside = Location().connect(obstacle_cave_entrance, SWORD)
obstacle_cave_inside.connect(obstacle_cave_entrance, FEATHER, one_way=True) # can get past the rock room from right to left pushing blocks and jumping over the pit
obstacle_cave_inside_chest = Location().add(Chest(0x2BB)).connect(obstacle_cave_inside, OR(HOOKSHOT, ROOSTER)) # chest at obstacles
obstacle_cave_exit = Location().connect(obstacle_cave_inside, OR(PEGASUS_BOOTS, ROOSTER))
lower_right_taltal = Location()
self._addEntrance("obstacle_cave_entrance", windfish_egg, obstacle_cave_entrance, POWER_BRACELET)
self._addEntrance("obstacle_cave_outside_chest", Location().add(Chest(0x018)), obstacle_cave_inside, None)
self._addEntrance("obstacle_cave_exit", lower_right_taltal, obstacle_cave_exit, None)
papahl_cave = Location().add(Chest(0x28A))
papahl = Location().connect(lower_right_taltal, None, one_way=True)
hibiscus_item = Location().add(TradeSequenceItem(0x019, TRADING_ITEM_HIBISCUS))
papahl.connect(hibiscus_item, TRADING_ITEM_PINEAPPLE, one_way=True)
self._addEntrance("papahl_entrance", lower_right_taltal, papahl_cave, None)
self._addEntrance("papahl_exit", papahl, papahl_cave, None)
# D4 entrance and related things
below_right_taltal = Location().connect(windfish_egg, POWER_BRACELET)
below_right_taltal.add(KeyLocation("ANGLER_KEYHOLE"))
below_right_taltal.connect(bay_water, FLIPPERS)
below_right_taltal.connect(next_to_castle, ROOSTER) # fly from staircase to staircase on the north side of the moat
lower_right_taltal.connect(below_right_taltal, FLIPPERS, one_way=True)
heartpiece_swim_cave = Location().connect(Location().add(HeartPiece(0x1F2)), FLIPPERS)
self._addEntrance("heartpiece_swim_cave", below_right_taltal, heartpiece_swim_cave, FLIPPERS) # cave next to level 4
d4_entrance = Location().connect(below_right_taltal, FLIPPERS)
lower_right_taltal.connect(d4_entrance, AND(ANGLER_KEY, "ANGLER_KEYHOLE"), one_way=True)
self._addEntrance("d4", d4_entrance, None, ANGLER_KEY)
self._addEntranceRequirementExit("d4", FLIPPERS) # if exiting, you can leave with flippers without opening the dungeon
mambo = Location().connect(Location().add(Song(0x2FD)), AND(OCARINA, FLIPPERS)) # Manbo's Mambo
self._addEntrance("mambo", d4_entrance, mambo, FLIPPERS)
# Raft game.
raft_house = Location("Raft House")
Location().add(KeyLocation("RAFT")).connect(raft_house, COUNT("RUPEES", 100))
raft_return_upper = Location()
raft_return_lower = Location().connect(raft_return_upper, None, one_way=True)
outside_raft_house = Location().connect(below_right_taltal, HOOKSHOT).connect(below_right_taltal, FLIPPERS, one_way=True)
raft_game = Location()
raft_game.connect(outside_raft_house, "RAFT")
raft_game.add(Chest(0x05C), Chest(0x05D)) # Chests in the rafting game
raft_exit = Location()
if options.logic != "casual": # use raft to reach north armos maze entrances without flippers
raft_game.connect(raft_exit, None, one_way=True)
raft_game.connect(armos_fairy_entrance, None, one_way=True)
self._addEntrance("raft_return_exit", outside_raft_house, raft_return_upper, None)
self._addEntrance("raft_return_enter", raft_exit, raft_return_lower, None)
raft_exit.connect(armos_fairy_entrance, FLIPPERS)
self._addEntrance("raft_house", outside_raft_house, raft_house, None)
if options.owlstatues == "both" or options.owlstatues == "overworld":
raft_game.add(OwlStatue(0x5D))
outside_rooster_house = Location().connect(lower_right_taltal, OR(FLIPPERS, ROOSTER))
self._addEntrance("rooster_house", outside_rooster_house, None, None)
bird_cave = Location()
bird_key = Location().add(BirdKey())
bird_cave.connect(bird_key, OR(AND(FEATHER, COUNT(POWER_BRACELET, 2)), ROOSTER))
if options.logic != "casual":
bird_cave.connect(lower_right_taltal, None, one_way=True) # Drop in a hole at bird cave
self._addEntrance("bird_cave", outside_rooster_house, bird_cave, None)
bridge_seashell = Location().add(Seashell(0x00C)).connect(outside_rooster_house, AND(OR(FEATHER, ROOSTER), POWER_BRACELET)) # seashell right of rooster house, there is a hole in the bridge
multichest_cave = Location()
multichest_cave_secret = Location().connect(multichest_cave, BOMB)
water_cave_hole = Location() # Location with the hole that drops you onto the hearth piece under water
if options.logic != "casual":
water_cave_hole.connect(heartpiece_swim_cave, FLIPPERS, one_way=True)
multichest_outside = Location().add(Chest(0x01D)) # chest after multichest puzzle outside
self._addEntrance("multichest_left", lower_right_taltal, multichest_cave, OR(FLIPPERS, ROOSTER))
self._addEntrance("multichest_right", water_cave_hole, multichest_cave, None)
self._addEntrance("multichest_top", multichest_outside, multichest_cave_secret, None)
if options.owlstatues == "both" or options.owlstatues == "overworld":
water_cave_hole.add(OwlStatue(0x1E)) # owl statue below d7
right_taltal_connector1 = Location()
right_taltal_connector_outside1 = Location()
right_taltal_connector2 = Location()
right_taltal_connector3 = Location()
right_taltal_connector2.connect(right_taltal_connector3, AND(OR(FEATHER, ROOSTER), HOOKSHOT), one_way=True)
right_taltal_connector_outside2 = Location()
right_taltal_connector4 = Location()
d7_platau = Location()
d7_tower = Location()
d7_platau.connect(d7_tower, AND(POWER_BRACELET, BIRD_KEY), one_way=True)
self._addEntrance("right_taltal_connector1", water_cave_hole, right_taltal_connector1, None)
self._addEntrance("right_taltal_connector2", right_taltal_connector_outside1, right_taltal_connector1, None)
self._addEntrance("right_taltal_connector3", right_taltal_connector_outside1, right_taltal_connector2, None)
self._addEntrance("right_taltal_connector4", right_taltal_connector_outside2, right_taltal_connector3, None)
self._addEntrance("right_taltal_connector5", right_taltal_connector_outside2, right_taltal_connector4, None)
self._addEntrance("right_taltal_connector6", d7_platau, right_taltal_connector4, None)
self._addEntrance("right_fairy", right_taltal_connector_outside2, None, BOMB)
self._addEntranceRequirementExit("right_fairy", None) # if exiting, you do not need bombs
self._addEntrance("d7", d7_tower, None, None)
if options.logic != "casual": # D7 area ledge drops
d7_platau.connect(heartpiece_swim_cave, FLIPPERS, one_way=True)
d7_platau.connect(right_taltal_connector_outside1, None, one_way=True)
mountain_bridge_staircase = Location().connect(outside_rooster_house, OR(HOOKSHOT, ROOSTER)) # cross bridges to staircase
if options.logic != "casual": # ledge drop
mountain_bridge_staircase.connect(windfish_egg, None, one_way=True)
left_right_connector_cave_entrance = Location()
left_right_connector_cave_exit = Location()
left_right_connector_cave_entrance.connect(left_right_connector_cave_exit, OR(HOOKSHOT, ROOSTER), one_way=True) # pass through the underground passage to left side
taltal_boulder_zone = Location()
self._addEntrance("left_to_right_taltalentrance", mountain_bridge_staircase, left_right_connector_cave_entrance, OR(BOMB, BOOMERANG, MAGIC_POWDER, MAGIC_ROD, SWORD))
self._addEntrance("left_taltal_entrance", taltal_boulder_zone, left_right_connector_cave_exit, None)
mountain_heartpiece = Location().add(HeartPiece(0x2BA)) # heartpiece in connecting cave
left_right_connector_cave_entrance.connect(mountain_heartpiece, BOMB, one_way=True) # in the connecting cave from right to left. one_way to prevent access to left_side_mountain via glitched logic
taltal_boulder_zone.add(Chest(0x004)) # top of falling rocks hill
taltal_madbatter = Location().connect(Location().add(MadBatter(0x1E2)), MAGIC_POWDER)
self._addEntrance("madbatter_taltal", taltal_boulder_zone, taltal_madbatter, POWER_BRACELET)
self._addEntranceRequirementExit("madbatter_taltal", None) # if exiting, you do not need bracelet
outside_fire_cave = Location()
if options.logic != "casual":
outside_fire_cave.connect(writes_hut_outside, None, one_way=True) # Jump down the ledge
taltal_boulder_zone.connect(outside_fire_cave, None, one_way=True)
fire_cave_bottom = Location()
fire_cave_top = Location().connect(fire_cave_bottom, COUNT(SHIELD, 2))
self._addEntrance("fire_cave_entrance", outside_fire_cave, fire_cave_bottom, BOMB)
self._addEntranceRequirementExit("fire_cave_entrance", None) # if exiting, you do not need bombs
d8_entrance = Location()
if options.logic != "casual":
d8_entrance.connect(writes_hut_outside, None, one_way=True) # Jump down the ledge
d8_entrance.connect(outside_fire_cave, None, one_way=True) # Jump down the other ledge
self._addEntrance("fire_cave_exit", d8_entrance, fire_cave_top, None)
self._addEntrance("phone_d8", d8_entrance, None, None)
self._addEntrance("d8", d8_entrance, None, AND(OCARINA, SONG3, SWORD))
self._addEntranceRequirementExit("d8", None) # if exiting, you do not need to wake the turtle
nightmare = Location("Nightmare")
windfish = Location("Windfish").connect(nightmare, AND(MAGIC_POWDER, SWORD, OR(BOOMERANG, BOW)))
if options.logic == 'hard' or options.logic == 'glitched' or options.logic == 'hell':
hookshot_cave.connect(hookshot_cave_chest, AND(FEATHER, PEGASUS_BOOTS)) # boots jump the gap to the chest
graveyard_cave_left.connect(graveyard_cave_right, HOOKSHOT, one_way=True) # hookshot the block behind the stairs while over the pit
swamp_chest.connect(swamp, None) # Clip past the flower
self._addEntranceRequirement("d2", POWER_BRACELET) # clip the top wall to walk between the goponga flower and the wall
self._addEntranceRequirement("d2", COUNT(SWORD, 2)) # use l2 sword spin to kill goponga flowers
swamp.connect(writes_hut_outside, HOOKSHOT, one_way=True) # hookshot the sign in front of writes hut
graveyard_heartpiece.connect(graveyard_cave_right, FEATHER) # jump to the bottom right tile around the blocks
graveyard_heartpiece.connect(graveyard_cave_right, OR(HOOKSHOT, BOOMERANG)) # push bottom block, wall clip and hookshot/boomerang corner to grab item
self._addEntranceRequirement("mamu", AND(FEATHER, POWER_BRACELET)) # can clear the gaps at the start with just feather, can reach bottom left sign with a well timed jump while wall clipped
self._addEntranceRequirement("prairie_madbatter_connector_entrance", AND(OR(FEATHER, ROOSTER), OR(MAGIC_POWDER, BOMB))) # use bombs or powder to get rid of a bush on the other side by jumping across and placing the bomb/powder before you fall into the pit
fisher_under_bridge.connect(bay_water, AND(TRADING_ITEM_FISHING_HOOK, FLIPPERS)) # can talk to the fisherman from the water when the boat is low (requires swimming up out of the water a bit)
crow_gold_leaf.connect(castle_courtyard, POWER_BRACELET) # bird on tree at left side kanalet, can use both rocks to kill the crow removing the kill requirement
castle_inside.connect(kanalet_chain_trooper, BOOMERANG, one_way=True) # kill the ball and chain trooper from the left side, then use boomerang to grab the dropped item
animal_village_bombcave_heartpiece.connect(animal_village_bombcave, AND(PEGASUS_BOOTS, FEATHER)) # jump across horizontal 4 gap to heart piece
desert_lanmola.connect(desert, BOMB) # use bombs to kill lanmola
d6_connector_left.connect(d6_connector_right, AND(OR(FLIPPERS, PEGASUS_BOOTS), FEATHER)) # jump the gap in underground passage to d6 left side to skip hookshot
bird_key.connect(bird_cave, COUNT(POWER_BRACELET, 2)) # corner walk past the one pit on the left side to get to the elephant statue
fire_cave_bottom.connect(fire_cave_top, PEGASUS_BOOTS, one_way=True) # flame skip
if options.logic == 'glitched' or options.logic == 'hell':
#self._addEntranceRequirement("dream_hut", FEATHER) # text clip TODO: require nag messages
self._addEntranceRequirementEnter("dream_hut", HOOKSHOT) # clip past the rocks in front of dream hut
dream_hut_right.connect(dream_hut_left, FEATHER) # super jump
forest.connect(swamp, BOMB) # bomb trigger tarin
forest.connect(forest_heartpiece, BOMB, one_way=True) # bomb trigger heartpiece
self._addEntranceRequirementEnter("hookshot_cave", HOOKSHOT) # clip past the rocks in front of hookshot cave
swamp.connect(forest_toadstool, None, one_way=True) # villa buffer from top (swamp phonebooth area) to bottom (toadstool area)
writes_hut_outside.connect(swamp, None, one_way=True) # villa buffer from top (writes hut) to bottom (swamp phonebooth area) or damage boost
graveyard.connect(forest_heartpiece, None, one_way=True) # villa buffer from top.
log_cave_heartpiece.connect(forest_cave, FEATHER) # super jump
log_cave_heartpiece.connect(forest_cave, BOMB) # bomb trigger
graveyard_cave_left.connect(graveyard_heartpiece, BOMB, one_way=True) # bomb trigger the heartpiece from the left side
graveyard_heartpiece.connect(graveyard_cave_right, None) # sideways block push from the right staircase.
prairie_island_seashell.connect(ukuku_prairie, AND(FEATHER, r.bush)) # jesus jump from right side, screen transition on top of the water to reach the island
self._addEntranceRequirement("castle_jump_cave", FEATHER) # 1 pit buffer to clip bottom wall and jump across.
left_bay_area.connect(ghost_hut_outside, FEATHER) # 1 pit buffer to get across
tiny_island.connect(left_bay_area, AND(FEATHER, r.bush)) # jesus jump around
bay_madbatter_connector_exit.connect(bay_madbatter_connector_entrance, FEATHER, one_way=True) # jesus jump (3 screen) through the underground passage leading to martha's bay mad batter
self._addEntranceRequirement("prairie_madbatter_connector_entrance", AND(FEATHER, POWER_BRACELET)) # villa buffer into the top side of the bush, then pick it up
ukuku_prairie.connect(richard_maze, OR(BOMB, BOOMERANG, MAGIC_POWDER, MAGIC_ROD, SWORD), one_way=True) # break bushes on north side of the maze, and 1 pit buffer into the maze
fisher_under_bridge.connect(bay_water, AND(BOMB, FLIPPERS)) # can bomb trigger the item without having the hook
animal_village.connect(ukuku_prairie, FEATHER) # jesus jump
below_right_taltal.connect(next_to_castle, FEATHER) # jesus jump (north of kanalet castle phonebooth)
animal_village_connector_right.connect(animal_village_connector_left, FEATHER) # text clip past the obstacles (can go both ways), feather to wall clip the obstacle without triggering text or shaq jump in bottom right corner if text is off
animal_village_bombcave_heartpiece.connect(animal_village_bombcave, AND(BOMB, OR(HOOKSHOT, FEATHER, PEGASUS_BOOTS))) # bomb trigger from right side, corner walking top right pit is stupid so hookshot or boots added
animal_village_bombcave_heartpiece.connect(animal_village_bombcave, FEATHER) # villa buffer across the pits
d6_entrance.connect(ukuku_prairie, FEATHER, one_way=True) # jesus jump (2 screen) from d6 entrance bottom ledge to ukuku prairie
d6_entrance.connect(armos_fairy_entrance, FEATHER, one_way=True) # jesus jump (2 screen) from d6 entrance top ledge to armos fairy entrance
armos_fairy_entrance.connect(d6_armos_island, FEATHER, one_way=True) # jesus jump from top (fairy bomb cave) to armos island
armos_fairy_entrance.connect(raft_exit, FEATHER) # jesus jump (2-ish screen) from fairy cave to lower raft connector
self._addEntranceRequirementEnter("obstacle_cave_entrance", HOOKSHOT) # clip past the rocks in front of obstacle cave entrance
obstacle_cave_inside_chest.connect(obstacle_cave_inside, FEATHER) # jump to the rightmost pits + 1 pit buffer to jump across
obstacle_cave_exit.connect(obstacle_cave_inside, FEATHER) # 1 pit buffer above boots crystals to get past
lower_right_taltal.connect(hibiscus_item, AND(TRADING_ITEM_PINEAPPLE, BOMB), one_way=True) # bomb trigger papahl from below ledge, requires pineapple
self._addEntranceRequirement("heartpiece_swim_cave", FEATHER) # jesus jump into the cave entrance after jumping down the ledge, can jesus jump back to the ladder 1 screen below
self._addEntranceRequirement("mambo", FEATHER) # jesus jump from (unlocked) d4 entrance to mambo's cave entrance
outside_raft_house.connect(below_right_taltal, FEATHER, one_way=True) # jesus jump from the ledge at raft to the staircase 1 screen south
self._addEntranceRequirement("multichest_left", FEATHER) # jesus jump past staircase leading up the mountain
outside_rooster_house.connect(lower_right_taltal, FEATHER) # jesus jump (1 or 2 screen depending if angler key is used) to staircase leading up the mountain
d7_platau.connect(water_cave_hole, None, one_way=True) # use save and quit menu to gain control while falling to dodge the water cave hole
mountain_bridge_staircase.connect(outside_rooster_house, AND(PEGASUS_BOOTS, FEATHER)) # cross bridge to staircase with pit buffer to clip bottom wall and jump across
bird_key.connect(bird_cave, AND(FEATHER, HOOKSHOT)) # hookshot jump across the big pits room
right_taltal_connector2.connect(right_taltal_connector3, None, one_way=True) # 2 seperate pit buffers so not obnoxious to get past the two pit rooms before d7 area. 2nd pits can pit buffer on top right screen, bottom wall to scroll on top of the wall on bottom screen
left_right_connector_cave_exit.connect(left_right_connector_cave_entrance, AND(HOOKSHOT, FEATHER), one_way=True) # pass through the passage in reverse using a superjump to get out of the dead end
obstacle_cave_inside.connect(mountain_heartpiece, BOMB, one_way=True) # bomb trigger from boots crystal cave
self._addEntranceRequirement("d8", OR(BOMB, AND(OCARINA, SONG3))) # bomb trigger the head and walk trough, or play the ocarina song 3 and walk through
if options.logic == 'hell':
dream_hut_right.connect(dream_hut, None) # alternate diagonal movement with orthogonal movement to control the mimics. Get them clipped into the walls to walk past
swamp.connect(forest_toadstool, None) # damage boost from toadstool area across the pit
swamp.connect(forest, AND(r.bush, OR(PEGASUS_BOOTS, HOOKSHOT))) # boots bonk / hookshot spam over the pits right of forest_rear_chest
forest.connect(forest_heartpiece, PEGASUS_BOOTS, one_way=True) # boots bonk across the pits
log_cave_heartpiece.connect(forest_cave, BOOMERANG) # clip the boomerang through the corner gaps on top right to grab the item
log_cave_heartpiece.connect(forest_cave, AND(ROOSTER, OR(PEGASUS_BOOTS, SWORD, BOW, MAGIC_ROD))) # boots rooster hop in bottom left corner to "superjump" into the area. use buffers after picking up rooster to gain height / time to throw rooster again facing up
writes_hut_outside.connect(swamp, None) # damage boost with moblin arrow next to telephone booth
writes_cave_left_chest.connect(writes_cave, None) # damage boost off the zol to get across the pit.
graveyard.connect(crazy_tracy_hut, HOOKSHOT, one_way=True) # use hookshot spam to clip the rock on the right with the crow
graveyard.connect(forest, OR(PEGASUS_BOOTS, HOOKSHOT)) # boots bonk witches hut, or hookshot spam across the pit
graveyard_cave_left.connect(graveyard_cave_right, HOOKSHOT) # hookshot spam over the pit
graveyard_cave_right.connect(graveyard_cave_left, PEGASUS_BOOTS, one_way=True) # boots bonk off the cracked block
self._addEntranceRequirementEnter("mamu", AND(PEGASUS_BOOTS, POWER_BRACELET)) # can clear the gaps at the start with multiple pit buffers, can reach bottom left sign with bonking along the bottom wall
self._addEntranceRequirement("castle_jump_cave", PEGASUS_BOOTS) # pit buffer to clip bottom wall and boots bonk across
prairie_cave_secret_exit.connect(prairie_cave, AND(BOMB, OR(PEGASUS_BOOTS, HOOKSHOT))) # hookshot spam or boots bonk across pits can go from left to right by pit buffering on top of the bottom wall then boots bonk across
richard_cave_chest.connect(richard_cave, None) # use the zol on the other side of the pit to damage boost across (requires damage from pit + zol)
castle_secret_entrance_right.connect(castle_secret_entrance_left, AND(PEGASUS_BOOTS, "MEDICINE2")) # medicine iframe abuse to get across spikes with a boots bonk
left_bay_area.connect(ghost_hut_outside, PEGASUS_BOOTS) # multiple pit buffers to bonk across the bottom wall
tiny_island.connect(left_bay_area, AND(PEGASUS_BOOTS, r.bush)) # jesus jump around with boots bonks, then one final bonk off the bottom wall to get on the staircase (needs to be centered correctly)
self._addEntranceRequirement("prairie_madbatter_connector_entrance", AND(PEGASUS_BOOTS, OR(MAGIC_POWDER, BOMB, SWORD, MAGIC_ROD, BOOMERANG))) # Boots bonk across the bottom wall, then remove one of the bushes to get on land
self._addEntranceRequirementExit("prairie_madbatter_connector_entrance", AND(PEGASUS_BOOTS, r.bush)) # if exiting, you can pick up the bushes by normal means and boots bonk across the bottom wall
# bay_water connectors, only left_bay_area, ukuku_prairie and animal_village have to be connected with jesus jumps. below_right_taltal, d6_armos_island and armos_fairy_entrance are accounted for via ukuku prairie in glitch logic
left_bay_area.connect(bay_water, FEATHER) # jesus jump (can always reach bay_water with jesus jumping from every way to enter bay_water, so no one_way)
animal_village.connect(bay_water, FEATHER) # jesus jump (can always reach bay_water with jesus jumping from every way to enter bay_water, so no one_way)
ukuku_prairie.connect(bay_water, FEATHER, one_way=True) # jesus jump
bay_water.connect(d5_entrance, FEATHER) # jesus jump into d5 entrance (wall clip), wall clip + jesus jump to get out
crow_gold_leaf.connect(castle_courtyard, BOMB) # bird on tree at left side kanalet, place a bomb against the tree and the crow flies off. With well placed second bomb the crow can be killed
mermaid_statue.connect(animal_village, AND(TRADING_ITEM_SCALE, FEATHER)) # early mermaid statue by buffering on top of the right ledge, then superjumping to the left (horizontal pixel perfect)
animal_village_bombcave_heartpiece.connect(animal_village_bombcave, PEGASUS_BOOTS) # boots bonk across bottom wall (both at entrance and in item room)
d6_armos_island.connect(ukuku_prairie, FEATHER) # jesus jump (3 screen) from seashell mansion to armos island
armos_fairy_entrance.connect(d6_armos_island, PEGASUS_BOOTS, one_way=True) # jesus jump from top (fairy bomb cave) to armos island with fast falling
d6_connector_right.connect(d6_connector_left, PEGASUS_BOOTS) # boots bonk across bottom wall at water and pits (can do both ways)
obstacle_cave_entrance.connect(obstacle_cave_inside, OR(HOOKSHOT, AND(FEATHER, PEGASUS_BOOTS, OR(SWORD, MAGIC_ROD, BOW)))) # get past crystal rocks by hookshotting into top pushable block, or boots dashing into top wall where the pushable block is to superjump down
obstacle_cave_entrance.connect(obstacle_cave_inside, AND(PEGASUS_BOOTS, ROOSTER)) # get past crystal rocks pushing the top pushable block, then boots dashing up picking up the rooster before bonking. Pause buffer until rooster is fully picked up then throw it down before bonking into wall
d4_entrance.connect(below_right_taltal, FEATHER) # jesus jump a long way
if options.entranceshuffle in ("default", "simple"): # connector cave from armos d6 area to raft shop may not be randomized to add a flippers path since flippers stop you from jesus jumping
below_right_taltal.connect(raft_game, AND(FEATHER, r.attack_hookshot_powder), one_way=True) # jesus jump from heartpiece water cave, around the island and clip past the diagonal gap in the rock, then jesus jump all the way down the waterfall to the chests (attack req for hardlock flippers+feather scenario)
outside_raft_house.connect(below_right_taltal, AND(FEATHER, PEGASUS_BOOTS)) #superjump from ledge left to right, can buffer to land on ledge instead of water, then superjump right which is pixel perfect
bridge_seashell.connect(outside_rooster_house, AND(PEGASUS_BOOTS, POWER_BRACELET)) # boots bonk
bird_key.connect(bird_cave, AND(FEATHER, PEGASUS_BOOTS)) # boots jump above wall, use multiple pit buffers to get across
mountain_bridge_staircase.connect(outside_rooster_house, OR(PEGASUS_BOOTS, FEATHER)) # cross bridge to staircase with pit buffer to clip bottom wall and jump or boots bonk across
left_right_connector_cave_entrance.connect(left_right_connector_cave_exit, AND(PEGASUS_BOOTS, FEATHER), one_way=True) # boots jump to bottom left corner of pits, pit buffer and jump to left
left_right_connector_cave_exit.connect(left_right_connector_cave_entrance, AND(ROOSTER, OR(PEGASUS_BOOTS, SWORD, BOW, MAGIC_ROD)), one_way=True) # pass through the passage in reverse using a boots rooster hop or rooster superjump in the one way passage area
self.start = start_house
self.egg = windfish_egg
self.nightmare = nightmare
self.windfish = windfish
def _addEntrance(self, name, outside, inside, requirement):
assert name not in self.overworld_entrance, "Duplicate entrance: %s" % name
assert name in ENTRANCE_INFO
self.overworld_entrance[name] = EntranceExterior(outside, requirement)
self.indoor_location[name] = inside
def _addEntranceRequirement(self, name, requirement):
assert name in self.overworld_entrance
self.overworld_entrance[name].addRequirement(requirement)
def _addEntranceRequirementEnter(self, name, requirement):
assert name in self.overworld_entrance
self.overworld_entrance[name].addEnterRequirement(requirement)
def _addEntranceRequirementExit(self, name, requirement):
assert name in self.overworld_entrance
self.overworld_entrance[name].addExitRequirement(requirement)
def updateIndoorLocation(self, name, location):
assert name in self.indoor_location
assert self.indoor_location[name] is None
self.indoor_location[name] = location
class DungeonDiveOverworld:
def __init__(self, options, r):
self.overworld_entrance = {}
self.indoor_location = {}
start_house = Location("Start House").add(StartItem())
Location().add(ShopItem(0)).connect(start_house, OR(COUNT("RUPEES", 200), SWORD))
Location().add(ShopItem(1)).connect(start_house, OR(COUNT("RUPEES", 980), SWORD))
Location().add(Song(0x0B1)).connect(start_house, OCARINA) # Marins song
start_house.add(DroppedKey(0xB2)) # Sword on the beach
egg = Location().connect(start_house, AND(r.bush, BOMB))
Location().add(MadBatter(0x1E1)).connect(start_house, MAGIC_POWDER)
if options.boomerang == 'trade':
Location().add(BoomerangGuy()).connect(start_house, AND(BOMB, OR(BOOMERANG, HOOKSHOT, MAGIC_ROD, PEGASUS_BOOTS, FEATHER, SHOVEL)))
elif options.boomerang == 'gift':
Location().add(BoomerangGuy()).connect(start_house, BOMB)
nightmare = Location("Nightmare")
windfish = Location("Windfish").connect(nightmare, AND(MAGIC_POWDER, SWORD, OR(BOOMERANG, BOW)))
self.start = start_house
self.overworld_entrance = {
"d1": EntranceExterior(start_house, None),
"d2": EntranceExterior(start_house, None),
"d3": EntranceExterior(start_house, None),
"d4": EntranceExterior(start_house, None),
"d5": EntranceExterior(start_house, FLIPPERS),
"d6": EntranceExterior(start_house, None),
"d7": EntranceExterior(start_house, None),
"d8": EntranceExterior(start_house, None),
"d0": EntranceExterior(start_house, None),
}
self.egg = egg
self.nightmare = nightmare
self.windfish = windfish
def updateIndoorLocation(self, name, location):
self.indoor_location[name] = location
class EntranceExterior:
def __init__(self, outside, requirement, one_way_enter_requirement="UNSET", one_way_exit_requirement="UNSET"):
self.location = outside
self.requirement = requirement
self.one_way_enter_requirement = one_way_enter_requirement
self.one_way_exit_requirement = one_way_exit_requirement
def addRequirement(self, new_requirement):
self.requirement = OR(self.requirement, new_requirement)
def addExitRequirement(self, new_requirement):
if self.one_way_exit_requirement == "UNSET":
self.one_way_exit_requirement = new_requirement
else:
self.one_way_exit_requirement = OR(self.one_way_exit_requirement, new_requirement)
def addEnterRequirement(self, new_requirement):
if self.one_way_enter_requirement == "UNSET":
self.one_way_enter_requirement = new_requirement
else:
self.one_way_enter_requirement = OR(self.one_way_enter_requirement, new_requirement)
def enterIsSet(self):
return self.one_way_enter_requirement != "UNSET"
def exitIsSet(self):
return self.one_way_exit_requirement != "UNSET"

View File

@@ -0,0 +1,318 @@
from typing import Optional
from ..locations.items import *
class OR:
__slots__ = ('__items', '__children')
def __new__(cls, *args):
if True in args:
return True
return super().__new__(cls)
def __init__(self, *args):
self.__items = [item for item in args if isinstance(item, str)]
self.__children = [item for item in args if type(item) not in (bool, str) and item is not None]
assert self.__items or self.__children, args
def __repr__(self) -> str:
return "or%s" % (self.__items+self.__children)
def remove(self, item) -> None:
if item in self.__items:
self.__items.remove(item)
def hasConsumableRequirement(self) -> bool:
for item in self.__items:
if isConsumable(item):
print("Consumable OR requirement? %r" % self)
return True
for child in self.__children:
if child.hasConsumableRequirement():
print("Consumable OR requirement? %r" % self)
return True
return False
def test(self, inventory) -> bool:
for item in self.__items:
if item in inventory:
return True
for child in self.__children:
if child.test(inventory):
return True
return False
def consume(self, inventory) -> bool:
for item in self.__items:
if item in inventory:
if isConsumable(item):
inventory[item] -= 1
if inventory[item] == 0:
del inventory[item]
inventory["%s_USED" % item] = inventory.get("%s_USED" % item, 0) + 1
return True
for child in self.__children:
if child.consume(inventory):
return True
return False
def getItems(self, inventory, target_set) -> None:
if self.test(inventory):
return
for item in self.__items:
target_set.add(item)
for child in self.__children:
child.getItems(inventory, target_set)
def copyWithModifiedItemNames(self, f) -> "OR":
return OR(*(f(item) for item in self.__items), *(child.copyWithModifiedItemNames(f) for child in self.__children))
class AND:
__slots__ = ('__items', '__children')
def __new__(cls, *args):
if False in args:
return False
return super().__new__(cls)
def __init__(self, *args):
self.__items = [item for item in args if isinstance(item, str)]
self.__children = [item for item in args if type(item) not in (bool, str) and item is not None]
def __repr__(self) -> str:
return "and%s" % (self.__items+self.__children)
def remove(self, item) -> None:
if item in self.__items:
self.__items.remove(item)
def hasConsumableRequirement(self) -> bool:
for item in self.__items:
if isConsumable(item):
return True
for child in self.__children:
if child.hasConsumableRequirement():
return True
return False
def test(self, inventory) -> bool:
for item in self.__items:
if item not in inventory:
return False
for child in self.__children:
if not child.test(inventory):
return False
return True
def consume(self, inventory) -> bool:
for item in self.__items:
if isConsumable(item):
inventory[item] -= 1
if inventory[item] == 0:
del inventory[item]
inventory["%s_USED" % item] = inventory.get("%s_USED" % item, 0) + 1
for child in self.__children:
if not child.consume(inventory):
return False
return True
def getItems(self, inventory, target_set) -> None:
if self.test(inventory):
return
for item in self.__items:
target_set.add(item)
for child in self.__children:
child.getItems(inventory, target_set)
def copyWithModifiedItemNames(self, f) -> "AND":
return AND(*(f(item) for item in self.__items), *(child.copyWithModifiedItemNames(f) for child in self.__children))
class COUNT:
__slots__ = ('__item', '__amount')
def __init__(self, item: str, amount: int) -> None:
self.__item = item
self.__amount = amount
def __repr__(self) -> str:
return "<%dx%s>" % (self.__amount, self.__item)
def hasConsumableRequirement(self) -> bool:
if isConsumable(self.__item):
return True
return False
def test(self, inventory) -> bool:
return inventory.get(self.__item, 0) >= self.__amount
def consume(self, inventory) -> None:
if isConsumable(self.__item):
inventory[self.__item] -= self.__amount
if inventory[self.__item] == 0:
del inventory[self.__item]
inventory["%s_USED" % self.__item] = inventory.get("%s_USED" % self.__item, 0) + self.__amount
def getItems(self, inventory, target_set) -> None:
if self.test(inventory):
return
target_set.add(self.__item)
def copyWithModifiedItemNames(self, f) -> "COUNT":
return COUNT(f(self.__item), self.__amount)
class COUNTS:
__slots__ = ('__items', '__amount')
def __init__(self, items, amount):
self.__items = items
self.__amount = amount
def __repr__(self) -> str:
return "<%dx%s>" % (self.__amount, self.__items)
def hasConsumableRequirement(self) -> bool:
for item in self.__items:
if isConsumable(item):
print("Consumable COUNTS requirement? %r" % (self))
return True
return False
def test(self, inventory) -> bool:
count = 0
for item in self.__items:
count += inventory.get(item, 0)
return count >= self.__amount
def consume(self, inventory) -> None:
for item in self.__items:
if isConsumable(item):
inventory[item] -= self.__amount
if inventory[item] == 0:
del inventory[item]
inventory["%s_USED" % item] = inventory.get("%s_USED" % item, 0) + self.__amount
def getItems(self, inventory, target_set) -> None:
if self.test(inventory):
return
for item in self.__items:
target_set.add(item)
def copyWithModifiedItemNames(self, f) -> "COUNTS":
return COUNTS([f(item) for item in self.__items], self.__amount)
class FOUND:
__slots__ = ('__item', '__amount')
def __init__(self, item: str, amount: int) -> None:
self.__item = item
self.__amount = amount
def __repr__(self) -> str:
return "{%dx%s}" % (self.__amount, self.__item)
def hasConsumableRequirement(self) -> bool:
return False
def test(self, inventory) -> bool:
return inventory.get(self.__item, 0) + inventory.get("%s_USED" % self.__item, 0) >= self.__amount
def consume(self, inventory) -> None:
pass
def getItems(self, inventory, target_set) -> None:
if self.test(inventory):
return
target_set.add(self.__item)
def copyWithModifiedItemNames(self, f) -> "FOUND":
return FOUND(f(self.__item), self.__amount)
def hasConsumableRequirement(requirements) -> bool:
if isinstance(requirements, str):
return isConsumable(requirements)
if requirements is None:
return False
return requirements.hasConsumableRequirement()
def isConsumable(item) -> bool:
if item is None:
return False
#if item.startswith("RUPEES_") or item == "RUPEES":
# return True
if item.startswith("KEY"):
return True
return False
class RequirementsSettings:
def __init__(self, options):
self.bush = OR(SWORD, MAGIC_POWDER, MAGIC_ROD, POWER_BRACELET, BOOMERANG)
self.attack = OR(SWORD, BOMB, BOW, MAGIC_ROD, BOOMERANG)
self.attack_hookshot = OR(SWORD, BOMB, BOW, MAGIC_ROD, BOOMERANG, HOOKSHOT) # switches, hinox, shrouded stalfos
self.attack_hookshot_powder = OR(SWORD, BOMB, BOW, MAGIC_ROD, BOOMERANG, HOOKSHOT, MAGIC_POWDER) # zols, keese, moldorm
self.attack_no_bomb = OR(SWORD, BOW, MAGIC_ROD, BOOMERANG, HOOKSHOT) # ?
self.attack_hookshot_no_bomb = OR(SWORD, BOW, MAGIC_ROD, BOOMERANG, HOOKSHOT) # vire
self.attack_no_boomerang = OR(SWORD, BOMB, BOW, MAGIC_ROD, HOOKSHOT) # teleporting owls
self.attack_skeleton = OR(SWORD, BOMB, BOW, BOOMERANG, HOOKSHOT) # cannot kill skeletons with the fire rod
self.rear_attack = OR(SWORD, BOMB) # mimic
self.rear_attack_range = OR(MAGIC_ROD, BOW) # mimic
self.fire = OR(MAGIC_POWDER, MAGIC_ROD) # torches
self.push_hardhat = OR(SHIELD, SWORD, HOOKSHOT, BOOMERANG)
self.boss_requirements = [
SWORD, # D1 boss
AND(OR(SWORD, MAGIC_ROD), POWER_BRACELET), # D2 boss
AND(PEGASUS_BOOTS, SWORD), # D3 boss
AND(FLIPPERS, OR(SWORD, MAGIC_ROD, BOW)), # D4 boss
AND(HOOKSHOT, SWORD), # D5 boss
BOMB, # D6 boss
AND(OR(MAGIC_ROD, SWORD, HOOKSHOT), COUNT(SHIELD, 2)), # D7 boss
MAGIC_ROD, # D8 boss
self.attack_hookshot_no_bomb, # D9 boss
]
self.miniboss_requirements = {
"ROLLING_BONES": self.attack_hookshot,
"HINOX": self.attack_hookshot,
"DODONGO": BOMB,
"CUE_BALL": SWORD,
"GHOMA": OR(BOW, HOOKSHOT),
"SMASHER": POWER_BRACELET,
"GRIM_CREEPER": self.attack_hookshot_no_bomb,
"BLAINO": SWORD,
"AVALAUNCH": self.attack_hookshot,
"GIANT_BUZZ_BLOB": MAGIC_POWDER,
"MOBLIN_KING": SWORD,
"ARMOS_KNIGHT": OR(BOW, MAGIC_ROD, SWORD),
}
# Adjust for options
if options.bowwow != 'normal':
# We cheat in bowwow mode, we pretend we have the sword, as bowwow can pretty much do all what the sword ca$ # Except for taking out bushes (and crystal pillars are removed)
self.bush.remove(SWORD)
if options.logic == "casual":
# In casual mode, remove the more complex kill methods
self.bush.remove(MAGIC_POWDER)
self.attack_hookshot_powder.remove(MAGIC_POWDER)
self.attack.remove(BOMB)
self.attack_hookshot.remove(BOMB)
self.attack_hookshot_powder.remove(BOMB)
self.attack_no_boomerang.remove(BOMB)
self.attack_skeleton.remove(BOMB)
if options.logic == "hard":
self.boss_requirements[3] = AND(FLIPPERS, OR(SWORD, MAGIC_ROD, BOW, BOMB)) # bomb angler fish
self.boss_requirements[6] = OR(MAGIC_ROD, AND(BOMB, BOW), COUNT(SWORD, 2), AND(OR(SWORD, HOOKSHOT, BOW), SHIELD)) # evil eagle 3 cycle magic rod / bomb arrows / l2 sword, and bow kill
if options.logic == "glitched":
self.boss_requirements[3] = AND(FLIPPERS, OR(SWORD, MAGIC_ROD, BOW, BOMB)) # bomb angler fish
self.boss_requirements[6] = OR(MAGIC_ROD, BOMB, BOW, HOOKSHOT, COUNT(SWORD, 2), AND(SWORD, SHIELD)) # evil eagle off screen kill or 3 cycle with bombs
if options.logic == "hell":
self.boss_requirements[3] = AND(FLIPPERS, OR(SWORD, MAGIC_ROD, BOW, BOMB)) # bomb angler fish
self.boss_requirements[6] = OR(MAGIC_ROD, BOMB, BOW, HOOKSHOT, COUNT(SWORD, 2), AND(SWORD, SHIELD)) # evil eagle off screen kill or 3 cycle with bombs
self.boss_requirements[7] = OR(MAGIC_ROD, COUNT(SWORD, 2)) # hot head sword beams
self.miniboss_requirements["GIANT_BUZZ_BLOB"] = OR(MAGIC_POWDER, COUNT(SWORD,2)) # use sword beams to damage buzz blob