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:
284
worlds/ladx/LADXR/logic/__init__.py
Normal file
284
worlds/ladx/LADXR/logic/__init__.py
Normal 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))
|
||||
46
worlds/ladx/LADXR/logic/dungeon1.py
Normal file
46
worlds/ladx/LADXR/logic/dungeon1.py
Normal 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
|
||||
62
worlds/ladx/LADXR/logic/dungeon2.py
Normal file
62
worlds/ladx/LADXR/logic/dungeon2.py
Normal 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
|
||||
89
worlds/ladx/LADXR/logic/dungeon3.py
Normal file
89
worlds/ladx/LADXR/logic/dungeon3.py
Normal 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
|
||||
81
worlds/ladx/LADXR/logic/dungeon4.py
Normal file
81
worlds/ladx/LADXR/logic/dungeon4.py
Normal 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
|
||||
89
worlds/ladx/LADXR/logic/dungeon5.py
Normal file
89
worlds/ladx/LADXR/logic/dungeon5.py
Normal 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
|
||||
65
worlds/ladx/LADXR/logic/dungeon6.py
Normal file
65
worlds/ladx/LADXR/logic/dungeon6.py
Normal 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
|
||||
65
worlds/ladx/LADXR/logic/dungeon7.py
Normal file
65
worlds/ladx/LADXR/logic/dungeon7.py
Normal 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
|
||||
107
worlds/ladx/LADXR/logic/dungeon8.py
Normal file
107
worlds/ladx/LADXR/logic/dungeon8.py
Normal 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
|
||||
49
worlds/ladx/LADXR/logic/dungeonColor.py
Normal file
49
worlds/ladx/LADXR/logic/dungeonColor.py
Normal 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
|
||||
57
worlds/ladx/LADXR/logic/location.py
Normal file
57
worlds/ladx/LADXR/logic/location.py
Normal 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))
|
||||
682
worlds/ladx/LADXR/logic/overworld.py
Normal file
682
worlds/ladx/LADXR/logic/overworld.py
Normal 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"
|
||||
318
worlds/ladx/LADXR/logic/requirements.py
Normal file
318
worlds/ladx/LADXR/logic/requirements.py
Normal 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
|
||||
Reference in New Issue
Block a user