Merge pull request #78 from aerinon/DoorDevUnstable
Door dev unstable to door dev main
This commit is contained in:
@@ -30,5 +30,7 @@ resources/user/*
|
||||
|
||||
*.exe
|
||||
|
||||
get-pip.py
|
||||
|
||||
venv
|
||||
test
|
||||
|
||||
+294
-23
@@ -7,8 +7,7 @@ from enum import Enum, unique
|
||||
try:
|
||||
from fast_enum import FastEnum
|
||||
except ImportError:
|
||||
from enum import Flag
|
||||
FastEnum = Flag
|
||||
from enum import IntFlag as FastEnum
|
||||
|
||||
|
||||
from source.classes.BabelFish import BabelFish
|
||||
@@ -80,6 +79,9 @@ class World(object):
|
||||
self.key_logic = {}
|
||||
self.pool_adjustment = {}
|
||||
self.key_layout = defaultdict(dict)
|
||||
self.dungeon_portals = defaultdict(list)
|
||||
self._portal_cache = {}
|
||||
self.sanc_portal = {}
|
||||
self.fish = BabelFish()
|
||||
|
||||
for player in range(1, players + 1):
|
||||
@@ -120,9 +122,18 @@ class World(object):
|
||||
set_player_attr('escape_assist', [])
|
||||
set_player_attr('crystals_needed_for_ganon', 7)
|
||||
set_player_attr('crystals_needed_for_gt', 7)
|
||||
set_player_attr('crystals_ganon_orig', {})
|
||||
set_player_attr('crystals_gt_orig', {})
|
||||
set_player_attr('open_pyramid', False)
|
||||
set_player_attr('treasure_hunt_icon', 'Triforce Piece')
|
||||
set_player_attr('treasure_hunt_count', 0)
|
||||
set_player_attr('potshuffle', False)
|
||||
set_player_attr('pot_contents', None)
|
||||
|
||||
set_player_attr('keydropshuffle', False)
|
||||
set_player_attr('mixed_travel', 'prevent')
|
||||
set_player_attr('standardize_palettes', 'standardize')
|
||||
set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False});
|
||||
|
||||
def get_name_string_for_object(self, obj):
|
||||
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
|
||||
@@ -141,6 +152,11 @@ class World(object):
|
||||
for door in doors:
|
||||
self._door_cache[(door.name, door.player)] = door
|
||||
|
||||
def remove_door(self, door, player):
|
||||
if (door, player) in self._door_cache.keys():
|
||||
del self._door_cache[(door, player)]
|
||||
self.doors.remove(door)
|
||||
|
||||
def get_regions(self, player=None):
|
||||
return self.regions if player is None else self._region_cache[player].values()
|
||||
|
||||
@@ -169,6 +185,10 @@ class World(object):
|
||||
return exit
|
||||
raise RuntimeError('No such entrance %s for player %d' % (entrance, player))
|
||||
|
||||
def remove_entrance(self, entrance, player):
|
||||
if (entrance, player) in self._entrance_cache.keys():
|
||||
del self._entrance_cache[(entrance, player)]
|
||||
|
||||
def get_location(self, location, player):
|
||||
if isinstance(location, Location):
|
||||
return location
|
||||
@@ -203,6 +223,18 @@ class World(object):
|
||||
return door
|
||||
raise RuntimeError('No such door %s for player %d' % (doorname, player))
|
||||
|
||||
def get_portal(self, portal_name, player):
|
||||
if isinstance(portal_name, Portal):
|
||||
return portal_name
|
||||
try:
|
||||
return self._portal_cache[(portal_name, player)]
|
||||
except KeyError:
|
||||
for portal in self.dungeon_portals[player]:
|
||||
if portal.name == portal_name and portal.player == player:
|
||||
self._portal_cache[(portal_name, player)] = portal
|
||||
return portal
|
||||
raise RuntimeError('No such portal %s for player %d' % (portal_name, player))
|
||||
|
||||
def check_for_door(self, doorname, player):
|
||||
if isinstance(doorname, Door):
|
||||
return doorname
|
||||
@@ -458,7 +490,7 @@ class CollectionState(object):
|
||||
new_crystal_state = crystal_state
|
||||
for exit in new_region.exits:
|
||||
door = exit.door
|
||||
if door is not None and door.crystal == CrystalBarrier.Either:
|
||||
if door is not None and door.crystal == CrystalBarrier.Either and door.entrance.can_reach(self):
|
||||
new_crystal_state = CrystalBarrier.Either
|
||||
break
|
||||
if new_region in rrp:
|
||||
@@ -469,7 +501,7 @@ class CollectionState(object):
|
||||
for exit in new_region.exits:
|
||||
door = exit.door
|
||||
if door is not None and not door.blocked:
|
||||
door_crystal_state = new_crystal_state & (door.crystal or CrystalBarrier.Either)
|
||||
door_crystal_state = door.crystal if door.crystal else new_crystal_state
|
||||
bc[exit] = door_crystal_state
|
||||
queue.append((exit, door_crystal_state))
|
||||
elif door is None:
|
||||
@@ -547,7 +579,9 @@ class CollectionState(object):
|
||||
def _do_not_flood_the_keys(self, reachable_events):
|
||||
adjusted_checks = list(reachable_events)
|
||||
for event in reachable_events:
|
||||
if event.name in flooded_keys.keys() and self.world.get_location(flooded_keys[event.name], event.player) not in reachable_events:
|
||||
if event.name in flooded_keys.keys():
|
||||
flood_location = self.world.get_location(flooded_keys[event.name], event.player)
|
||||
if flood_location.item and flood_location not in self.locations_checked:
|
||||
adjusted_checks.remove(event)
|
||||
if len(adjusted_checks) < len(reachable_events):
|
||||
return adjusted_checks
|
||||
@@ -555,7 +589,10 @@ class CollectionState(object):
|
||||
|
||||
def not_flooding_a_key(self, world, location):
|
||||
if location.name in flooded_keys.keys():
|
||||
return world.get_location(flooded_keys[location.name], location.player) in self.locations_checked
|
||||
flood_location = world.get_location(flooded_keys[location.name], location.player)
|
||||
item = flood_location.item
|
||||
item_is_important = False if not item else item.advancement or item.bigkey or item.smallkey
|
||||
return flood_location in self.locations_checked or not item_is_important
|
||||
return True
|
||||
|
||||
def has(self, item, player, count=1):
|
||||
@@ -563,7 +600,7 @@ class CollectionState(object):
|
||||
return (item, player) in self.prog_items
|
||||
return self.prog_items[item, player] >= count
|
||||
|
||||
def has_key(self, item, player, count=1):
|
||||
def has_sm_key(self, item, player, count=1):
|
||||
if self.world.retro[player]:
|
||||
if self.world.mode[player] == 'standard' and self.world.doorShuffle[player] == 'vanilla' and item == 'Small Key (Escape)':
|
||||
return True # Cannot access the shop until escape is finished. This is safe because the key is manually placed in make_custom_item_pool
|
||||
@@ -838,6 +875,7 @@ class CollectionState(object):
|
||||
|
||||
@unique
|
||||
class RegionType(Enum):
|
||||
Menu = 0
|
||||
LightWorld = 1
|
||||
DarkWorld = 2
|
||||
Cave = 3 # Also includes Houses
|
||||
@@ -937,9 +975,10 @@ class Entrance(object):
|
||||
world = self.parent_region.world if self.parent_region else None
|
||||
return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})'
|
||||
|
||||
|
||||
class Dungeon(object):
|
||||
|
||||
def __init__(self, name, regions, big_key, small_keys, dungeon_items, player):
|
||||
def __init__(self, name, regions, big_key, small_keys, dungeon_items, player, dungeon_id):
|
||||
self.name = name
|
||||
self.regions = regions
|
||||
self.big_key = big_key
|
||||
@@ -948,6 +987,7 @@ class Dungeon(object):
|
||||
self.bosses = dict()
|
||||
self.player = player
|
||||
self.world = None
|
||||
self.dungeon_id = dungeon_id
|
||||
|
||||
self.entrance_regions = []
|
||||
|
||||
@@ -1152,6 +1192,7 @@ class Door(object):
|
||||
# 0-4 for spiral offset thing
|
||||
self.doorIndex = -1
|
||||
self.layer = -1 # 0 for normal floor, 1 for the inset layer
|
||||
self.pseudo_bg = 0 # 0 for normal floor, 1 for pseudo bg
|
||||
self.toggle = False
|
||||
self.trapFlag = 0x0
|
||||
self.quadrant = 2
|
||||
@@ -1163,6 +1204,17 @@ class Door(object):
|
||||
self.edge_id = None
|
||||
self.edge_width = None
|
||||
|
||||
#portal items
|
||||
self.portalAble = False
|
||||
self.roomLayout = 0x22 # free scroll- both directions
|
||||
self.entranceFlag = False
|
||||
self.deadEnd = False
|
||||
self.passage = True
|
||||
self.dungeonLink = None
|
||||
self.bk_shuffle_req = False
|
||||
# self.incognitoPos = -1
|
||||
# self.sectorLink = False
|
||||
|
||||
# logical properties
|
||||
# self.connected = False # combine with Dest?
|
||||
self.dest = None
|
||||
@@ -1295,6 +1347,20 @@ class Door(object):
|
||||
self.dead = True
|
||||
return self
|
||||
|
||||
def portal(self, quadrant, roomLayout, pseudo_bg=0):
|
||||
self.quadrant = quadrant
|
||||
self.roomLayout = roomLayout
|
||||
self.pseudo_bg = pseudo_bg
|
||||
self.portalAble = True
|
||||
return self
|
||||
|
||||
def dead_end(self, allowPassage=False):
|
||||
self.deadEnd = True
|
||||
if allowPassage:
|
||||
self.passage = True
|
||||
else:
|
||||
self.passage = False
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, self.__class__) and self.name == other.name
|
||||
|
||||
@@ -1316,7 +1382,6 @@ class Sector(object):
|
||||
self.name = None
|
||||
self.r_name_set = None
|
||||
self.chest_locations = 0
|
||||
self.big_chest_present = False
|
||||
self.key_only_locations = 0
|
||||
self.c_switch = False
|
||||
self.orange_barrier = False
|
||||
@@ -1378,10 +1443,9 @@ class Sector(object):
|
||||
self.branch_factor -= cnt_dead - 1
|
||||
for region in self.regions:
|
||||
for ent in region.entrances:
|
||||
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] or ent.parent_region.name == 'Sewer Drop':
|
||||
# same sector as another entrance
|
||||
if region.name not in ['Skull Pot Circle', 'Skull Back Drop', 'Desert East Lobby', 'Desert West Lobby']:
|
||||
if (ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] and ent.parent_region.name != 'Menu') or ent.parent_region.name == 'Sewer Drop':
|
||||
self.branch_factor += 1
|
||||
break # you only ever get one allowance for an entrance region, multiple entrances don't help
|
||||
return self.branch_factor
|
||||
|
||||
def branches(self):
|
||||
@@ -1424,6 +1488,122 @@ class Sector(object):
|
||||
return f'{next(iter(self.region_set()))}'
|
||||
|
||||
|
||||
class Portal(object):
|
||||
|
||||
def __init__(self, player, name, door, entrance_offset, exit_offset, boss_exit_idx):
|
||||
self.player = player
|
||||
self.name = name
|
||||
self.door = door
|
||||
self.ent_offset = entrance_offset
|
||||
self.exit_offset = exit_offset
|
||||
self.boss_exit_idx = boss_exit_idx
|
||||
self.default = True
|
||||
self.destination = False
|
||||
self.dependent = None
|
||||
self.deadEnd = False
|
||||
self.light_world = False
|
||||
|
||||
def change_boss_exit(self, exit_idx):
|
||||
self.default = False
|
||||
self.boss_exit_idx = exit_idx
|
||||
|
||||
def change_door(self, new_door):
|
||||
if new_door != self.door:
|
||||
self.default = False
|
||||
self.door = new_door
|
||||
|
||||
def current_room(self):
|
||||
return self.door.roomIndex
|
||||
|
||||
def relative_coords(self):
|
||||
y_rel = (self.door.roomIndex & 0xf0) >> 3 #todo: fix the shift!!!!
|
||||
x_rel = (self.door.roomIndex & 0x0f) * 2
|
||||
quad = self.door.quadrant
|
||||
if quad == 0:
|
||||
return [y_rel, y_rel, y_rel, y_rel+1, x_rel, x_rel, x_rel, x_rel+1]
|
||||
elif quad == 1:
|
||||
return [y_rel, y_rel, y_rel, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1]
|
||||
elif quad == 2:
|
||||
return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel, x_rel, x_rel, x_rel+1]
|
||||
else:
|
||||
return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1]
|
||||
|
||||
def scroll_x(self):
|
||||
x_rel = (self.door.roomIndex & 0x0f) * 2
|
||||
if self.door.doorIndex == 0:
|
||||
return [0x00, x_rel]
|
||||
elif self.door.doorIndex == 1:
|
||||
return [0x80, x_rel]
|
||||
else:
|
||||
return [0x00, x_rel+1]
|
||||
|
||||
def scroll_y(self):
|
||||
y_rel = ((self.door.roomIndex & 0xf0) >> 3) + 1
|
||||
return [0x10, y_rel]
|
||||
|
||||
def link_y(self):
|
||||
y_rel = ((self.door.roomIndex & 0xf0) >> 3) + 1
|
||||
inset = False
|
||||
if self.door.pseudo_bg == 1 or self.door.layer == 1:
|
||||
inset = True
|
||||
return [(0xd8 if not inset else 0xc0), y_rel]
|
||||
|
||||
def link_x(self):
|
||||
x_rel = (self.door.roomIndex & 0x0f) * 2
|
||||
if self.door.doorIndex == 0:
|
||||
return [0x78, x_rel]
|
||||
elif self.door.doorIndex == 1:
|
||||
return [0xf8, x_rel]
|
||||
else:
|
||||
return [0x78, x_rel+1]
|
||||
|
||||
# def camera_y(self):
|
||||
# return [0x87, 0x01]
|
||||
|
||||
def camera_x(self):
|
||||
if self.door.doorIndex == 0:
|
||||
return [0x7f, 0x00]
|
||||
elif self.door.doorIndex == 1:
|
||||
return [0xff, 0x00]
|
||||
else:
|
||||
return [0x7f, 0x01]
|
||||
|
||||
def bg_setting(self):
|
||||
if self.door.layer == 0:
|
||||
return 0x00 | self.door.pseudo_bg
|
||||
else:
|
||||
return 0x10 | self.door.pseudo_bg
|
||||
|
||||
def hv_scroll(self):
|
||||
return self.door.roomLayout
|
||||
|
||||
def scroll_quad(self):
|
||||
quad = self.door.quadrant
|
||||
if quad == 0:
|
||||
return 0x00
|
||||
elif quad == 1:
|
||||
return 0x10
|
||||
elif quad == 2:
|
||||
return 0x02
|
||||
else:
|
||||
return 0x12
|
||||
|
||||
def __str__(self):
|
||||
return str(self.__unicode__())
|
||||
|
||||
def __unicode__(self):
|
||||
return f'{self.name}:{self.door.name}'
|
||||
|
||||
|
||||
class DungeonInfo(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.total = 0
|
||||
self.required_passage = {}
|
||||
self.sole_entrance = None
|
||||
# self.dead_ends = 0 total - 1 - req = dead_ends possible
|
||||
|
||||
|
||||
class Boss(object):
|
||||
def __init__(self, name, enemizer_name, defeat_rule, player):
|
||||
self.name = name
|
||||
@@ -1469,6 +1649,24 @@ class Location(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def forced_big_key(self):
|
||||
if self.forced_item and self.forced_item.bigkey and self.player == self.forced_item.player:
|
||||
item_dungeon = self.forced_item.name.split('(')[1][:-1]
|
||||
if item_dungeon == 'Escape':
|
||||
item_dungeon = 'Hyrule Castle'
|
||||
if self.parent_region.dungeon.name == item_dungeon:
|
||||
return True
|
||||
return False
|
||||
|
||||
def gen_name(self):
|
||||
name = self.name
|
||||
world = self.parent_region.world if self.parent_region and self.parent_region.world else None
|
||||
if self.parent_region.dungeon and world and world.doorShuffle[self.player] == 'crossed':
|
||||
name += f' @ {self.parent_region.dungeon.name}'
|
||||
if world and world.players > 1:
|
||||
name += f' ({world.get_player_names(self.player)})'
|
||||
return name
|
||||
|
||||
def __str__(self):
|
||||
return str(self.__unicode__())
|
||||
|
||||
@@ -1594,9 +1792,10 @@ class Spoiler(object):
|
||||
def __init__(self, world):
|
||||
self.world = world
|
||||
self.hashes = {}
|
||||
self.entrances = OrderedDict()
|
||||
self.doors = OrderedDict()
|
||||
self.doorTypes = OrderedDict()
|
||||
self.entrances = {}
|
||||
self.doors = {}
|
||||
self.doorTypes = {}
|
||||
self.lobbies = {}
|
||||
self.medallions = {}
|
||||
self.playthrough = {}
|
||||
self.unreachables = []
|
||||
@@ -1619,6 +1818,12 @@ class Spoiler(object):
|
||||
else:
|
||||
self.doors[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction), ('dname', d_name)])
|
||||
|
||||
def set_lobby(self, lobby_name, door_name, player):
|
||||
if self.world.players == 1:
|
||||
self.lobbies[(lobby_name, player)] = {'lobby_name': lobby_name, 'door_name': door_name}
|
||||
else:
|
||||
self.lobbies[(lobby_name, player)] = {'player': player, 'lobby_name': lobby_name, 'door_name': door_name}
|
||||
|
||||
def set_door_type(self, doorNames, type, player):
|
||||
if self.world.players == 1:
|
||||
self.doorTypes[(doorNames, player)] = OrderedDict([('doorNames', doorNames), ('type', type)])
|
||||
@@ -1641,25 +1846,25 @@ class Spoiler(object):
|
||||
listed_locations = set()
|
||||
|
||||
lw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.LightWorld]
|
||||
self.locations['Light World'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in lw_locations])
|
||||
self.locations['Light World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in lw_locations])
|
||||
listed_locations.update(lw_locations)
|
||||
|
||||
dw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.DarkWorld]
|
||||
self.locations['Dark World'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in dw_locations])
|
||||
self.locations['Dark World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dw_locations])
|
||||
listed_locations.update(dw_locations)
|
||||
|
||||
cave_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.Cave]
|
||||
self.locations['Caves'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in cave_locations])
|
||||
self.locations['Caves'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in cave_locations])
|
||||
listed_locations.update(cave_locations)
|
||||
|
||||
for dungeon in self.world.dungeons:
|
||||
dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon]
|
||||
self.locations[str(dungeon)] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations])
|
||||
self.locations[str(dungeon)] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations])
|
||||
listed_locations.update(dungeon_locations)
|
||||
|
||||
other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations]
|
||||
if other_locations:
|
||||
self.locations['Other Locations'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in other_locations])
|
||||
self.locations['Other Locations'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in other_locations])
|
||||
listed_locations.update(other_locations)
|
||||
|
||||
self.shops = []
|
||||
@@ -1698,6 +1903,11 @@ class Spoiler(object):
|
||||
if self.world.players == 1:
|
||||
self.bosses = self.bosses["1"]
|
||||
|
||||
for player in range(1, self.world.players + 1):
|
||||
if self.world.intensity[player] >= 3:
|
||||
for portal in self.world.dungeon_portals[player]:
|
||||
self.set_lobby(portal.name, portal.door.name, player)
|
||||
|
||||
from Main import __version__ as ERVersion
|
||||
self.metadata = {'version': ERVersion,
|
||||
'logic': self.world.logic,
|
||||
@@ -1725,7 +1935,8 @@ class Spoiler(object):
|
||||
'enemy_damage': self.world.enemy_damage,
|
||||
'players': self.world.players,
|
||||
'teams': self.world.teams,
|
||||
'experimental' : self.world.experimental
|
||||
'experimental': self.world.experimental,
|
||||
'keydropshuffle': self.world.keydropshuffle,
|
||||
}
|
||||
|
||||
def to_json(self):
|
||||
@@ -1733,6 +1944,7 @@ class Spoiler(object):
|
||||
out = OrderedDict()
|
||||
out['Entrances'] = list(self.entrances.values())
|
||||
out['Doors'] = list(self.doors.values())
|
||||
out['Lobbies'] = list(self.lobbies.values())
|
||||
out['DoorTypes'] = list(self.doorTypes.values())
|
||||
out.update(self.locations)
|
||||
out['Starting Inventory'] = self.startinventory
|
||||
@@ -1771,8 +1983,10 @@ class Spoiler(object):
|
||||
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
||||
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
||||
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
|
||||
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'][player])
|
||||
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'][player])
|
||||
addition = ' (Random)' if self.world.crystals_gt_orig[player] == 'random' else ''
|
||||
outfile.write('Crystals required for GT: %s\n' % (str(self.metadata['gt_crystals'][player]) + addition))
|
||||
addition = ' (Random)' if self.world.crystals_ganon_orig[player] == 'random' else ''
|
||||
outfile.write('Crystals required for Ganon: %s\n' % (str(self.metadata['ganon_crystals'][player]) + addition))
|
||||
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
|
||||
outfile.write('Accessibility: %s\n' % self.metadata['accessibility'][player])
|
||||
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
|
||||
@@ -1785,6 +1999,7 @@ class Spoiler(object):
|
||||
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
|
||||
outfile.write('Hints: %s\n' % ('Yes' if self.metadata['hints'][player] else 'No'))
|
||||
outfile.write('Experimental: %s\n' % ('Yes' if self.metadata['experimental'][player] else 'No'))
|
||||
outfile.write('Key Drops shuffled: %s\n' % ('Yes' if self.metadata['keydropshuffle'][player] else 'No'))
|
||||
if self.doors:
|
||||
outfile.write('\n\nDoors:\n\n')
|
||||
outfile.write('\n'.join(
|
||||
@@ -1794,6 +2009,12 @@ class Spoiler(object):
|
||||
self.world.fish.translate("meta","doors",entry['exit']),
|
||||
'({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] == 'crossed' else '') for
|
||||
entry in self.doors.values()]))
|
||||
if self.lobbies:
|
||||
outfile.write('\n\nDungeon Lobbies:\n\n')
|
||||
outfile.write('\n'.join(
|
||||
[f"{'Player {0}: '.format(entry['player']) if self.world.players > 1 else ''}{entry['lobby_name']}: {entry['door_name']}"
|
||||
for
|
||||
entry in self.lobbies.values()]))
|
||||
if self.doorTypes:
|
||||
# doorNames: For some reason these come in combined, somehow need to split on the thing to translate
|
||||
# doorTypes: Small Key, Bombable, Bonkable
|
||||
@@ -1859,3 +2080,53 @@ flooded_keys = {
|
||||
'Trench 1 Switch': 'Swamp Palace - Trench 1 Pot Key',
|
||||
'Trench 2 Switch': 'Swamp Palace - Trench 2 Pot Key'
|
||||
}
|
||||
|
||||
dungeon_names = [
|
||||
'Hyrule Castle', 'Eastern Palace', 'Desert Palace', 'Tower of Hera', 'Agahnims Tower', 'Palace of Darkness',
|
||||
'Swamp Palace', 'Skull Woods', 'Thieves Town', 'Ice Palace', 'Misery Mire', 'Turtle Rock', 'Ganons Tower'
|
||||
]
|
||||
|
||||
|
||||
class PotItem(FastEnum):
|
||||
Nothing = 0x0
|
||||
OneRupee = 0x1
|
||||
RockCrab = 0x2
|
||||
Bee = 0x3
|
||||
Random = 0x4
|
||||
Bomb_0 = 0x5
|
||||
Heart_0 = 0x6
|
||||
FiveRupees = 0x7
|
||||
Key = 0x8
|
||||
FiveArrows = 0x9
|
||||
Bomb = 0xA
|
||||
Heart = 0xB
|
||||
SmallMagic = 0xC
|
||||
BigMagic = 0xD
|
||||
Chicken = 0xE
|
||||
GreenSoldier = 0xF
|
||||
AliveRock = 0x10
|
||||
BlueSoldier = 0x11
|
||||
GroundBomb = 0x12
|
||||
Heart_2 = 0x13
|
||||
Fairy = 0x14
|
||||
Heart_3 = 0x15
|
||||
Hole = 0x80
|
||||
Warp = 0x82
|
||||
Staircase = 0x84
|
||||
Bombable = 0x86
|
||||
Switch = 0x88
|
||||
|
||||
|
||||
class PotFlags(FastEnum):
|
||||
Normal = 0x0
|
||||
NoSwitch = 0x1 # A switch should never go here
|
||||
SwitchLogicChange = 0x2 # A switch can go here, but requires a logic change
|
||||
|
||||
|
||||
class Pot(object):
|
||||
def __init__(self, x, y, item, room, flags = PotFlags.Normal):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.item = item
|
||||
self.room = room
|
||||
self.flags = flags
|
||||
@@ -2,8 +2,6 @@ import argparse
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
import random
|
||||
import textwrap
|
||||
import shlex
|
||||
import sys
|
||||
@@ -95,7 +93,7 @@ def parse_cli(argv, no_defaults=False):
|
||||
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
||||
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
||||
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep',
|
||||
'remote_items']:
|
||||
'remote_items', 'keydropshuffle', 'mixed_travel', 'standardize_palettes']:
|
||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||
if player == 1:
|
||||
setattr(ret, name, {1: value})
|
||||
@@ -135,6 +133,7 @@ def parse_settings():
|
||||
"enemy_health": "default",
|
||||
"enemizercli": os.path.join(".", "EnemizerCLI", "EnemizerCLI.Core"),
|
||||
|
||||
"keydropshuffle": False,
|
||||
"mapshuffle": False,
|
||||
"compassshuffle": False,
|
||||
"keyshuffle": False,
|
||||
@@ -144,6 +143,8 @@ def parse_settings():
|
||||
"intensity": 2,
|
||||
"experimental": False,
|
||||
"dungeon_counters": "default",
|
||||
"mixed_travel": "prevent",
|
||||
"standardize_palettes": "standardize",
|
||||
|
||||
"multi": 1,
|
||||
"names": "",
|
||||
|
||||
+862
-77
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
|
||||
from BaseClasses import Door, DoorType, Direction, CrystalBarrier
|
||||
from BaseClasses import Door, DoorType, Direction, CrystalBarrier, Portal
|
||||
from RoomData import PairedDoor
|
||||
|
||||
# constants
|
||||
@@ -46,18 +46,21 @@ def create_doors(world, player):
|
||||
create_door(player, 'Hyrule Castle Lobby W', Nrml).dir(We, 0x61, Mid, High).toggler().pos(0),
|
||||
create_door(player, 'Hyrule Castle Lobby E', Nrml).dir(Ea, 0x61, Mid, High).toggler().pos(2),
|
||||
create_door(player, 'Hyrule Castle Lobby WN', Nrml).dir(We, 0x61, Top, High).pos(1),
|
||||
create_door(player, 'Hyrule Castle Lobby S', Nrml).dir(So, 0x61, Mid, High).pos(4).portal(Z, 0x22, 1),
|
||||
create_door(player, 'Hyrule Castle Lobby North Stairs', StrS).dir(No, 0x61, Mid, High),
|
||||
create_door(player, 'Hyrule Castle West Lobby E', Nrml).dir(Ea, 0x60, Mid, Low).toggler().pos(1),
|
||||
create_door(player, 'Hyrule Castle West Lobby N', Nrml).dir(No, 0x60, Right, Low).pos(0),
|
||||
create_door(player, 'Hyrule Castle West Lobby EN', Nrml).dir(Ea, 0x60, Top, High).pos(3),
|
||||
create_door(player, 'Hyrule Castle West Lobby S', Nrml).dir(So, 0x60, Right, High).pos(2).portal(X, 0x02),
|
||||
create_door(player, 'Hyrule Castle East Lobby W', Nrml).dir(We, 0x62, Mid, Low).toggler().pos(0),
|
||||
create_door(player, 'Hyrule Castle East Lobby N', Nrml).dir(No, 0x62, Mid, High).pos(3),
|
||||
create_door(player, 'Hyrule Castle East Lobby NW', Nrml).dir(No, 0x62, Left, Low).pos(2),
|
||||
create_door(player, 'Hyrule Castle East Lobby S', Nrml).dir(So, 0x62, Left, High).pos(1).portal(Z, 0x22),
|
||||
create_door(player, 'Hyrule Castle East Hall W', Nrml).dir(We, 0x52, Top, Low).pos(0),
|
||||
create_door(player, 'Hyrule Castle East Hall S', Nrml).dir(So, 0x52, Mid, High).pos(2),
|
||||
create_door(player, 'Hyrule Castle East Hall SW', Nrml).dir(So, 0x52, Left, Low).pos(1),
|
||||
create_door(player, 'Hyrule Castle East Hall S', Nrml).dir(So, 0x52, Mid, High).pos(2).portal(Z, 0x22),
|
||||
create_door(player, 'Hyrule Castle East Hall SW', Nrml).dir(So, 0x52, Left, Low).pos(1).portal(Z, 0x22, 1),
|
||||
create_door(player, 'Hyrule Castle West Hall E', Nrml).dir(Ea, 0x50, Top, Low).pos(0),
|
||||
create_door(player, 'Hyrule Castle West Hall S', Nrml).dir(So, 0x50, Right, Low).pos(1),
|
||||
create_door(player, 'Hyrule Castle West Hall S', Nrml).dir(So, 0x50, Right, Low).pos(1).portal(X, 0x02, 1),
|
||||
create_door(player, 'Hyrule Castle Back Hall W', Nrml).dir(We, 0x01, Top, Low).pos(0),
|
||||
create_door(player, 'Hyrule Castle Back Hall E', Nrml).dir(Ea, 0x01, Top, Low).pos(1),
|
||||
create_door(player, 'Hyrule Castle Back Hall Down Stairs', Sprl).dir(Dn, 0x01, 0, HTL).ss(A, 0x2a, 0x00),
|
||||
@@ -80,7 +83,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', Open).dir(Ea, 0x81, None, High).edge(3, S, 0x10),
|
||||
create_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', Open).dir(Ea, 0x81, None, Low).edge(4, X, 0x18),
|
||||
create_door(player, 'Hyrule Dungeon Guardroom N', Nrml).dir(No, 0x81, Left, Low).pos(0),
|
||||
create_door(player, 'Hyrule Dungeon Armory S', Nrml).dir(So, 0x71, Left, Low).trap(0x2).pos(1),
|
||||
create_door(player, 'Hyrule Dungeon Armory S', Nrml).dir(So, 0x71, Left, Low).trap(0x2).pos(1).portal(Z, 0x00, 1),
|
||||
create_door(player, 'Hyrule Dungeon Armory ES', Intr).dir(Ea, 0x71, Left, Low).pos(2),
|
||||
create_door(player, 'Hyrule Dungeon Armory Boomerang WS', Intr).dir(We, 0x71, Left, Low).pos(2),
|
||||
create_door(player, 'Hyrule Dungeon Armory Interior Key Door N', Intr).dir(No, 0x71, Left, High).small_key().pos(0),
|
||||
@@ -89,19 +92,21 @@ def create_doors(world, player):
|
||||
create_door(player, 'Hyrule Dungeon Staircase Up Stairs', Sprl).dir(Up, 0x70, 2, LTH).ss(A, 0x32, 0x94, True),
|
||||
create_door(player, 'Hyrule Dungeon Staircase Down Stairs', Sprl).dir(Dn, 0x70, 1, HTH).ss(A, 0x11, 0x58),
|
||||
create_door(player, 'Hyrule Dungeon Cellblock Up Stairs', Sprl).dir(Up, 0x80, 0, HTH).ss(A, 0x1a, 0x44),
|
||||
create_door(player, 'Hyrule Dungeon Cellblock Door', Lgcl).big_key(),
|
||||
create_door(player, 'Hyrule Dungeon Cell Exit', Lgcl),
|
||||
|
||||
# sewers
|
||||
create_door(player, 'Sewers Behind Tapestry S', Nrml).dir(So, 0x41, Mid, High).no_exit().trap(0x4).pos(0),
|
||||
create_door(player, 'Sewers Behind Tapestry S', Nrml).dir(So, 0x41, Mid, High).no_exit().trap(0x4).pos(0).portal(Z, 0x22),
|
||||
create_door(player, 'Sewers Behind Tapestry Down Stairs', Sprl).dir(Dn, 0x41, 0, HTH).ss(S, 0x12, 0xb0),
|
||||
create_door(player, 'Sewers Rope Room Up Stairs', Sprl).dir(Up, 0x42, 0, HTH).ss(S, 0x1b, 0x9c),
|
||||
create_door(player, 'Sewers Rope Room North Stairs', StrS).dir(No, 0x42, Mid, High),
|
||||
create_door(player, 'Sewers Dark Cross South Stairs', StrS).dir(So, 0x32, Mid, High),
|
||||
create_door(player, 'Sewers Dark Cross Key Door N', Nrml).dir(No, 0x32, Mid, High).small_key().pos(0),
|
||||
create_door(player, 'Sewers Water S', Nrml).dir(So, 0x22, Mid, High).small_key().pos(0),
|
||||
create_door(player, 'Sewers Water S', Nrml).dir(So, 0x22, Mid, High).small_key().pos(0).portal(Z, 0x22),
|
||||
create_door(player, 'Sewers Water W', Nrml).dir(We, 0x22, Bot, High).pos(1),
|
||||
create_door(player, 'Sewers Key Rat E', Nrml).dir(Ea, 0x21, Bot, High).pos(1),
|
||||
create_door(player, 'Sewers Key Rat Key Door N', Nrml).dir(No, 0x21, Right, High).small_key().pos(0),
|
||||
create_door(player, 'Sewers Secret Room Key Door S', Nrml).dir(So, 0x11, Right, High).small_key().pos(2),
|
||||
create_door(player, 'Sewers Secret Room Key Door S', Nrml).dir(So, 0x11, Right, High).small_key().pos(2).portal(X, 0x02),
|
||||
create_door(player, 'Sewers Rat Path WS', Intr).dir(We, 0x11, Bot, High).pos(1),
|
||||
create_door(player, 'Sewers Rat Path WN', Intr).dir(We, 0x11, Top, High).pos(0),
|
||||
create_door(player, 'Sewers Secret Room ES', Intr).dir(Ea, 0x11, Bot, High).pos(1),
|
||||
@@ -111,11 +116,14 @@ def create_doors(world, player):
|
||||
create_door(player, 'Sewers Pull Switch Down Stairs', Sprl).dir(Dn, 0x02, 0, HTL).ss(S, 0x12, 0x80),
|
||||
create_door(player, 'Sewers Yet More Rats S', Intr).dir(So, 0x02, Mid, Low).pos(1),
|
||||
create_door(player, 'Sewers Pull Switch N', Intr).dir(No, 0x02, Mid, Low).pos(1),
|
||||
create_door(player, 'Sewers Pull Switch S', Nrml).dir(So, 0x02, Mid, Low).trap(0x4).toggler().pos(0),
|
||||
create_door(player, 'Sewers Pull Switch S', Nrml).dir(So, 0x02, Mid, Low).trap(0x4).toggler().pos(0).portal(Z, 0x20, 1),
|
||||
# logically one way the sanc, but should be linked - also toggle
|
||||
create_door(player, 'Sanctuary N', Nrml).dir(No, 0x12, Mid, High).no_exit().toggler().pos(0),
|
||||
create_door(player, 'Sanctuary S', Nrml).dir(So, 0x12, Mid, High).pos(2).portal(Z, 0x22, 1),
|
||||
create_door(player, 'Sanctuary Mirror Route', Lgcl),
|
||||
|
||||
# Eastern Palace
|
||||
create_door(player, 'Eastern Lobby S', Nrml).dir(So, 0xc9, Mid, High).pos(4).portal(Z, 0x20),
|
||||
create_door(player, 'Eastern Lobby N', Intr).dir(No, 0xc9, Mid, High).pos(0),
|
||||
create_door(player, 'Eastern Lobby Bridge S', Intr).dir(So, 0xc9, Mid, High).pos(0),
|
||||
create_door(player, 'Eastern Lobby NW', Intr).dir(No, 0xc9, Left, High).pos(2),
|
||||
@@ -123,11 +131,11 @@ def create_doors(world, player):
|
||||
create_door(player, 'Eastern Lobby NE', Intr).dir(No, 0xc9, Right, High).pos(3),
|
||||
create_door(player, 'Eastern Lobby Right Ledge SE', Intr).dir(So, 0xc9, Right, High).pos(3),
|
||||
create_door(player, 'Eastern Lobby Bridge N', Nrml).dir(No, 0xc9, Mid, High).pos(1).trap(0x2),
|
||||
create_door(player, 'Eastern Cannonball S', Nrml).dir(So, 0xb9, Mid, High).pos(2),
|
||||
create_door(player, 'Eastern Cannonball S', Nrml).dir(So, 0xb9, Mid, High).pos(2).portal(Z, 0x22),
|
||||
create_door(player, 'Eastern Cannonball N', Nrml).dir(No, 0xb9, Mid, High).pos(1),
|
||||
create_door(player, 'Eastern Cannonball Ledge WN', Nrml).dir(We, 0xb9, Top, High).pos(3),
|
||||
create_door(player, 'Eastern Cannonball Ledge Key Door EN', Nrml).dir(Ea, 0xb9, Top, High).small_key().pos(0),
|
||||
create_door(player, 'Eastern Courtyard Ledge S', Nrml).dir(So, 0xa9, Mid, High).pos(5),
|
||||
create_door(player, 'Eastern Courtyard Ledge S', Nrml).dir(So, 0xa9, Mid, High).pos(5).portal(Z, 0x22),
|
||||
create_door(player, 'Eastern Courtyard Ledge W', Nrml).dir(We, 0xa9, Mid, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Eastern Courtyard Ledge E', Nrml).dir(Ea, 0xa9, Mid, High).trap(0x2).pos(1),
|
||||
create_door(player, 'Eastern East Wing W', Nrml).dir(We, 0xaa, Mid, High).pos(4),
|
||||
@@ -147,7 +155,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Eastern Compass Room EN', Intr).dir(Ea, 0xa8, Top, High).pos(3),
|
||||
create_door(player, 'Eastern Hint Tile WN', Intr).dir(We, 0xa8, Top, High).pos(3),
|
||||
create_door(player, 'Eastern Hint Tile EN', Nrml).dir(Ea, 0xa8, Top, Low).pos(4),
|
||||
create_door(player, 'Eastern Hint Tile Blocked Path SE', Nrml).dir(So, 0xa8, Right, High).small_key().pos(2).kill(),
|
||||
create_door(player, 'Eastern Hint Tile Blocked Path SE', Nrml).dir(So, 0xa8, Right, High).small_key().pos(2).kill().portal(X, 0x02),
|
||||
create_door(player, 'Eastern Hint Tile Push Block', Lgcl),
|
||||
create_door(player, 'Eastern Courtyard WN', Nrml).dir(We, 0xa9, Top, Low).pos(3),
|
||||
create_door(player, 'Eastern Courtyard EN', Nrml).dir(Ea, 0xa9, Top, Low).pos(4),
|
||||
@@ -155,14 +163,14 @@ def create_doors(world, player):
|
||||
create_door(player, 'Eastern Courtyard Potholes', Hole),
|
||||
create_door(player, 'Eastern Fairies\' Warp', Warp),
|
||||
create_door(player, 'Eastern Map Valley WN', Nrml).dir(We, 0xaa, Top, Low).pos(1),
|
||||
create_door(player, 'Eastern Map Valley SW', Nrml).dir(So, 0xaa, Left, High).pos(5),
|
||||
create_door(player, 'Eastern Map Valley SW', Nrml).dir(So, 0xaa, Left, High).pos(5).portal(Z, 0x02),
|
||||
create_door(player, 'Eastern Dark Square NW', Nrml).dir(No, 0xba, Left, High).trap(0x2).pos(1),
|
||||
create_door(player, 'Eastern Dark Square Key Door WN', Nrml).dir(We, 0xba, Top, High).small_key().pos(0),
|
||||
create_door(player, 'Eastern Dark Square EN', Intr).dir(Ea, 0xba, Top, High).pos(2),
|
||||
create_door(player, 'Eastern Dark Pots WN', Intr).dir(We, 0xba, Top, High).pos(2),
|
||||
create_door(player, 'Eastern Big Key EN', Nrml).dir(Ea, 0xb8, Top, High).pos(1),
|
||||
create_door(player, 'Eastern Big Key NE', Nrml).dir(No, 0xb8, Right, High).big_key().pos(0),
|
||||
create_door(player, 'Eastern Darkness S', Nrml).dir(So, 0x99, Mid, High).small_key().pos(1),
|
||||
create_door(player, 'Eastern Darkness S', Nrml).dir(So, 0x99, Mid, High).small_key().pos(1).portal(Z, 0x20),
|
||||
create_door(player, 'Eastern Darkness NE', Intr).dir(No, 0x99, Right, High).pos(2),
|
||||
create_door(player, 'Eastern Rupees SE', Intr).dir(So, 0x99, Right, High).pos(2),
|
||||
# Up is a keydoor and down is not. Only the up stairs should be considered a key door for now.
|
||||
@@ -180,6 +188,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Eastern Boss SE', Nrml).dir(So, 0xc8, Right, High).no_exit().trap(0x4).pos(0),
|
||||
|
||||
# Desert Palace
|
||||
create_door(player, 'Desert Main Lobby S', Nrml).dir(So, 0x84, Mid, High).pos(0).portal(Z, 0x22),
|
||||
create_door(player, 'Desert Main Lobby NW Edge', Open).dir(No, 0x84, None, High).edge(3, A, 0x20),
|
||||
create_door(player, 'Desert Main Lobby N Edge', Open).dir(No, 0x84, None, High).edge(4, A, 0xa0),
|
||||
create_door(player, 'Desert Main Lobby NE Edge', Open).dir(No, 0x84, None, High).edge(5, S, 0x20),
|
||||
@@ -191,12 +200,13 @@ def create_doors(world, player):
|
||||
create_door(player, 'Desert Dead End Edge', Open).dir(So, 0x74, None, High).edge(4, Z, 0xa0),
|
||||
create_door(player, 'Desert East Wing W Edge', Open).dir(We, 0x85, None, High).edge(5, A, 0xa0),
|
||||
create_door(player, 'Desert East Wing N Edge', Open).dir(No, 0x85, None, High).edge(6, A, 0x20),
|
||||
create_door(player, 'Desert East Lobby S', Nrml).dir(So, 0x85, Right, High).pos(2).portal(X, 0x00),
|
||||
create_door(player, 'Desert East Lobby WS', Intr).dir(We, 0x85, Bot, High).pos(3),
|
||||
create_door(player, 'Desert East Wing ES', Intr).dir(Ea, 0x85, Bot, High).pos(3),
|
||||
create_door(player, 'Desert East Wing Key Door EN', Intr).dir(Ea, 0x85, Top, High).small_key().pos(1),
|
||||
create_door(player, 'Desert Compass Key Door WN', Intr).dir(We, 0x85, Top, High).small_key().pos(1),
|
||||
create_door(player, 'Desert Compass NW', Nrml).dir(No, 0x85, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Desert Cannonball S', Nrml).dir(So, 0x75, Right, High).pos(1),
|
||||
create_door(player, 'Desert Cannonball S', Nrml).dir(So, 0x75, Right, High).pos(1).portal(Z, 0x02),
|
||||
create_door(player, 'Desert Arrow Pot Corner S Edge', Open).dir(So, 0x75, None, High).edge(6, Z, 0x20),
|
||||
create_door(player, 'Desert Arrow Pot Corner W Edge', Open).dir(We, 0x75, None, High).edge(2, Z, 0x20),
|
||||
create_door(player, 'Desert Arrow Pot Corner NW', Intr).dir(No, 0x75, Left, High).pos(0),
|
||||
@@ -219,10 +229,12 @@ def create_doors(world, player):
|
||||
create_door(player, 'Desert Big Chest SW', Intr).dir(So, 0x73, Left, High).pos(0),
|
||||
create_door(player, 'Desert West Wing N Edge', Open).dir(No, 0x83, None, High).edge(2, S, 0x20),
|
||||
create_door(player, 'Desert West Wing WS', Intr).dir(We, 0x83, Bot, High).pos(2),
|
||||
create_door(player, 'Desert West S', Nrml).dir(So, 0x83, Left, High).pos(1).portal(Z, 0x00),
|
||||
create_door(player, 'Desert West Lobby ES', Intr).dir(Ea, 0x83, Bot, High).pos(2),
|
||||
create_door(player, 'Desert West Lobby NW', Intr).dir(No, 0x83, Left, High).pos(0),
|
||||
create_door(player, 'Desert Fairy Fountain SW', Intr).dir(So, 0x83, Left, High).pos(0),
|
||||
# Desert Back
|
||||
create_door(player, 'Desert Back Lobby S', Nrml).dir(So, 0x63, Left, High).pos(2).portal(Z, 0x00),
|
||||
create_door(player, 'Desert Back Lobby NW', Intr).dir(No, 0x63, Left, High).pos(1),
|
||||
create_door(player, 'Desert Tiles 1 SW', Intr).dir(So, 0x63, Left, High).pos(1),
|
||||
create_door(player, 'Desert Tiles 1 Up Stairs', Sprl).dir(Up, 0x63, 0, HTH).ss(A, 0x1b, 0x6c, True).small_key().pos(0),
|
||||
@@ -232,13 +244,14 @@ def create_doors(world, player):
|
||||
create_door(player, 'Desert Four Statues ES', Intr).dir(Ea, 0x53, Bot, High).pos(1),
|
||||
create_door(player, 'Desert Beamos Hall WS', Intr).dir(We, 0x53, Bot, High).pos(1),
|
||||
create_door(player, 'Desert Beamos Hall NE', Nrml).dir(No, 0x53, Right, High).small_key().pos(2),
|
||||
create_door(player, 'Desert Tiles 2 SE', Nrml).dir(So, 0x43, Right, High).small_key().pos(2).kill(),
|
||||
create_door(player, 'Desert Tiles 2 SE', Nrml).dir(So, 0x43, Right, High).small_key().pos(2).kill().portal(X, 0x00),
|
||||
create_door(player, 'Desert Tiles 2 NE', Intr).dir(No, 0x43, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Desert Wall Slide SE', Intr).dir(So, 0x43, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Desert Wall Slide NW', Nrml).dir(No, 0x43, Left, High).big_key().pos(0).no_entrance(),
|
||||
create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0),
|
||||
create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), problem with enemizer
|
||||
|
||||
# Hera
|
||||
create_door(player, 'Hera Lobby S', Nrml).dir(So, 0x77, Mid, Low).pos(0).portal(Z, 0x22, 1),
|
||||
create_door(player, 'Hera Lobby Down Stairs', Sprl).dir(Dn, 0x77, 3, HTL).ss(Z, 0x21, 0x90, False, True),
|
||||
create_door(player, 'Hera Lobby Key Stairs', Sprl).dir(Dn, 0x77, 1, HTL).ss(A, 0x12, 0x80).small_key().pos(1),
|
||||
create_door(player, 'Hera Lobby Up Stairs', Sprl).dir(Up, 0x77, 2, HTL).ss(X, 0x2b, 0x5c, False, True),
|
||||
@@ -275,6 +288,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Hera Boss Inner Hole', Hole),
|
||||
|
||||
# Castle Tower
|
||||
create_door(player, 'Tower Lobby S', Nrml).dir(So, 0xe0, Left, High).pos(3).portal(Z, 0x00),
|
||||
create_door(player, 'Tower Lobby NW', Intr).dir(No, 0xe0, Left, High).pos(1),
|
||||
create_door(player, 'Tower Gold Knights SW', Intr).dir(So, 0xe0, Left, High).pos(1),
|
||||
create_door(player, 'Tower Gold Knights EN', Intr).dir(Ea, 0xe0, Top, High).pos(0),
|
||||
@@ -311,6 +325,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Tower Agahnim 1 SW', Nrml).dir(So, 0x20, Left, High).no_exit().trap(0x4).pos(0),
|
||||
|
||||
# Palace of Darkness
|
||||
create_door(player, 'PoD Lobby S', Nrml).dir(So, 0x4a, Mid, High).pos(4).portal(Z, 0x20),
|
||||
create_door(player, 'PoD Lobby N', Intr).dir(No, 0x4a, Mid, High).pos(3),
|
||||
create_door(player, 'PoD Lobby NW', Intr).dir(No, 0x4a, Left, High).pos(0),
|
||||
create_door(player, 'PoD Lobby NE', Intr).dir(No, 0x4a, Right, High).pos(1),
|
||||
@@ -323,7 +338,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'PoD Shooter Room Up Stairs', Sprl).dir(Up, 0x09, 1, HTH).ss(A, 0x1b, 0x6c, True, True),
|
||||
create_door(player, 'PoD Warp Room Up Stairs', Sprl).dir(Up, 0x09, 0, HTH).ss(S, 0x1a, 0x6c, True, True),
|
||||
create_door(player, 'PoD Warp Room Warp', Warp),
|
||||
create_door(player, 'PoD Pit Room S', Nrml).dir(So, 0x3a, Mid, High).small_key().pos(0),
|
||||
create_door(player, 'PoD Pit Room S', Nrml).dir(So, 0x3a, Mid, High).small_key().pos(0).portal(Z, 0x22),
|
||||
create_door(player, 'PoD Pit Room NW', Nrml).dir(No, 0x3a, Left, High).pos(1),
|
||||
create_door(player, 'PoD Pit Room NE', Nrml).dir(No, 0x3a, Right, High).pos(2),
|
||||
create_door(player, 'PoD Pit Room Freefall', Hole),
|
||||
@@ -335,8 +350,8 @@ def create_doors(world, player):
|
||||
create_door(player, 'PoD Basement Ledge Up Stairs', Sprl).dir(Up, 0x0a, 0, HTH).ss(A, 0x1a, 0xec).small_key().pos(0),
|
||||
create_door(player, 'PoD Basement Ledge Drop Down', Lgcl),
|
||||
create_door(player, 'PoD Stalfos Basement Warp', Warp),
|
||||
create_door(player, 'PoD Arena Main SW', Nrml).dir(So, 0x2a, Left, High).pos(4),
|
||||
create_door(player, 'PoD Arena Bridge SE', Nrml).dir(So, 0x2a, Right, High).pos(5),
|
||||
create_door(player, 'PoD Arena Main SW', Nrml).dir(So, 0x2a, Left, High).pos(4).portal(Z, 0x22),
|
||||
create_door(player, 'PoD Arena Bridge SE', Nrml).dir(So, 0x2a, Right, High).pos(5).portal(X, 0x22),
|
||||
create_door(player, 'PoD Arena Main NW', Nrml).dir(No, 0x2a, Left, High).small_key().pos(1),
|
||||
create_door(player, 'PoD Arena Main NE', Nrml).dir(No, 0x2a, Right, High).no_exit().trap(0x4).pos(0),
|
||||
create_door(player, 'PoD Arena Main Crystal Path', Lgcl),
|
||||
@@ -353,14 +368,14 @@ def create_doors(world, player):
|
||||
create_door(player, 'PoD Map Balcony WS', Nrml).dir(We, 0x2b, Bot, High).pos(1),
|
||||
create_door(player, 'PoD Map Balcony South Stairs', StrS).dir(So, 0x2b, Left, High),
|
||||
create_door(player, 'PoD Conveyor North Stairs', StrS).dir(No, 0x3b, Left, High),
|
||||
create_door(player, 'PoD Conveyor SW', Nrml).dir(So, 0x3b, Left, High).pos(0),
|
||||
create_door(player, 'PoD Conveyor SW', Nrml).dir(So, 0x3b, Left, High).pos(0).portal(Z, 0x02),
|
||||
create_door(player, 'PoD Mimics 1 NW', Nrml).dir(No, 0x4b, Left, High).trap(0x4).pos(0),
|
||||
create_door(player, 'PoD Mimics 1 SW', Intr).dir(So, 0x4b, Left, High).pos(1),
|
||||
create_door(player, 'PoD Jelly Hall NW', Intr).dir(No, 0x4b, Left, High).pos(1),
|
||||
create_door(player, 'PoD Jelly Hall NE', Intr).dir(No, 0x4b, Right, High).pos(2),
|
||||
create_door(player, 'PoD Warp Hint SE', Intr).dir(So, 0x4b, Right, High).pos(2),
|
||||
create_door(player, 'PoD Warp Hint Warp', Warp),
|
||||
create_door(player, 'PoD Falling Bridge SW', Nrml).dir(So, 0x1a, Left, High).small_key().pos(3),
|
||||
create_door(player, 'PoD Falling Bridge SW', Nrml).dir(So, 0x1a, Left, High).small_key().pos(3).portal(Z, 0x02),
|
||||
create_door(player, 'PoD Falling Bridge WN', Nrml).dir(We, 0x1a, Top, High).small_key().pos(1),
|
||||
create_door(player, 'PoD Falling Bridge EN', Intr).dir(Ea, 0x1a, Top, High).pos(4),
|
||||
create_door(player, 'PoD Falling Bridge Path N', Lgcl),
|
||||
@@ -371,13 +386,13 @@ def create_doors(world, player):
|
||||
create_door(player, 'PoD Compass Room WN', Intr).dir(We, 0x1a, Top, High).pos(4),
|
||||
create_door(player, 'PoD Compass Room SE', Intr).dir(So, 0x1a, Mid, High).small_key().pos(0),
|
||||
create_door(player, 'PoD Harmless Hellway NE', Intr).dir(No, 0x1a, Right, High).small_key().pos(0),
|
||||
create_door(player, 'PoD Harmless Hellway SE', Nrml).dir(So, 0x1a, Right, High).pos(5),
|
||||
create_door(player, 'PoD Harmless Hellway SE', Nrml).dir(So, 0x1a, Right, High).pos(5).portal(X, 0x00),
|
||||
create_door(player, 'PoD Compass Room W Down Stairs', Sprl).dir(Dn, 0x1a, 0, HTH).ss(S, 0x12, 0x50, True, True),
|
||||
create_door(player, 'PoD Compass Room E Down Stairs', Sprl).dir(Dn, 0x1a, 1, HTH).ss(S, 0x11, 0xb0, True, True),
|
||||
create_door(player, 'PoD Dark Basement W Up Stairs', Sprl).dir(Up, 0x6a, 0, HTH).ss(S, 0x1b, 0x3c, True),
|
||||
create_door(player, 'PoD Dark Basement E Up Stairs', Sprl).dir(Up, 0x6a, 1, HTH).ss(S, 0x1b, 0x9c, True),
|
||||
create_door(player, 'PoD Dark Alley NE', Nrml).dir(No, 0x6a, Right, High).big_key().pos(0),
|
||||
create_door(player, 'PoD Mimics 2 SW', Nrml).dir(So, 0x1b, Left, High).pos(1).kill(),
|
||||
create_door(player, 'PoD Mimics 2 SW', Nrml).dir(So, 0x1b, Left, High).pos(1).kill().portal(Z, 0x00),
|
||||
create_door(player, 'PoD Mimics 2 NW', Intr).dir(No, 0x1b, Left, High).pos(0),
|
||||
create_door(player, 'PoD Bow Statue SW', Intr).dir(So, 0x1b, Left, High).pos(0),
|
||||
create_door(player, 'PoD Bow Statue Down Ladder', Lddr).no_entrance(),
|
||||
@@ -391,6 +406,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'PoD Callback Warp', Warp),
|
||||
create_door(player, 'PoD Boss SE', Nrml).dir(So, 0x5a, Right, High).no_exit().trap(0x4).pos(0),
|
||||
|
||||
create_door(player, 'Swamp Lobby S', Nrml).dir(So, 0x28, Mid, High).pos(1).portal(Z, 0x22),
|
||||
create_door(player, 'Swamp Lobby Moat', Lgcl),
|
||||
create_door(player, 'Swamp Entrance Down Stairs', Sprl).dir(Dn, 0x28, 0, HTH).ss(A, 0x11, 0x80).small_key().pos(0),
|
||||
create_door(player, 'Swamp Entrance Moat', Lgcl),
|
||||
@@ -417,7 +433,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Swamp Hammer Switch SW', Intr).dir(So, 0x37, Left, High).small_key().pos(2),
|
||||
create_door(player, 'Swamp Hammer Switch WN', Nrml).dir(We, 0x37, Top, High).pos(0),
|
||||
create_door(player, 'Swamp Hub ES', Nrml).dir(Ea, 0x36, Bot, High).pos(4),
|
||||
create_door(player, 'Swamp Hub S', Nrml).dir(So, 0x36, Mid, High).pos(5),
|
||||
create_door(player, 'Swamp Hub S', Nrml).dir(So, 0x36, Mid, High).pos(5), # .portal(Z, 0x22, 1), couldn't figure this out
|
||||
create_door(player, 'Swamp Hub WS', Nrml).dir(We, 0x36, Bot, High).pos(3),
|
||||
create_door(player, 'Swamp Hub WN', Nrml).dir(We, 0x36, Top, High).small_key().pos(2),
|
||||
create_door(player, 'Swamp Hub Hook Path', Lgcl),
|
||||
@@ -458,7 +474,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Swamp Attic Down Stairs', Sprl).dir(Dn, 0x54, 0, HTH).ss(Z, 0x12, 0x80).kill(),
|
||||
create_door(player, 'Swamp Attic Left Pit', Hole),
|
||||
create_door(player, 'Swamp Attic Right Pit', Hole),
|
||||
create_door(player, 'Swamp Push Statue S', Nrml).dir(So, 0x26, Mid, High).small_key().pos(0),
|
||||
create_door(player, 'Swamp Push Statue S', Nrml).dir(So, 0x26, Mid, High).small_key().pos(0).portal(Z, 0x20),
|
||||
create_door(player, 'Swamp Push Statue NW', Intr).dir(No, 0x26, Left, High).pos(1),
|
||||
create_door(player, 'Swamp Push Statue NE', Intr).dir(No, 0x26, Right, High).pos(2),
|
||||
create_door(player, 'Swamp Push Statue Down Stairs', Sprl).dir(Dn, 0x26, 2, HTH).ss(X, 0x12, 0xc0, False, True),
|
||||
@@ -479,7 +495,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Swamp Basement Shallows NW', Nrml).dir(No, 0x76, Left, High).toggler().pos(2),
|
||||
create_door(player, 'Swamp Basement Shallows EN', Intr).dir(Ea, 0x76, Top, High).pos(0),
|
||||
create_door(player, 'Swamp Basement Shallows ES', Intr).dir(Ea, 0x76, Bot, High).pos(1),
|
||||
create_door(player, 'Swamp Waterfall Room SW', Nrml).dir(So, 0x66, Left, Low).toggler().pos(1),
|
||||
create_door(player, 'Swamp Waterfall Room SW', Nrml).dir(So, 0x66, Left, Low).toggler().pos(1).portal(Z, 0x20, 1),
|
||||
create_door(player, 'Swamp Waterfall Room NW', Intr).dir(No, 0x66, Left, Low).pos(3),
|
||||
create_door(player, 'Swamp Waterfall Room NE', Intr).dir(No, 0x66, Right, Low).pos(0),
|
||||
create_door(player, 'Swamp Refill SW', Intr).dir(So, 0x66, Left, Low).pos(3),
|
||||
@@ -495,10 +511,11 @@ def create_doors(world, player):
|
||||
create_door(player, 'Swamp T NW', Nrml).dir(No, 0x16, Left, High).pos(3),
|
||||
create_door(player, 'Swamp Boss SW', Nrml).dir(So, 0x06, Left, High).no_exit().trap(0x4).pos(0),
|
||||
|
||||
create_door(player, 'Skull 1 Lobby S', Nrml).dir(So, 0x58, Left, High).pos(4).portal(Z, 0x00),
|
||||
create_door(player, 'Skull 1 Lobby WS', Nrml).dir(We, 0x58, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'Skull 1 Lobby ES', Intr).dir(Ea, 0x58, Bot, High).pos(5),
|
||||
create_door(player, 'Skull Map Room WS', Intr).dir(We, 0x58, Bot, High).pos(5),
|
||||
create_door(player, 'Skull Map Room SE', Nrml).dir(So, 0x58, Right, High).small_key().pos(2),
|
||||
create_door(player, 'Skull Map Room SE', Nrml).dir(So, 0x58, Right, High).small_key().pos(2).portal(X, 0x02),
|
||||
create_door(player, 'Skull Pot Circle WN', Intr).dir(We, 0x58, Top, High).pos(3),
|
||||
create_door(player, 'Skull Pull Switch EN', Intr).dir(Ea, 0x58, Top, High).pos(3),
|
||||
create_door(player, 'Skull Pot Circle Star Path', Lgcl),
|
||||
@@ -512,7 +529,8 @@ def create_doors(world, player):
|
||||
create_door(player, 'Skull Left Drop ES', Intr).dir(Ea, 0x67, Bot, High).pos(1),
|
||||
create_door(player, 'Skull Compass Room WS', Intr).dir(We, 0x67, Bot, High).pos(1),
|
||||
create_door(player, 'Skull Pot Prison ES', Nrml).dir(Ea, 0x57, Bot, High).small_key().pos(2),
|
||||
create_door(player, 'Skull Pot Prison SE', Nrml).dir(So, 0x57, Right, High).pos(5),
|
||||
create_door(player, 'Skull Pot Prison SE', Nrml).dir(So, 0x57, Right, High).pos(5).portal(X, 0x00),
|
||||
create_door(player, 'Skull 2 East Lobby SW', Nrml).dir(So, 0x57, Left, High).pos(3).portal(Z, 0x00),
|
||||
create_door(player, 'Skull 2 East Lobby WS', Nrml).dir(We, 0x57, Bot, High).pos(4),
|
||||
create_door(player, 'Skull 2 East Lobby NW', Intr).dir(No, 0x57, Left, High).pos(1),
|
||||
create_door(player, 'Skull Big Key SW', Intr).dir(So, 0x57, Left, High).pos(1),
|
||||
@@ -520,26 +538,29 @@ def create_doors(world, player):
|
||||
create_door(player, 'Skull Lone Pot EN', Intr).dir(Ea, 0x57, Top, High).pos(0),
|
||||
create_door(player, 'Skull Small Hall ES', Nrml).dir(Ea, 0x56, Bot, High).pos(3),
|
||||
create_door(player, 'Skull Small Hall WS', Intr).dir(We, 0x56, Bot, High).pos(2),
|
||||
create_door(player, 'Skull 2 West Lobby S', Nrml).dir(So, 0x56, Left, High).pos(1).portal(Z, 0x00),
|
||||
create_door(player, 'Skull 2 West Lobby ES', Intr).dir(Ea, 0x56, Bot, High).pos(2),
|
||||
create_door(player, 'Skull 2 West Lobby NW', Intr).dir(No, 0x56, Left, High).small_key().pos(0),
|
||||
create_door(player, 'Skull X Room SW', Intr).dir(So, 0x56, Left, High).small_key().pos(0),
|
||||
create_door(player, 'Skull Back Drop Star Path', Lgcl),
|
||||
create_door(player, 'Skull 3 Lobby SW', Nrml).dir(So, 0x59, Left, High).pos(1).portal(Z, 0x02),
|
||||
create_door(player, 'Skull 3 Lobby NW', Nrml).dir(No, 0x59, Left, High).small_key().pos(0),
|
||||
create_door(player, 'Skull 3 Lobby EN', Intr).dir(Ea, 0x59, Top, High).pos(2),
|
||||
create_door(player, 'Skull East Bridge WN', Intr).dir(We, 0x59, Top, High).pos(2),
|
||||
create_door(player, 'Skull East Bridge WS', Intr).dir(We, 0x59, Bot, High).pos(3),
|
||||
create_door(player, 'Skull West Bridge Nook ES', Intr).dir(Ea, 0x59, Bot, High).pos(3),
|
||||
create_door(player, 'Skull Star Pits SW', Nrml).dir(So, 0x49, Left, High).small_key().pos(2),
|
||||
create_door(player, 'Skull Star Pits SW', Nrml).dir(So, 0x49, Left, High).small_key().pos(2).portal(Z, 0x00),
|
||||
create_door(player, 'Skull Star Pits ES', Intr).dir(Ea, 0x49, Bot, High).pos(3),
|
||||
create_door(player, 'Skull Torch Room WS', Intr).dir(We, 0x49, Bot, High).pos(3),
|
||||
create_door(player, 'Skull Torch Room WN', Intr).dir(We, 0x49, Top, High).pos(1),
|
||||
create_door(player, 'Skull Vines EN', Intr).dir(Ea, 0x49, Top, High).pos(1),
|
||||
create_door(player, 'Skull Vines NW', Nrml).dir(No, 0x49, Left, High).pos(0),
|
||||
create_door(player, 'Skull Spike Corner SW', Nrml).dir(So, 0x39, Left, High).no_exit().trap(0x4).pos(0),
|
||||
create_door(player, 'Skull Spike Corner SW', Nrml).dir(So, 0x39, Left, High).no_exit().trap(0x4).pos(0).portal(Z, 0x00),
|
||||
create_door(player, 'Skull Spike Corner ES', Intr).dir(Ea, 0x39, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'Skull Final Drop WS', Intr).dir(We, 0x39, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'Skull Final Drop Hole', Hole),
|
||||
|
||||
create_door(player, 'Thieves Lobby S', Nrml).dir(So, 0xdb, Mid, High).pos(1).portal(Z, 0x22),
|
||||
create_door(player, 'Thieves Lobby N Edge', Open).dir(No, 0xdb, None, Low).edge(7, A, 0x10),
|
||||
create_door(player, 'Thieves Lobby NE Edge', Open).dir(No, 0xdb, None, Low).edge(8, S, 0x18),
|
||||
create_door(player, 'Thieves Lobby E', Nrml).dir(Ea, 0xdb, Mid, High).no_exit().trap(0x4).pos(0),
|
||||
@@ -561,10 +582,10 @@ def create_doors(world, player):
|
||||
create_door(player, 'Thieves Compass Room N Edge', Open).dir(No, 0xdc, None, Low).edge(10, A, 0x10),
|
||||
create_door(player, 'Thieves Compass Room WS Edge', Open).dir(We, 0xdc, None, Low).edge(8, Z, 0x50),
|
||||
create_door(player, 'Thieves Compass Room W', Nrml).dir(We, 0xdc, Mid, High).pos(0),
|
||||
create_door(player, 'Thieves Hallway SE', Nrml).dir(So, 0xbc, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Thieves Hallway SE', Nrml).dir(So, 0xbc, Right, High).small_key().pos(1).portal(X, 0x02),
|
||||
create_door(player, 'Thieves Hallway NE', Nrml).dir(No, 0xbc, Right, High).pos(7),
|
||||
create_door(player, 'Thieves Pot Alcove Mid WS', Nrml).dir(We, 0xbc, Bot, High).pos(5),
|
||||
create_door(player, 'Thieves Pot Alcove Bottom SW', Nrml).dir(So, 0xbc, Left, High).pos(3),
|
||||
create_door(player, 'Thieves Pot Alcove Bottom SW', Nrml).dir(So, 0xbc, Left, High).pos(3).portal(Z, 0x00),
|
||||
create_door(player, 'Thieves Conveyor Maze WN', Nrml).dir(We, 0xbc, Top, High).pos(4),
|
||||
create_door(player, 'Thieves Hallway WS', Intr).dir(We, 0xbc, Bot, High).small_key().pos(0),
|
||||
create_door(player, 'Thieves Pot Alcove Mid ES', Intr).dir(Ea, 0xbc, Bot, High).small_key().pos(0),
|
||||
@@ -587,7 +608,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Thieves Triple Bypass SE', Intr).dir(So, 0xbb, Right, High).pos(3),
|
||||
create_door(player, 'Thieves Hellway Crystal EN', Intr).dir(Ea, 0xbb, Top, High).pos(1),
|
||||
create_door(player, 'Thieves Triple Bypass WN', Intr).dir(We, 0xbb, Top, High).pos(1),
|
||||
create_door(player, 'Thieves Spike Switch SW', Nrml).dir(So, 0xab, Left, High).pos(1),
|
||||
create_door(player, 'Thieves Spike Switch SW', Nrml).dir(So, 0xab, Left, High).pos(1).portal(Z, 0x00),
|
||||
create_door(player, 'Thieves Spike Switch Up Stairs', Sprl).dir(Up, 0xab, 0, HTH).ss(Z, 0x1a, 0x6c, True, True).small_key().pos(0),
|
||||
create_door(player, 'Thieves Attic Down Stairs', Sprl).dir(Dn, 0x64, 0, HTH).ss(Z, 0x11, 0x80, True, True),
|
||||
create_door(player, 'Thieves Attic ES', Intr).dir(Ea, 0x64, Bot, High).pos(0),
|
||||
@@ -607,6 +628,8 @@ def create_doors(world, player):
|
||||
create_door(player, 'Thieves Lonely Zazak NW', Intr).dir(No, 0x45, Left, High).pos(1),
|
||||
create_door(player, 'Thieves Lonely Zazak ES', Intr).dir(Ea, 0x45, Right, High).pos(3),
|
||||
create_door(player, 'Thieves Blind\'s Cell WS', Intr).dir(We, 0x45, Right, High).pos(3),
|
||||
create_door(player, "Thieves Blind's Cell Door", Lgcl).big_key(),
|
||||
create_door(player, "Thieves Blind's Cell Exit", Lgcl),
|
||||
create_door(player, 'Thieves Conveyor Bridge EN', Nrml).dir(Ea, 0x44, Top, High).pos(2),
|
||||
create_door(player, 'Thieves Conveyor Bridge ES', Nrml).dir(Ea, 0x44, Bot, High).pos(3),
|
||||
create_door(player, 'Thieves Conveyor Bridge Block Path', Lgcl),
|
||||
@@ -616,6 +639,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Thieves Conveyor Block WN', Intr).dir(We, 0x44, Top, High).pos(0),
|
||||
create_door(player, 'Thieves Trap EN', Intr).dir(Ea, 0x44, Left, Top).pos(0),
|
||||
|
||||
create_door(player, 'Ice Lobby SE', Nrml).dir(So, 0x0e, Right, High).pos(2).portal(X, 0x00),
|
||||
create_door(player, 'Ice Lobby WS', Intr).dir(We, 0x0e, Bot, High).pos(1),
|
||||
create_door(player, 'Ice Jelly Key ES', Intr).dir(Ea, 0x0e, Bot, High).pos(1),
|
||||
create_door(player, 'Ice Jelly Key Down Stairs', Sprl).dir(Dn, 0x0e, 0, HTH).ss(Z, 0x11, 0x80, True, True).small_key().pos(0),
|
||||
@@ -631,7 +655,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Ice Cross Right Push Block Bottom', Lgcl), # dynamic
|
||||
create_door(player, 'Ice Cross Top Push Block Bottom', Lgcl), # dynamic
|
||||
create_door(player, 'Ice Cross Top Push Block Right', Lgcl), # dynamic
|
||||
create_door(player, 'Ice Cross Bottom SE', Nrml).dir(So, 0x1e, Right, High).pos(3),
|
||||
create_door(player, 'Ice Cross Bottom SE', Nrml).dir(So, 0x1e, Right, High).pos(3).portal(X, 0x00),
|
||||
create_door(player, 'Ice Cross Right ES', Nrml).dir(Ea, 0x1e, Bot, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Ice Bomb Drop Hole', Hole),
|
||||
create_door(player, 'Ice Compass Room NE', Nrml).dir(No, 0x2e, Right, High).pos(0),
|
||||
@@ -642,7 +666,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Ice Big Key Down Ladder', Lddr),
|
||||
create_door(player, 'Ice Stalfos Hint SE', Intr).dir(So, 0x3e, Right, High).pos(0),
|
||||
create_door(player, 'Ice Conveyor NE', Intr).dir(No, 0x3e, Right, High).no_exit().pos(0),
|
||||
create_door(player, 'Ice Conveyor SW', Nrml).dir(So, 0x3e, Left, High).small_key().pos(1),
|
||||
create_door(player, 'Ice Conveyor SW', Nrml).dir(So, 0x3e, Left, High).small_key().pos(1).portal(Z, 0x20),
|
||||
create_door(player, 'Ice Bomb Jump NW', Nrml).dir(No, 0x4e, Left, High).small_key().pos(1),
|
||||
create_door(player, 'Ice Bomb Jump Ledge Orange Barrier', Lgcl),
|
||||
create_door(player, 'Ice Bomb Jump Catwalk Orange Barrier', Lgcl),
|
||||
@@ -651,7 +675,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Ice Narrow Corridor Down Stairs', Sprl).dir(Dn, 0x4e, 0, HTH).ss(S, 0x52, 0xc0, True, True),
|
||||
create_door(player, 'Ice Pengator Trap Up Stairs', Sprl).dir(Up, 0x6e, 0, HTH).ss(S, 0x5a, 0xac, True, True),
|
||||
create_door(player, 'Ice Pengator Trap NE', Nrml).dir(No, 0x6e, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Ice Spike Cross SE', Nrml).dir(So, 0x5e, Right, High).pos(2),
|
||||
create_door(player, 'Ice Spike Cross SE', Nrml).dir(So, 0x5e, Right, High).pos(2).portal(X, 0x00),
|
||||
create_door(player, 'Ice Spike Cross ES', Nrml).dir(Ea, 0x5e, Bot, High).small_key().pos(0),
|
||||
create_door(player, 'Ice Spike Cross WS', Intr).dir(We, 0x5e, Bot, High).pos(3),
|
||||
create_door(player, 'Ice Firebar ES', Intr).dir(Ea, 0x5e, Bot, High).pos(3),
|
||||
@@ -673,7 +697,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Ice Freezors Ledge ES', Intr).dir(Ea, 0x7e, Bot, High).pos(2),
|
||||
create_door(player, 'Ice Tall Hint WS', Intr).dir(We, 0x7e, Bot, High).pos(1),
|
||||
create_door(player, 'Ice Tall Hint EN', Nrml).dir(Ea, 0x7e, Top, High).pos(2),
|
||||
create_door(player, 'Ice Tall Hint SE', Nrml).dir(So, 0x7e, Right, High).small_key().pos(0),
|
||||
create_door(player, 'Ice Tall Hint SE', Nrml).dir(So, 0x7e, Right, High).small_key().pos(0).portal(X, 0x02),
|
||||
create_door(player, 'Ice Hookshot Ledge WN', Nrml).dir(We, 0x7f, Top, High).no_exit().trap(0x4).pos(0).kill(),
|
||||
create_door(player, 'Ice Hookshot Ledge Path', Lgcl),
|
||||
create_door(player, 'Ice Hookshot Balcony Path', Lgcl),
|
||||
@@ -686,7 +710,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Iced T Up Stairs', Sprl).dir(Up, 0xae, 0, HTH).ss(S, 0x1a, 0x3c, True, True),
|
||||
create_door(player, 'Ice Catwalk WN', Nrml).dir(We, 0xaf, Top, High).pos(1),
|
||||
create_door(player, 'Ice Catwalk NW', Nrml).dir(No, 0xaf, Left, High).pos(0),
|
||||
create_door(player, 'Ice Many Pots SW', Nrml).dir(So, 0x9f, Left, High).trap(0x2).pos(1),
|
||||
create_door(player, 'Ice Many Pots SW', Nrml).dir(So, 0x9f, Left, High).trap(0x2).pos(1).portal(Z, 0x00),
|
||||
create_door(player, 'Ice Many Pots WS', Nrml).dir(We, 0x9f, Bot, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Ice Crystal Right ES', Nrml).dir(Ea, 0x9e, Bot, High).pos(3),
|
||||
create_door(player, 'Ice Crystal Right Orange Barrier', Lgcl),
|
||||
@@ -706,18 +730,19 @@ def create_doors(world, player):
|
||||
create_door(player, 'Ice Anti-Fairy SE', Intr).dir(So, 0xbe, Right, High).pos(2),
|
||||
create_door(player, 'Ice Switch Room NE', Intr).dir(No, 0xbe, Right, High).pos(2),
|
||||
create_door(player, 'Ice Switch Room ES', Nrml).dir(Ea, 0xbe, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'Ice Switch Room SE', Nrml).dir(So, 0xbe, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Ice Switch Room SE', Nrml).dir(So, 0xbe, Right, High).trap(0x4).pos(0).portal(X, 0x00),
|
||||
create_door(player, 'Ice Refill WS', Nrml).dir(We, 0xbf, Bot, High).small_key().pos(0),
|
||||
create_door(player, 'Ice Fairy Warp', Warp),
|
||||
create_door(player, 'Ice Antechamber NE', Nrml).dir(No, 0xce, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Ice Antechamber Hole', Hole),
|
||||
|
||||
create_door(player, 'Mire Lobby S', Nrml).dir(So, 0x98, Left, High).pos(0).portal(Z, 0x20),
|
||||
create_door(player, 'Mire Lobby Gap', Lgcl),
|
||||
create_door(player, 'Mire Post-Gap Gap', Lgcl),
|
||||
create_door(player, 'Mire Post-Gap Down Stairs', Sprl).dir(Dn, 0x98, 0, HTH).ss(X, 0x11, 0x90, False, True),
|
||||
create_door(player, 'Mire 2 Up Stairs', Sprl).dir(Up, 0xd2, 0, HTH).ss(X, 0x1a, 0x7c, False, True),
|
||||
create_door(player, 'Mire 2 NE', Nrml).dir(No, 0xd2, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'Mire Hub SE', Nrml).dir(So, 0xc2, Right, High).pos(5),
|
||||
create_door(player, 'Mire Hub SE', Nrml).dir(So, 0xc2, Right, High).pos(5).portal(X, 0x22),
|
||||
create_door(player, 'Mire Hub ES', Nrml).dir(Ea, 0xc2, Bot, High).pos(6),
|
||||
create_door(player, 'Mire Hub E', Nrml).dir(Ea, 0xc2, Mid, High).pos(4),
|
||||
create_door(player, 'Mire Hub NE', Nrml).dir(No, 0xc2, Right, High).pos(7),
|
||||
@@ -746,7 +771,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire Map Spot Blue Barrier', Lgcl),
|
||||
create_door(player, 'Mire Crystal Dead End Left Barrier', Lgcl),
|
||||
create_door(player, 'Mire Crystal Dead End Right Barrier', Lgcl),
|
||||
create_door(player, 'Mire Hidden Shooters SE', Nrml).dir(So, 0xb2, Right, High).pos(6),
|
||||
create_door(player, 'Mire Hidden Shooters SE', Nrml).dir(So, 0xb2, Right, High).pos(6).portal(X, 0x00),
|
||||
create_door(player, 'Mire Hidden Shooters ES', Nrml).dir(Ea, 0xb2, Bot, High).pos(7),
|
||||
create_door(player, 'Mire Hidden Shooters WS', Intr).dir(We, 0xb2, Bot, High).pos(1),
|
||||
create_door(player, 'Mire Cross ES', Intr).dir(Ea, 0xb2, Bot, High).pos(1),
|
||||
@@ -754,22 +779,22 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire Hidden Shooters Block Path N', Lgcl),
|
||||
create_door(player, 'Mire Hidden Shooters NE', Intr).dir(No, 0xb2, Right, High).pos(2),
|
||||
create_door(player, 'Mire Minibridge SE', Intr).dir(So, 0xb2, Right, High).pos(2),
|
||||
create_door(player, 'Mire Cross SW', Nrml).dir(So, 0xb2, Left, High).pos(5),
|
||||
create_door(player, 'Mire Cross SW', Nrml).dir(So, 0xb2, Left, High).pos(5).portal(Z, 0x00),
|
||||
create_door(player, 'Mire Minibridge NE', Nrml).dir(No, 0xb2, Right, High).pos(4),
|
||||
create_door(player, 'Mire BK Door Room EN', Nrml).dir(Ea, 0xb2, Top, Low).pos(3),
|
||||
create_door(player, 'Mire BK Door Room N', Nrml).dir(No, 0xb2, Mid, High).big_key().pos(0),
|
||||
create_door(player, 'Mire Spikes WS', Nrml).dir(We, 0xb3, Bot, High).pos(3),
|
||||
create_door(player, 'Mire Spikes SW', Nrml).dir(So, 0xb3, Left, High).pos(4),
|
||||
create_door(player, 'Mire Spikes SW', Nrml).dir(So, 0xb3, Left, High).pos(4).portal(Z, 0x00),
|
||||
create_door(player, 'Mire Spikes NW', Intr).dir(No, 0xb3, Left, High).small_key().pos(0),
|
||||
create_door(player, 'Mire Ledgehop SW', Intr).dir(So, 0xb3, Left, High).small_key().pos(0),
|
||||
create_door(player, 'Mire Ledgehop WN', Nrml).dir(We, 0xb3, Top, Low).pos(1),
|
||||
create_door(player, 'Mire Ledgehop NW', Nrml).dir(No, 0xb3, Left, High).pos(2),
|
||||
create_door(player, 'Mire Bent Bridge SW', Nrml).dir(So, 0xa3, Left, High).pos(1),
|
||||
create_door(player, 'Mire Bent Bridge SW', Nrml).dir(So, 0xa3, Left, High).pos(1).portal(Z, 0x02),
|
||||
create_door(player, 'Mire Bent Bridge W', Nrml).dir(We, 0xa3, Mid, High).pos(0),
|
||||
create_door(player, 'Mire Over Bridge E', Nrml).dir(Ea, 0xa2, Mid, High).pos(2),
|
||||
create_door(player, 'Mire Over Bridge W', Nrml).dir(We, 0xa2, Mid, High).pos(1),
|
||||
create_door(player, 'Mire Right Bridge SE', Nrml).dir(So, 0xa2, Right, High).pos(3),
|
||||
create_door(player, 'Mire Left Bridge S', Nrml).dir(So, 0xa2, Mid, High).small_key().pos(0),
|
||||
create_door(player, 'Mire Right Bridge SE', Nrml).dir(So, 0xa2, Right, High).pos(3).portal(X, 0x22),
|
||||
create_door(player, 'Mire Left Bridge S', Nrml).dir(So, 0xa2, Mid, High).small_key().pos(0).portal(Z, 0x22),
|
||||
create_door(player, 'Mire Left Bridge Hook Path', Lgcl),
|
||||
create_door(player, 'Mire Left Bridge Down Stairs', Sprl).dir(Dn, 0xa2, 0, HTL).ss(A, 0x12, 0x00),
|
||||
create_door(player, 'Mire Fishbone E', Nrml).dir(Ea, 0xa1, Mid, High).pos(1),
|
||||
@@ -777,7 +802,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire South Fish Blue Barrier', Lgcl),
|
||||
create_door(player, 'Mire Fishbone SE', Nrml).dir(So, 0xa1, Right, High).small_key().pos(0),
|
||||
create_door(player, 'Mire Spike Barrier NE', Nrml).dir(No, 0xb1, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Mire Spike Barrier SE', Nrml).dir(So, 0xb1, Right, High).pos(2),
|
||||
create_door(player, 'Mire Spike Barrier SE', Nrml).dir(So, 0xb1, Right, High).pos(2).portal(X, 0x02),
|
||||
create_door(player, 'Mire Spike Barrier ES', Intr).dir(Ea, 0xb1, Bot, High).pos(3),
|
||||
create_door(player, 'Mire Square Rail WS', Intr).dir(We, 0xb1, Bot, High).pos(3),
|
||||
create_door(player, 'Mire Square Rail NW', Intr).dir(No, 0xb1, Left, High).big_key().pos(0),
|
||||
@@ -786,10 +811,10 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire Wizzrobe Bypass EN', Nrml).dir(Ea, 0xc1, Top, High).pos(5),
|
||||
create_door(player, 'Mire Wizzrobe Bypass NE', Nrml).dir(No, 0xc1, Right, High).pos(6),
|
||||
create_door(player, 'Mire Conveyor Crystal ES', Nrml).dir(Ea, 0xc1, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'Mire Conveyor Crystal SE', Nrml).dir(So, 0xc1, Right, High).pos(7),
|
||||
create_door(player, 'Mire Conveyor Crystal SE', Nrml).dir(So, 0xc1, Right, High).pos(7).portal(X, 0x00),
|
||||
create_door(player, 'Mire Conveyor Crystal WS', Intr).dir(We, 0xc1, Bot, High).small_key().pos(0),
|
||||
create_door(player, 'Mire Tile Room ES', Intr).dir(Ea, 0xc1, Bot, High).small_key().pos(0),
|
||||
create_door(player, 'Mire Tile Room SW', Nrml).dir(So, 0xc1, Left, High).pos(4),
|
||||
create_door(player, 'Mire Tile Room SW', Nrml).dir(So, 0xc1, Left, High).pos(4).portal(Z, 0x00),
|
||||
create_door(player, 'Mire Tile Room NW', Intr).dir(No, 0xc1, Left, High).pos(3),
|
||||
create_door(player, 'Mire Compass Room SW', Intr).dir(So, 0xc1, Left, High).pos(3),
|
||||
create_door(player, 'Mire Compass Room EN', Intr).dir(Ea, 0xc1, Top, High).pos(2),
|
||||
@@ -838,12 +863,13 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire Antechamber NW', Nrml).dir(No, 0xa0, Left, High).big_key().pos(0),
|
||||
create_door(player, 'Mire Boss SW', Nrml).dir(So, 0x90, Left, High).no_exit().trap(0x4).pos(0),
|
||||
|
||||
create_door(player, 'TR Main Lobby SE', Nrml).dir(So, 0xd6, Right, High).pos(1).portal(X, 0x02),
|
||||
create_door(player, 'TR Lobby Ledge NE', Nrml).dir(No, 0xd6, Right, High).pos(2),
|
||||
create_door(player, 'TR Main Lobby Gap', Lgcl),
|
||||
create_door(player, 'TR Lobby Ledge Gap', Lgcl),
|
||||
create_door(player, 'TR Compass Room NW', Nrml).dir(No, 0xd6, Left, High).pos(0),
|
||||
create_door(player, 'TR Hub SW', Nrml).dir(So, 0xc6, Left, High).pos(4),
|
||||
create_door(player, 'TR Hub SE', Nrml).dir(So, 0xc6, Right, High).pos(5),
|
||||
create_door(player, 'TR Hub SW', Nrml).dir(So, 0xc6, Left, High).pos(4).portal(Z, 0x22),
|
||||
create_door(player, 'TR Hub SE', Nrml).dir(So, 0xc6, Right, High).pos(5).portal(X, 0x22),
|
||||
create_door(player, 'TR Hub ES', Nrml).dir(Ea, 0xc6, Bot, High).pos(3),
|
||||
create_door(player, 'TR Hub EN', Nrml).dir(Ea, 0xc6, Top, High).pos(2),
|
||||
create_door(player, 'TR Hub NW', Nrml).dir(No, 0xc6, Left, High).small_key().pos(0),
|
||||
@@ -851,9 +877,9 @@ def create_doors(world, player):
|
||||
create_door(player, 'TR Torches Ledge WS', Nrml).dir(We, 0xc7, Bot, High).pos(2),
|
||||
create_door(player, 'TR Torches WN', Nrml).dir(We, 0xc7, Top, High).pos(1),
|
||||
create_door(player, 'TR Torches NW', Nrml).dir(No, 0xc7, Left, High).trap(0x4).pos(0),
|
||||
create_door(player, 'TR Roller Room SW', Nrml).dir(So, 0xb7, Left, High).pos(0),
|
||||
create_door(player, 'TR Pokey 1 SW', Nrml).dir(So, 0xb6, Left, High).small_key().pos(2),
|
||||
create_door(player, 'TR Tile Room SE', Nrml).dir(So, 0xb6, Right, High).pos(4),
|
||||
create_door(player, 'TR Roller Room SW', Nrml).dir(So, 0xb7, Left, High).pos(0).portal(Z, 0x02),
|
||||
create_door(player, 'TR Pokey 1 SW', Nrml).dir(So, 0xb6, Left, High).small_key().pos(2).portal(Z, 0x00),
|
||||
create_door(player, 'TR Tile Room SE', Nrml).dir(So, 0xb6, Right, High).pos(4).portal(X, 0x00),
|
||||
create_door(player, 'TR Tile Room NE', Intr).dir(No, 0xb6, Right, High).pos(1),
|
||||
create_door(player, 'TR Refill SE', Intr).dir(So, 0xb6, Right, High).pos(1),
|
||||
create_door(player, 'TR Pokey 1 NW', Intr).dir(No, 0xb6, Left, High).small_key().pos(3),
|
||||
@@ -865,7 +891,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'TR Pipe Ledge Drop Down', Lgcl),
|
||||
create_door(player, 'TR Lava Dual Pipes EN', Nrml).dir(Ea, 0x14, Top, High).pos(5),
|
||||
create_door(player, 'TR Lava Dual Pipes WN', Nrml).dir(We, 0x14, Top, High).pos(3),
|
||||
create_door(player, 'TR Lava Dual Pipes SW', Nrml).dir(So, 0x14, Left, High).pos(4),
|
||||
create_door(player, 'TR Lava Dual Pipes SW', Nrml).dir(So, 0x14, Left, High).pos(4).portal(Z, 0x22),
|
||||
create_door(player, 'TR Lava Island WS', Nrml).dir(We, 0x14, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'TR Lava Island ES', Nrml).dir(Ea, 0x14, Bot, High).pos(6),
|
||||
create_door(player, 'TR Lava Escape SE', Nrml).dir(So, 0x14, Right, High).small_key().pos(0),
|
||||
@@ -881,12 +907,14 @@ def create_doors(world, player):
|
||||
create_door(player, 'TR Hallway ES', Intr).dir(Ea, 0x24, Bot, High).pos(7),
|
||||
create_door(player, 'TR Big View WS', Intr).dir(We, 0x24, Bot, High).pos(7),
|
||||
create_door(player, 'TR Big Chest Gap', Lgcl),
|
||||
create_door(player, 'TR Big Chest Entrance SE', Nrml).dir(So, 0x24, Right, High).pos(4).kill().portal(X, 0x00),
|
||||
create_door(player, 'TR Big Chest Entrance Gap', Lgcl),
|
||||
create_door(player, 'TR Big Chest NE', Intr).dir(No, 0x24, Right, High).pos(3),
|
||||
create_door(player, 'TR Dodgers SE', Intr).dir(So, 0x24, Right, High).no_exit().pos(3),
|
||||
create_door(player, 'TR Dodgers NE', Nrml).dir(No, 0x24, Right, High).big_key().pos(0),
|
||||
create_door(player, 'TR Lazy Eyes SE', Nrml).dir(So, 0x23, Right, High).pos(0).portal(X, 0x00),
|
||||
create_door(player, 'TR Lazy Eyes ES', Nrml).dir(Ea, 0x23, Bot, High).pos(1),
|
||||
create_door(player, 'TR Dash Room SW', Nrml).dir(So, 0x04, Left, High).pos(4),
|
||||
create_door(player, 'TR Dash Room SW', Nrml).dir(So, 0x04, Left, High).pos(4).portal(Z, 0x00),
|
||||
create_door(player, 'TR Dash Room ES', Intr).dir(Ea, 0x04, Bot, High).pos(2),
|
||||
create_door(player, 'TR Tongue Pull WS', Intr).dir(We, 0x04, Bot, High).pos(2),
|
||||
create_door(player, 'TR Tongue Pull NE', Intr).dir(No, 0x04, Right, High).pos(3),
|
||||
@@ -895,11 +923,12 @@ def create_doors(world, player):
|
||||
create_door(player, 'TR Crystaroller SW', Intr).dir(So, 0x04, Left, High).pos(1),
|
||||
create_door(player, 'TR Crystaroller Down Stairs', Sprl).dir(Dn, 0x04, 0, HTH).ss(A, 0x12, 0x80, True, True).small_key().pos(0),
|
||||
create_door(player, 'TR Dark Ride Up Stairs', Sprl).dir(Up, 0xb5, 0, HTH).ss(A, 0x1b, 0x6c),
|
||||
create_door(player, 'TR Dark Ride SW', Nrml).dir(So, 0xb5, Left, High).trap(0x4).pos(0),
|
||||
create_door(player, 'TR Dark Ride SW', Nrml).dir(So, 0xb5, Left, High).trap(0x4).pos(0).portal(Z, 0x22),
|
||||
create_door(player, 'TR Dash Bridge NW', Nrml).dir(No, 0xc5, Left, High).pos(1),
|
||||
create_door(player, 'TR Dash Bridge SW', Nrml).dir(So, 0xc5, Left, High).pos(2),
|
||||
create_door(player, 'TR Dash Bridge SW', Nrml).dir(So, 0xc5, Left, High).pos(2).portal(Z, 0x02),
|
||||
create_door(player, 'TR Dash Bridge WS', Nrml).dir(We, 0xc5, Bot, High).small_key().pos(0),
|
||||
create_door(player, 'TR Eye Bridge NW', Nrml).dir(No, 0xd5, Left, High).pos(1),
|
||||
create_door(player, 'TR Eye Bridge SW', Nrml).dir(So, 0xd5, Left, High).pos(0).portal(Z, 0x02),
|
||||
create_door(player, 'TR Crystal Maze ES', Nrml).dir(Ea, 0xc4, Bot, High).small_key().pos(0),
|
||||
create_door(player, 'TR Crystal Maze Forwards Path', Lgcl),
|
||||
create_door(player, 'TR Crystal Maze Blue Path', Lgcl),
|
||||
@@ -907,8 +936,9 @@ def create_doors(world, player):
|
||||
create_door(player, 'TR Crystal Maze North Stairs', StrS).dir(No, 0xc4, Mid, High),
|
||||
create_door(player, 'TR Final Abyss South Stairs', StrS).dir(So, 0xb4, Mid, High),
|
||||
create_door(player, 'TR Final Abyss NW', Nrml).dir(No, 0xb4, Left, High).big_key().pos(0),
|
||||
create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0),
|
||||
create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), -enemizer doesn't work
|
||||
|
||||
create_door(player, 'GT Lobby S', Nrml).dir(So, 0x0c, Mid, High).pos(0).portal(Z, 0x22),
|
||||
create_door(player, 'GT Lobby Left Down Stairs', Sprl).dir(Dn, 0x0c, 1, HTL).ss(A, 0x0f, 0x80),
|
||||
create_door(player, 'GT Lobby Up Stairs', Sprl).dir(Up, 0x0c, 2, HTH).ss(A, 0x1b, 0xec),
|
||||
create_door(player, 'GT Lobby Right Down Stairs', Sprl).dir(Dn, 0x0c, 0, HTL).ss(S, 0x12, 0x80),
|
||||
@@ -922,8 +952,8 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Big Chest NW', Intr).dir(No, 0x8c, Left, High).pos(1),
|
||||
create_door(player, 'GT Blocked Stairs Down Stairs', Sprl).dir(Dn, 0x8c, 3, HTH).ss(Z, 0x12, 0x40, True, True).kill(),
|
||||
create_door(player, 'GT Blocked Stairs Block Path', Lgcl),
|
||||
create_door(player, 'GT Big Chest SW', Nrml).dir(So, 0x8c, Left, High).pos(4),
|
||||
create_door(player, 'GT Bob\'s Room SE', Nrml).dir(So, 0x8c, Right, High).pos(5).kill(),
|
||||
create_door(player, 'GT Big Chest SW', Nrml).dir(So, 0x8c, Left, High).pos(4).portal(Z, 0x00),
|
||||
create_door(player, 'GT Bob\'s Room SE', Nrml).dir(So, 0x8c, Right, High).pos(5).kill().portal(X, 0x00),
|
||||
create_door(player, 'GT Bob\'s Room Hole', Hole),
|
||||
create_door(player, 'GT Tile Room WN', Nrml).dir(We, 0x8d, Top, High).pos(2),
|
||||
create_door(player, 'GT Tile Room EN', Intr).dir(Ea, 0x8d, Top, High).small_key().pos(1),
|
||||
@@ -933,7 +963,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Speed Torch North Path', Lgcl),
|
||||
create_door(player, 'GT Speed Torch WS', Intr).dir(We, 0x8d, Bot, High).pos(4),
|
||||
create_door(player, 'GT Pots n Blocks ES', Intr).dir(Ea, 0x8d, Bot, High).pos(4),
|
||||
create_door(player, 'GT Speed Torch SE', Nrml).dir(So, 0x8d, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'GT Speed Torch SE', Nrml).dir(So, 0x8d, Right, High).trap(0x4).pos(0).portal(X, 0x02),
|
||||
create_door(player, 'GT Crystal Conveyor NE', Nrml).dir(No, 0x9d, Right, High).pos(0).kill(),
|
||||
create_door(player, 'GT Crystal Conveyor WN', Intr).dir(We, 0x9d, Top, High).pos(2),
|
||||
create_door(player, 'GT Compass Room EN', Intr).dir(Ea, 0x9d, Top, High).pos(2),
|
||||
@@ -954,10 +984,11 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Hookshot South-North Path', Lgcl),
|
||||
create_door(player, 'GT Hookshot Platform Blue Barrier', Lgcl),
|
||||
create_door(player, 'GT Hookshot Entry Blue Barrier', Lgcl),
|
||||
create_door(player, 'GT Hookshot Entry Boomerang Path', Lgcl),
|
||||
create_door(player, 'GT Hookshot NW', Nrml).dir(No, 0x8b, Left, High).pos(4),
|
||||
create_door(player, 'GT Hookshot ES', Intr).dir(Ea, 0x8b, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'GT Map Room WS', Intr).dir(We, 0x8b, Bot, High).small_key().pos(1),
|
||||
create_door(player, 'GT Hookshot SW', Nrml).dir(So, 0x8b, Left, High).pos(3),
|
||||
create_door(player, 'GT Hookshot SW', Nrml).dir(So, 0x8b, Left, High).pos(3).portal(Z, 0x02),
|
||||
create_door(player, 'GT Double Switch NW', Nrml).dir(No, 0x9b, Left, High).pos(1).kill(),
|
||||
create_door(player, 'GT Double Switch Orange Barrier', Lgcl),
|
||||
create_door(player, 'GT Double Switch Orange Barrier 2', Lgcl),
|
||||
@@ -991,11 +1022,11 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Warp Maze - Main Rails Right Mid Warp', Warp),
|
||||
create_door(player, 'GT Warp Maze - Pot Rail Warp', Warp),
|
||||
create_door(player, 'GT Warp Maze (Rails) WS', Nrml).dir(We, 0x7d, Bot, High).pos(1),
|
||||
create_door(player, 'GT Petting Zoo SE', Nrml).dir(So, 0x7d, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'GT Petting Zoo SE', Nrml).dir(So, 0x7d, Right, High).trap(0x4).pos(0).portal(X, 0x00),
|
||||
create_door(player, 'GT Conveyor Star Pits EN', Nrml).dir(Ea, 0x7b, Top, High).small_key().pos(1),
|
||||
create_door(player, 'GT Hidden Star ES', Nrml).dir(Ea, 0x7b, Bot, High).pos(2).kill(),
|
||||
create_door(player, 'GT Hidden Star Warp', Warp),
|
||||
create_door(player, 'GT DMs Room SW', Nrml).dir(So, 0x7b, Left, High).trap(0x4).pos(0),
|
||||
create_door(player, 'GT DMs Room SW', Nrml).dir(So, 0x7b, Left, High).trap(0x4).pos(0).portal(Z, 0x00),
|
||||
create_door(player, 'GT Falling Bridge WN', Nrml).dir(We, 0x7c, Top, High).small_key().pos(2),
|
||||
create_door(player, 'GT Falling Bridge WS', Nrml).dir(We, 0x7c, Bot, High).pos(3),
|
||||
create_door(player, 'GT Randomizer Room ES', Nrml).dir(Ea, 0x7c, Bot, High).pos(1),
|
||||
@@ -1014,7 +1045,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Mimics 2 NE', Intr).dir(No, 0x6b, Right, High).pos(1),
|
||||
create_door(player, 'GT Dash Hall SE', Intr).dir(So, 0x6b, Right, High).pos(1),
|
||||
create_door(player, 'GT Dash Hall NE', Nrml).dir(No, 0x6b, Right, High).big_key().pos(0),
|
||||
create_door(player, 'GT Hidden Spikes SE', Nrml).dir(So, 0x5b, Right, High).small_key().pos(0),
|
||||
create_door(player, 'GT Hidden Spikes SE', Nrml).dir(So, 0x5b, Right, High).small_key().pos(0).portal(X, 0x02),
|
||||
create_door(player, 'GT Hidden Spikes EN', Nrml).dir(Ea, 0x5b, Top, High).trap(0x2).pos(1),
|
||||
create_door(player, 'GT Cannonball Bridge WN', Nrml).dir(We, 0x5c, Top, High).pos(1),
|
||||
create_door(player, 'GT Cannonball Bridge SE', Intr).dir(So, 0x5c, Right, High).pos(0),
|
||||
@@ -1025,7 +1056,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Gauntlet 2 EN', Intr).dir(Ea, 0x5d, Top, High).pos(2),
|
||||
create_door(player, 'GT Gauntlet 2 SW', Intr).dir(So, 0x5d, Left, High).pos(0),
|
||||
create_door(player, 'GT Gauntlet 3 NW', Intr).dir(No, 0x5d, Left, High).pos(0),
|
||||
create_door(player, 'GT Gauntlet 3 SW', Nrml).dir(So, 0x5d, Left, High).trap(0x2).pos(1),
|
||||
create_door(player, 'GT Gauntlet 3 SW', Nrml).dir(So, 0x5d, Left, High).trap(0x2).pos(1).portal(Z, 0x00),
|
||||
create_door(player, 'GT Gauntlet 4 NW', Nrml).dir(No, 0x6d, Left, High).trap(0x4).pos(0),
|
||||
create_door(player, 'GT Gauntlet 4 SW', Intr).dir(So, 0x6d, Left, High).pos(1),
|
||||
create_door(player, 'GT Gauntlet 5 NW', Intr).dir(No, 0x6d, Left, High).pos(1),
|
||||
@@ -1042,7 +1073,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Dashing Bridge NE', Intr).dir(No, 0xa5, Right, High).pos(1),
|
||||
create_door(player, 'GT Wizzrobes 2 SE', Intr).dir(So, 0xa5, Right, High).pos(1),
|
||||
create_door(player, 'GT Wizzrobes 2 NE', Nrml).dir(No, 0xa5, Right, High).trap(0x4).pos(0),
|
||||
create_door(player, 'GT Conveyor Bridge SE', Nrml).dir(So, 0x95, Right, High).pos(0),
|
||||
create_door(player, 'GT Conveyor Bridge SE', Nrml).dir(So, 0x95, Right, High).pos(0).portal(X, 0x02),
|
||||
create_door(player, 'GT Conveyor Bridge EN', Nrml).dir(Ea, 0x95, Top, High).pos(1),
|
||||
create_door(player, 'GT Torch Cross WN', Nrml).dir(We, 0x96, Top, High).pos(1),
|
||||
create_door(player, 'GT Torch Cross ES', Intr).dir(Ea, 0x96, Bot, High).pos(0),
|
||||
@@ -1056,7 +1087,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Bomb Conveyor EN', Intr).dir(Ea, 0x3d, Top, High).small_key().pos(1),
|
||||
create_door(player, 'GT Bomb Conveyor SW', Intr).dir(So, 0x3d, Left, High).pos(3),
|
||||
create_door(player, 'GT Crystal Circles NW', Intr).dir(No, 0x3d, Left, High).pos(3),
|
||||
create_door(player, 'GT Crystal Circles SW', Nrml).dir(So, 0x3d, Left, High).small_key().pos(2),
|
||||
create_door(player, 'GT Crystal Circles SW', Nrml).dir(So, 0x3d, Left, High).small_key().pos(2).portal(Z, 0x00),
|
||||
create_door(player, 'GT Left Moldorm Ledge NW', Nrml).dir(No, 0x4d, Left, High).small_key().pos(0).kill(),
|
||||
create_door(player, 'GT Left Moldorm Ledge Drop Down', Lgcl),
|
||||
create_door(player, 'GT Right Moldorm Ledge Drop Down', Lgcl),
|
||||
@@ -1209,6 +1240,7 @@ def create_doors(world, player):
|
||||
world.get_door('GT Hookshot South-East Path', player).c_switch()
|
||||
world.get_door('GT Hookshot ES', player).c_switch()
|
||||
world.get_door('GT Hookshot Platform Blue Barrier', player).c_switch()
|
||||
world.get_door('GT Hookshot Entry Boomerang Path', player).c_switch()
|
||||
world.get_door('GT Double Switch Orange Path', player).c_switch()
|
||||
world.get_door('GT Double Switch Blue Path', player).c_switch()
|
||||
world.get_door('GT Spike Crystals WN', player).c_switch()
|
||||
@@ -1239,6 +1271,70 @@ def create_doors(world, player):
|
||||
|
||||
assign_entrances(world, player)
|
||||
|
||||
dungeon_portals = [
|
||||
create_portal(player, 'Sanctuary', world.get_door('Sanctuary S', player), 0x02, 0x02),
|
||||
create_portal(player, 'Hyrule Castle West', world.get_door('Hyrule Castle West Lobby S', player), 0x03, 0x04),
|
||||
create_portal(player, 'Hyrule Castle South', world.get_door('Hyrule Castle Lobby S', player), 0x04, 0x06),
|
||||
create_portal(player, 'Hyrule Castle East', world.get_door('Hyrule Castle East Lobby S', player), 0x05, 0x08),
|
||||
create_portal(player, 'Eastern', world.get_door('Eastern Lobby S', player), 0x08, 0x12, 0),
|
||||
create_portal(player, 'Desert South', world.get_door('Desert Main Lobby S', player), 0x09, 0x14),
|
||||
create_portal(player, 'Desert East', world.get_door('Desert East Lobby S', player), 0x0a, 0x16),
|
||||
create_portal(player, 'Desert West', world.get_door('Desert West S', player), 0x0b, 0x18),
|
||||
create_portal(player, 'Desert Back', world.get_door('Desert Back Lobby S', player), 0x0c, 0x1a, 1),
|
||||
create_portal(player, 'Turtle Rock Lazy Eyes', world.get_door('TR Lazy Eyes SE', player), 0x15, 0x2c),
|
||||
create_portal(player, 'Turtle Rock Eye Bridge', world.get_door('TR Eye Bridge SW', player), 0x18, 0x32),
|
||||
create_portal(player, 'Turtle Rock Chest', world.get_door('TR Big Chest Entrance SE', player), 0x19, 0x34),
|
||||
create_portal(player, 'Agahnims Tower', world.get_door('Tower Lobby S', player), 0x24, 0x4a),
|
||||
create_portal(player, 'Swamp', world.get_door('Swamp Lobby S', player), 0x25, 0x4c, 4),
|
||||
create_portal(player, 'Palace of Darkness', world.get_door('PoD Lobby S', player), 0x26, 0x4e, 5),
|
||||
create_portal(player, 'Mire', world.get_door('Mire Lobby S', player), 0x27, 0x50, 7),
|
||||
create_portal(player, 'Skull 2 West', world.get_door('Skull 2 West Lobby S', player), 0x28, 0x52),
|
||||
create_portal(player, 'Skull 2 East', world.get_door('Skull 2 East Lobby SW', player), 0x29, 0x54),
|
||||
create_portal(player, 'Skull 1', world.get_door('Skull 1 Lobby S', player), 0x2a, 0x56),
|
||||
create_portal(player, 'Skull 3', world.get_door('Skull 3 Lobby SW', player), 0x2b, 0x58, 6),
|
||||
create_portal(player, 'Ice', world.get_door('Ice Lobby SE', player), 0x2d, 0x5c, 8),
|
||||
create_portal(player, 'Hera', world.get_door('Hera Lobby S', player), 0x33, 0x5a, 2),
|
||||
create_portal(player, 'Thieves Town', world.get_door('Thieves Lobby S', player), 0x34, 0x6a, 10),
|
||||
create_portal(player, 'Turtle Rock Main', world.get_door('TR Main Lobby SE', player), 0x35, 0x68, 9),
|
||||
create_portal(player, 'Ganons Tower', world.get_door('GT Lobby S', player), 0x37, 0x70),
|
||||
]
|
||||
world.dungeon_portals[player] += dungeon_portals
|
||||
|
||||
world.get_door('Sanctuary S', player).dead_end(allowPassage=True)
|
||||
world.get_door('TR Big Chest Entrance SE', player).passage = False
|
||||
world.get_door('Sewers Secret Room Key Door S', player).dungeonLink = 'Hyrule Castle'
|
||||
world.get_door('Desert Cannonball S', player).dead_end()
|
||||
# world.get_door('Desert Boss SW', player).dead_end()
|
||||
# world.get_door('Desert Boss SW', player).dungeonLink = 'Desert Palace'
|
||||
world.get_door('Skull 1 Lobby S', player).dungeonLink = 'Skull Woods'
|
||||
world.get_door('Skull Map Room SE', player).dungeonLink = 'Skull Woods'
|
||||
world.get_door('Skull Spike Corner SW', player).dungeonLink = 'Skull Woods'
|
||||
world.get_door('Skull Spike Corner SW', player).dead_end()
|
||||
world.get_door('Thieves Pot Alcove Bottom SW', player).dead_end()
|
||||
world.get_door('Ice Conveyor SW', player).dead_end(allowPassage=True)
|
||||
world.get_door('Mire Right Bridge SE', player).dead_end(allowPassage=True)
|
||||
world.get_door('TR Roller Room SW', player).dead_end()
|
||||
world.get_door('TR Tile Room SE', player).dead_end()
|
||||
# world.get_door('TR Boss SW', player).dead_end()
|
||||
# world.get_door('TR Boss SW', player).dungeonLink = 'Turtle Rock'
|
||||
world.get_door('GT Petting Zoo SE', player).dead_end()
|
||||
world.get_door('GT DMs Room SW', player).dead_end()
|
||||
world.get_door("GT Bob\'s Room SE", player).passage = False
|
||||
world.get_door('PoD Mimics 2 SW', player).bk_shuffle_req = True
|
||||
world.get_door('Desert Tiles 2 SE', player).bk_shuffle_req = True # key-drop note (todo)
|
||||
|
||||
# can't unlink from boss right now
|
||||
world.get_door('Hera Lobby S', player).dungeonLink = 'Tower of Hera'
|
||||
# can't unlink from skull woods right now
|
||||
world.get_door('Skull 2 West Lobby S', player).dungeonLink = 'Skull Woods'
|
||||
|
||||
world.get_door('Ice Spike Cross SE', player).dungeonLink = 'linkIceFalls'
|
||||
world.get_door('Ice Tall Hint SE', player).dungeonLink = 'linkIceFalls'
|
||||
world.get_door('Ice Switch Room SE', player).dungeonLink = 'linkIceFalls'
|
||||
|
||||
world.get_door('Ice Cross Bottom SE', player).dungeonLink = 'linkIceFalls2'
|
||||
world.get_door('Ice Conveyor SW', player).dungeonLink = 'linkIceFalls2'
|
||||
|
||||
|
||||
def create_paired_doors(world, player):
|
||||
world.paired_doors[player] = [
|
||||
@@ -1304,3 +1400,8 @@ def create_door(player, name, door_type):
|
||||
def ugly_door(door):
|
||||
door.ugly = True
|
||||
return door
|
||||
|
||||
|
||||
def create_portal(player, name, door, ent, ext, boss_exit_idx=-1):
|
||||
door.entranceFlag = True
|
||||
return Portal(player, name, door, ent, ext, boss_exit_idx)
|
||||
|
||||
+300
-143
@@ -9,9 +9,9 @@ import operator as op
|
||||
import time
|
||||
from typing import List
|
||||
|
||||
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys
|
||||
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys, Sector
|
||||
from BaseClasses import Hook, hook_from_door
|
||||
from Regions import key_only_locations, dungeon_events, flooded_keys_reverse
|
||||
from Regions import dungeon_events, flooded_keys_reverse
|
||||
from Dungeons import dungeon_regions, split_region_starts
|
||||
from RoomData import DoorKind
|
||||
|
||||
@@ -30,6 +30,12 @@ class GraphPiece:
|
||||
# Dungeons shouldn't be generated until all entrances are appropriately accessible
|
||||
def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
|
||||
entrance_regions = convert_regions(entrance_region_names, world, player)
|
||||
excluded = {}
|
||||
for region in entrance_regions:
|
||||
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
||||
if portal and portal.destination:
|
||||
excluded[region] = None
|
||||
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
|
||||
proposed_map = {}
|
||||
doors_to_connect = {}
|
||||
all_regions = set()
|
||||
@@ -41,11 +47,11 @@ def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
|
||||
all_regions.update(sector.regions)
|
||||
bk_needed = bk_needed or determine_if_bk_needed(sector, split_dungeon, world, player)
|
||||
bk_special = bk_special or check_for_special(sector)
|
||||
paths = determine_required_paths(world, player, split_dungeon, all_regions, builder.name)[builder.name]
|
||||
paths = determine_paths_for_dungeon(world, player, all_regions, builder.name)
|
||||
dungeon, hangers, hooks = gen_dungeon_info(builder.name, builder.sectors, entrance_regions, all_regions,
|
||||
proposed_map, doors_to_connect, bk_needed, bk_special, world, player)
|
||||
return check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||
bk_needed, paths, entrance_regions)
|
||||
return check_valid(builder.name, dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||
bk_needed, bk_special, paths, entrance_regions, world, player)
|
||||
|
||||
|
||||
def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player):
|
||||
@@ -69,11 +75,14 @@ def generate_dungeon_main(builder, entrance_region_names, split_dungeon, world,
|
||||
proposed_map = builder.valid_proposal
|
||||
else:
|
||||
proposed_map = generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon, world, player)
|
||||
builder.valid_proposal = proposed_map
|
||||
queue = collections.deque(proposed_map.items())
|
||||
while len(queue) > 0:
|
||||
a, b = queue.popleft()
|
||||
connect_doors(a, b)
|
||||
queue.remove((b, a))
|
||||
if len(builder.sectors) == 0:
|
||||
return Sector()
|
||||
available_sectors = list(builder.sectors)
|
||||
master_sector = available_sectors.pop()
|
||||
for sub_sector in available_sectors:
|
||||
@@ -87,6 +96,12 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
||||
logger = logging.getLogger('')
|
||||
name = builder.name
|
||||
entrance_regions = convert_regions(entrance_region_names, world, player)
|
||||
excluded = {}
|
||||
for region in entrance_regions:
|
||||
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
||||
if portal and portal.destination:
|
||||
excluded[region] = None
|
||||
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
|
||||
doors_to_connect = {}
|
||||
all_regions = set()
|
||||
bk_needed = False
|
||||
@@ -106,7 +121,7 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
||||
attempt = 1
|
||||
finished = False
|
||||
# flag if standard and this is hyrule castle
|
||||
paths = determine_required_paths(world, player, split_dungeon, all_regions, name)[name]
|
||||
paths = determine_paths_for_dungeon(world, player, all_regions, name)
|
||||
while not finished:
|
||||
# what are my choices?
|
||||
itr += 1
|
||||
@@ -125,8 +140,8 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
||||
dungeon, hangers, hooks = gen_dungeon_info(name, builder.sectors, entrance_regions, all_regions, proposed_map,
|
||||
doors_to_connect, bk_needed, bk_special, world, player)
|
||||
dungeon_cache[depth] = dungeon, hangers, hooks
|
||||
valid = check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||
bk_needed, paths, entrance_regions)
|
||||
valid = check_valid(name, dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||
bk_needed, bk_special, paths, entrance_regions, world, player)
|
||||
else:
|
||||
dungeon, hangers, hooks = dungeon_cache[depth]
|
||||
valid = True
|
||||
@@ -181,7 +196,11 @@ def determine_if_bk_needed(sector, split_dungeon, world, player):
|
||||
|
||||
|
||||
def check_for_special(sector):
|
||||
return 'Hyrule Dungeon Cellblock' in sector.region_set()
|
||||
for region in sector.regions:
|
||||
for loc in region.locations:
|
||||
if loc.forced_big_key():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def gen_dungeon_info(name, available_sectors, entrance_regions, all_regions, proposed_map, valid_doors, bk_needed, bk_special, world, player):
|
||||
@@ -190,11 +209,12 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, all_regions, pro
|
||||
start = ExplorationState(dungeon=name)
|
||||
start.big_key_special = bk_special
|
||||
group_flags, door_map = find_bk_groups(name, available_sectors, proposed_map, bk_special)
|
||||
bk_flag = False if world.bigkeyshuffle[player] and not bk_special else bk_needed
|
||||
|
||||
def exception(d):
|
||||
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
||||
original_state = extend_reachable_state_improved(entrance_regions, start, proposed_map, all_regions,
|
||||
valid_doors, bk_needed, world, player, exception)
|
||||
valid_doors, bk_flag, world, player, exception)
|
||||
dungeon['Origin'] = create_graph_piece_from_state(None, original_state, original_state, proposed_map, exception)
|
||||
either_crystal = True # if all hooks from the origin are either, explore all bits with either
|
||||
for hook, crystal in dungeon['Origin'].hooks.items():
|
||||
@@ -206,7 +226,7 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, all_regions, pro
|
||||
o_state_cache = {}
|
||||
for sector in available_sectors:
|
||||
for door in sector.outstanding_doors:
|
||||
if not door.stonewall and door not in proposed_map.keys():
|
||||
if door not in proposed_map.keys():
|
||||
hanger_set.add(door)
|
||||
bk_flag = group_flags[door_map[door]]
|
||||
parent = door.entrance.parent_region
|
||||
@@ -385,14 +405,15 @@ def filter_choices(next_hanger, door, orig_hang, prev_choices, hook_candidates):
|
||||
return next_hanger != door and orig_hang != next_hanger and door not in hook_candidates
|
||||
|
||||
|
||||
def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||
bk_needed, paths, entrance_regions):
|
||||
def check_valid(name, dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||
bk_needed, bk_special, paths, entrance_regions, world, player):
|
||||
# evaluate if everything is still plausible
|
||||
|
||||
# only origin is left in the dungeon and not everything is connected
|
||||
if len(dungeon.keys()) <= 1 and len(proposed_map.keys()) < len(doors_to_connect):
|
||||
return False
|
||||
# origin has no more hooks, but not all doors have been proposed
|
||||
if not world.bigkeyshuffle[player]:
|
||||
possible_bks = len(dungeon['Origin'].possible_bk_locations)
|
||||
true_origin_hooks = [x for x in dungeon['Origin'].hooks.keys() if not x.bigKey or possible_bks > 0 or not bk_needed]
|
||||
if len(true_origin_hooks) == 0 and len(proposed_map.keys()) < len(doors_to_connect):
|
||||
@@ -425,7 +446,7 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
||||
if len(outstanding_doors[key]) > 0 and len(hangers[key]) == 0 and len(hooks[opp_key]) == 0:
|
||||
return False
|
||||
all_visited = set()
|
||||
bk_possible = not bk_needed
|
||||
bk_possible = not bk_needed or (world.bigkeyshuffle[player] and not bk_special)
|
||||
for piece in dungeon.values():
|
||||
all_visited.update(piece.visited_regions)
|
||||
if not bk_possible and len(piece.possible_bk_locations) > 0:
|
||||
@@ -434,7 +455,8 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
||||
return False
|
||||
if not bk_possible:
|
||||
return False
|
||||
if not valid_paths(paths, entrance_regions, doors_to_connect, all_regions, proposed_map):
|
||||
if not valid_paths(name, paths, entrance_regions, doors_to_connect, all_regions, proposed_map,
|
||||
bk_needed, bk_special, world, player):
|
||||
return False
|
||||
new_hangers_found = True
|
||||
accessible_hook_types = []
|
||||
@@ -463,7 +485,8 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
||||
return len(all_hangers.difference(hanger_matching)) == 0
|
||||
|
||||
|
||||
def valid_paths(paths, entrance_regions, valid_doors, all_regions, proposed_map):
|
||||
def valid_paths(name, paths, entrance_regions, valid_doors, all_regions, proposed_map,
|
||||
bk_needed, bk_special, world, player):
|
||||
for path in paths:
|
||||
if type(path) is tuple:
|
||||
target = path[1]
|
||||
@@ -475,76 +498,84 @@ def valid_paths(paths, entrance_regions, valid_doors, all_regions, proposed_map)
|
||||
else:
|
||||
target = path
|
||||
start_regions = entrance_regions
|
||||
if not valid_path(start_regions, target, valid_doors, proposed_map):
|
||||
if not valid_path(name, start_regions, target, valid_doors, proposed_map, all_regions,
|
||||
bk_needed, bk_special, world, player):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def valid_path(starting_regions, target, valid_doors, proposed_map):
|
||||
queue = deque(starting_regions)
|
||||
visited = set(starting_regions)
|
||||
while len(queue) > 0:
|
||||
region = queue.popleft()
|
||||
if region.name == target:
|
||||
return True
|
||||
for ext in region.exits:
|
||||
connect = ext.connected_region
|
||||
if connect is None and ext.name in valid_doors:
|
||||
door = valid_doors[ext.name]
|
||||
if not door.blocked:
|
||||
if door in proposed_map:
|
||||
new_region = proposed_map[door].entrance.parent_region
|
||||
if new_region not in visited:
|
||||
visited.add(new_region)
|
||||
queue.append(new_region)
|
||||
def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_regions,
|
||||
bk_needed, bk_special, world, player):
|
||||
target_regions = set()
|
||||
if type(target) is not list:
|
||||
for region in all_regions:
|
||||
if target == region.name:
|
||||
target_regions.add(region)
|
||||
break
|
||||
else:
|
||||
for region in all_regions:
|
||||
if region.name in target:
|
||||
target_regions.add(region)
|
||||
|
||||
start = ExplorationState(dungeon=name)
|
||||
start.big_key_special = bk_special
|
||||
bk_flag = False if world.bigkeyshuffle[player] and not bk_special else bk_needed
|
||||
|
||||
def exception(d):
|
||||
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
||||
original_state = extend_reachable_state_improved(starting_regions, start, proposed_map, all_regions,
|
||||
valid_doors, bk_flag, world, player, exception)
|
||||
|
||||
for exp_door in original_state.unattached_doors:
|
||||
if not exp_door.door.blocked:
|
||||
return True # outstanding connection possible
|
||||
elif connect is not None:
|
||||
door = ext.door
|
||||
if door is not None and not door.blocked and connect not in visited:
|
||||
visited.add(connect)
|
||||
queue.append(connect)
|
||||
for target in target_regions:
|
||||
if original_state.visited_at_all(target):
|
||||
return True
|
||||
return False # couldn't find an outstanding door or the target
|
||||
|
||||
|
||||
def determine_required_paths(world, player, split_dungeon=False, all_regions=None, name=None):
|
||||
if all_regions is None:
|
||||
all_regions = set()
|
||||
paths = {
|
||||
'Hyrule Castle': ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby'],
|
||||
'Eastern Palace': ['Eastern Boss'],
|
||||
'Desert Palace': ['Desert Main Lobby', 'Desert East Lobby', 'Desert West Lobby', 'Desert Boss'],
|
||||
'Tower of Hera': ['Hera Boss'],
|
||||
'Agahnims Tower': ['Tower Agahnim 1'],
|
||||
'Palace of Darkness': ['PoD Boss'],
|
||||
'Swamp Palace': ['Swamp Boss'],
|
||||
'Skull Woods': ['Skull 1 Lobby', 'Skull 2 East Lobby', 'Skull 2 West Lobby', 'Skull Boss'],
|
||||
'Thieves Town': ['Thieves Boss', ('Thieves Blind\'s Cell', 'Thieves Boss')],
|
||||
'Ice Palace': ['Ice Boss'],
|
||||
'Misery Mire': ['Mire Boss'],
|
||||
'Turtle Rock': ['TR Main Lobby', 'TR Lazy Eyes', 'TR Big Chest Entrance', 'TR Eye Bridge', 'TR Boss'],
|
||||
'Ganons Tower': ['GT Agahnim 2'],
|
||||
'Skull Woods 1': ['Skull 1 Lobby'],
|
||||
'Skull Woods 2': ['Skull 2 East Lobby', 'Skull 2 West Lobby'],
|
||||
'Skull Woods 3': [],
|
||||
'Desert Palace Main': ['Desert Main Lobby', 'Desert East Lobby', 'Desert West Lobby'],
|
||||
'Desert Palace Back': []
|
||||
}
|
||||
if world.mode[player] == 'standard':
|
||||
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
||||
# noinspection PyTypeChecker
|
||||
paths['Hyrule Castle'].append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
||||
if world.doorShuffle[player] in ['basic']:
|
||||
paths['Thieves Town'].append('Thieves Attic Window')
|
||||
if split_dungeon:
|
||||
if world.get_region('Desert Boss', player) in all_regions:
|
||||
paths[name].append('Desert Boss')
|
||||
if world.get_region('Skull Boss', player) in all_regions:
|
||||
paths[name].append('Skull Boss')
|
||||
def determine_required_paths(world, player):
|
||||
paths = {}
|
||||
for name, builder in world.dungeon_layouts[player].items():
|
||||
all_regions = builder.master_sector.regions
|
||||
paths[name] = determine_paths_for_dungeon(world, player, all_regions, name)
|
||||
return paths
|
||||
|
||||
|
||||
boss_path_checks = ['Eastern Boss', 'Desert Boss', 'Hera Boss', 'Tower Agahnim 1', 'PoD Boss', 'Swamp Boss',
|
||||
'Skull Boss', 'Ice Boss', 'Mire Boss', 'TR Boss', 'GT Agahnim 2']
|
||||
|
||||
# pinball is allowed to orphan you
|
||||
drop_path_checks = ['Skull Pot Circle', 'Skull Left Drop', 'Skull Back Drop', 'Sewers Rat Path']
|
||||
|
||||
|
||||
def determine_paths_for_dungeon(world, player, all_regions, name):
|
||||
all_r_names = set(x.name for x in all_regions)
|
||||
paths = []
|
||||
non_hole_portals = []
|
||||
for portal in world.dungeon_portals[player]:
|
||||
if portal.door.entrance.parent_region in all_regions:
|
||||
non_hole_portals.append(portal.door.entrance.parent_region.name)
|
||||
if portal.destination:
|
||||
paths.append(portal.door.entrance.parent_region.name)
|
||||
if world.mode[player] == 'standard' and name == 'Hyrule Castle':
|
||||
paths.append('Hyrule Dungeon Cellblock')
|
||||
paths.append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
||||
if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town':
|
||||
paths.append('Thieves Attic Window')
|
||||
elif 'Thieves Attic Window' in all_r_names:
|
||||
paths.append('Thieves Attic Window')
|
||||
for boss in boss_path_checks:
|
||||
if boss in all_r_names:
|
||||
paths.append(boss)
|
||||
if 'Thieves Boss' in all_r_names:
|
||||
paths.append('Thieves Boss')
|
||||
paths.append(('Thieves Blind\'s Cell', 'Thieves Boss'))
|
||||
for drop_check in drop_path_checks:
|
||||
if drop_check in all_r_names:
|
||||
paths.append((drop_check, non_hole_portals))
|
||||
return paths
|
||||
|
||||
|
||||
def winnow_hangers(hangers, hooks):
|
||||
@@ -588,7 +619,7 @@ def stonewall_valid(stonewall):
|
||||
return False # you can get stuck from an entrance
|
||||
else:
|
||||
door = entrance.door
|
||||
if door is not None and door != stonewall and not door.blocked and parent not in visited:
|
||||
if (door is None or (door != stonewall and not door.blocked)) and parent not in visited:
|
||||
visited.add(parent)
|
||||
queue.append(parent)
|
||||
# we didn't find anything bad
|
||||
@@ -636,7 +667,7 @@ def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exceptio
|
||||
def filter_for_potential_bk_locations(locations):
|
||||
return [x for x in locations if
|
||||
'- Big Chest' not in x.name and '- Prize' not in x.name and x.name not in dungeon_events
|
||||
and x.name not in key_only_locations.keys() and x.name not in ['Agahnim 1', 'Agahnim 2']]
|
||||
and not x.forced_item and x.name not in ['Agahnim 1', 'Agahnim 2']]
|
||||
|
||||
|
||||
type_map = {
|
||||
@@ -738,6 +769,9 @@ def connect_simple_door(exit_door, region):
|
||||
exit_door.dest = region
|
||||
|
||||
|
||||
special_big_key_doors = ['Hyrule Dungeon Cellblock Door', "Thieves Blind's Cell Door"]
|
||||
|
||||
|
||||
class ExplorationState(object):
|
||||
|
||||
def __init__(self, init_crystal=CrystalBarrier.Orange, dungeon=None):
|
||||
@@ -819,7 +853,7 @@ class ExplorationState(object):
|
||||
if region.type == RegionType.Dungeon:
|
||||
for location in region.locations:
|
||||
if key_checks and location not in self.found_locations:
|
||||
if location.name in key_only_locations and 'Small Key' in location.item.name:
|
||||
if location.forced_item and 'Small Key' in location.item.name:
|
||||
self.key_locations += 1
|
||||
if location.name not in dungeon_events and '- Prize' not in location.name and location.name not in ['Agahnim 1', 'Agahnim 2']:
|
||||
self.ttl_locations += 1
|
||||
@@ -833,10 +867,6 @@ class ExplorationState(object):
|
||||
if location.name in flooded_keys_reverse.keys() and self.location_found(
|
||||
flooded_keys_reverse[location.name]):
|
||||
self.perform_event(flooded_keys_reverse[location.name], key_region)
|
||||
if key_checks and region.name == 'Hyrule Dungeon Cellblock' and not self.big_key_opened:
|
||||
self.big_key_opened = True
|
||||
self.avail_doors.extend(self.big_doors)
|
||||
self.big_doors.clear()
|
||||
|
||||
def flooded_key_check(self, location):
|
||||
if location.name not in flooded_keys.keys():
|
||||
@@ -934,7 +964,7 @@ class ExplorationState(object):
|
||||
if door in key_door_proposal and door not in self.opened_doors:
|
||||
if not self.in_door_list(door, self.small_doors):
|
||||
self.append_door_to_list(door, self.small_doors)
|
||||
elif door.bigKey and not self.big_key_opened:
|
||||
elif (door.bigKey or door.name in special_big_key_doors) and not self.big_key_opened:
|
||||
if not self.in_door_list(door, self.big_doors):
|
||||
self.append_door_to_list(door, self.big_doors)
|
||||
elif door.req_event is not None and door.req_event not in self.events:
|
||||
@@ -955,6 +985,12 @@ class ExplorationState(object):
|
||||
def visited_at_all(self, region):
|
||||
return region in self.visited_blue or region in self.visited_orange
|
||||
|
||||
def found_forced_bk(self):
|
||||
for location in self.found_locations:
|
||||
if location.forced_big_key():
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_traverse(self, door, exception=None):
|
||||
if door.blocked:
|
||||
return exception(door) if exception else False
|
||||
@@ -962,18 +998,10 @@ class ExplorationState(object):
|
||||
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
||||
return True
|
||||
|
||||
def can_traverse_bk_check(self, door, isOrigin):
|
||||
if door.blocked:
|
||||
return False
|
||||
if door.crystal not in [CrystalBarrier.Null, CrystalBarrier.Either]:
|
||||
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
||||
return not isOrigin or not door.bigKey or self.count_locations_exclude_specials() > 0
|
||||
# return not door.bigKey or len([x for x in self.found_locations if '- Prize' not in x.name]) > 0
|
||||
|
||||
def count_locations_exclude_specials(self):
|
||||
cnt = 0
|
||||
for loc in self.found_locations:
|
||||
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events and loc.name not in key_only_locations.keys():
|
||||
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events and not loc.forced_item:
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
@@ -1039,7 +1067,7 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, all_reg
|
||||
explorable_door = local_state.next_avail_door()
|
||||
if explorable_door.door.bigKey:
|
||||
if bk_flag:
|
||||
big_not_found = not special_big_key_found(local_state, world, player) if local_state.big_key_special else local_state.count_locations_exclude_specials() == 0
|
||||
big_not_found = not special_big_key_found(local_state) if local_state.big_key_special else local_state.count_locations_exclude_specials() == 0
|
||||
if big_not_found:
|
||||
continue # we can't open this door
|
||||
if explorable_door.door in proposed_map:
|
||||
@@ -1055,22 +1083,28 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, all_reg
|
||||
return local_state
|
||||
|
||||
|
||||
def special_big_key_found(state, world, player):
|
||||
cellblock = world.get_region('Hyrule Dungeon Cellblock', player)
|
||||
return state.visited(cellblock)
|
||||
def special_big_key_found(state):
|
||||
for location in state.found_locations:
|
||||
if location.forced_item and location.forced_item.bigkey:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def valid_region_to_explore_in_regions(region, all_regions, world, player):
|
||||
if region is None:
|
||||
return False
|
||||
return (region.type == RegionType.Dungeon and region in all_regions) or region.name in world.inaccessible_regions[player]
|
||||
return (region.type == RegionType.Dungeon and region in all_regions)\
|
||||
or region.name in world.inaccessible_regions[player]\
|
||||
or (region.name == 'Hyrule Castle Ledge' and world.mode[player] == 'standard')
|
||||
|
||||
|
||||
# cross-utility methods
|
||||
def valid_region_to_explore(region, name, world, player):
|
||||
if region is None:
|
||||
return False
|
||||
return (region.type == RegionType.Dungeon and region.dungeon.name in name) or region.name in world.inaccessible_regions[player]
|
||||
return (region.type == RegionType.Dungeon and region.dungeon.name in name)\
|
||||
or region.name in world.inaccessible_regions[player]\
|
||||
or (region.name == 'Hyrule Castle Ledge' and world.mode[player] == 'standard')
|
||||
|
||||
|
||||
def get_doors(world, region, player):
|
||||
@@ -1146,9 +1180,7 @@ class DungeonBuilder(object):
|
||||
self.key_door_proposal = None
|
||||
|
||||
self.allowance = None
|
||||
if name in dungeon_dead_end_allowance.keys():
|
||||
self.allowance = dungeon_dead_end_allowance[name]
|
||||
elif 'Stonewall' in name:
|
||||
if 'Stonewall' in name:
|
||||
self.allowance = 1
|
||||
elif 'Prewall' in name:
|
||||
orig_name = name[:-8]
|
||||
@@ -1232,11 +1264,27 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
||||
sector = find_sector(r_name, all_sectors)
|
||||
reverse_d_map[sector] = key
|
||||
|
||||
complete_dungeons = {x: y for x, y in dungeon_map.items() if sum(len(sector.outstanding_doors) for sector in y.sectors) <= 0}
|
||||
[dungeon_map.pop(key) for key in complete_dungeons.keys()]
|
||||
|
||||
# categorize sectors
|
||||
identify_destination_sectors(accessible_sectors, reverse_d_map, dungeon_map, connections,
|
||||
dungeon_entrances, split_dungeon_entrances)
|
||||
for name, builder in dungeon_map.items():
|
||||
calc_allowance_and_dead_ends(builder, connections_tuple)
|
||||
calc_allowance_and_dead_ends(builder, connections_tuple, world, player)
|
||||
|
||||
if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']:
|
||||
sanc = find_sector('Sanctuary', candidate_sectors)
|
||||
if sanc: # only run if sanc if a candidate
|
||||
lw_builders = []
|
||||
for name, portal_list in dungeon_portals.items():
|
||||
for portal_name in portal_list:
|
||||
if world.get_portal(portal_name, player).light_world:
|
||||
lw_builders.append(dungeon_map[name])
|
||||
break
|
||||
# portals only - not drops for mirror stuff
|
||||
sanc_builder = random.choice(lw_builders)
|
||||
assign_sector(sanc, sanc_builder, candidate_sectors, global_pole)
|
||||
|
||||
free_location_sectors = {}
|
||||
crystal_switches = {}
|
||||
@@ -1270,10 +1318,11 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
||||
# restart
|
||||
raise NeutralizingException('Either free location/crystal assignment is already globally invalid')
|
||||
logger.info(world.fish.translate("cli", "cli", "balance.doors"))
|
||||
builder_info = dungeon_entrances, split_dungeon_entrances, world, player
|
||||
builder_info = dungeon_entrances, split_dungeon_entrances, connections_tuple, world, player
|
||||
assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, builder_info)
|
||||
# the rest
|
||||
assign_the_rest(dungeon_map, neutral_sectors, global_pole, builder_info)
|
||||
dungeon_map.update(complete_dungeons)
|
||||
finished = True
|
||||
except NeutralizingException:
|
||||
pass
|
||||
@@ -1327,15 +1376,21 @@ def identify_destination_sectors(accessible_sectors, reverse_d_map, dungeon_map,
|
||||
break
|
||||
|
||||
|
||||
def calc_allowance_and_dead_ends(builder, connections_tuple):
|
||||
# todo: split version that adds allowance for potential entrances
|
||||
def calc_allowance_and_dead_ends(builder, connections_tuple, world, player):
|
||||
portals = world.dungeon_portals[player]
|
||||
entrances_map, potentials, connections = connections_tuple
|
||||
needed_connections = [x for x in builder.all_entrances if x not in entrances_map[builder.name]]
|
||||
name = builder.name if not builder.split_flag else builder.name.rsplit(' ', 1)[0]
|
||||
needed_connections = [x for x in builder.all_entrances if x not in entrances_map[name]]
|
||||
starting_allowance = 0
|
||||
used_sectors = set()
|
||||
for entrance in entrances_map[builder.name]:
|
||||
destination_entrances = [x.door.entrance.parent_region.name for x in portals if x.destination]
|
||||
dead_ends = [x.door.entrance.parent_region.name for x in portals if x.deadEnd]
|
||||
for entrance in entrances_map[name]:
|
||||
sector = find_sector(entrance, builder.sectors)
|
||||
if sector:
|
||||
outflow_target = 0 if entrance not in drop_entrances_allowance else 1
|
||||
if sector not in used_sectors and sector.adj_outflow() > outflow_target:
|
||||
if sector not in used_sectors and (sector.adj_outflow() > outflow_target or entrance in dead_ends):
|
||||
if entrance not in destination_entrances:
|
||||
starting_allowance += 1
|
||||
else:
|
||||
@@ -1349,12 +1404,16 @@ def calc_allowance_and_dead_ends(builder, connections_tuple):
|
||||
builder.allowance = starting_allowance
|
||||
for entrance in needed_connections:
|
||||
sector = find_sector(entrance, builder.sectors)
|
||||
if sector not in used_sectors: # ignore things on same sector
|
||||
if sector and sector not in used_sectors: # ignore things on same sector
|
||||
is_destination = entrance in destination_entrances
|
||||
connect_able = False
|
||||
if entrance in connections.keys():
|
||||
enabling_region = connections[entrance]
|
||||
connecting_entrances = [x for x in potentials[enabling_region] if x != entrance and x not in dead_entrances and x not in drop_entrances_allowance]
|
||||
check_list = list(potentials[enabling_region])
|
||||
if enabling_region.name in ['Desert Ledge', 'Desert Palace Entrance (North) Spot']:
|
||||
alternate = 'Desert Palace Entrance (North) Spot' if enabling_region.name == 'Desert Ledge' else 'Desert Ledge'
|
||||
check_list.extend(potentials[world.get_region(alternate, player)])
|
||||
connecting_entrances = [x for x in check_list if x != entrance and x not in dead_entrances and x not in drop_entrances_allowance]
|
||||
connect_able = len(connecting_entrances) > 0
|
||||
if is_destination and sector.branches() == 0: #
|
||||
builder.dead_ends += 1
|
||||
@@ -1367,21 +1426,19 @@ def calc_allowance_and_dead_ends(builder, connections_tuple):
|
||||
|
||||
def define_sector_features(sectors):
|
||||
for sector in sectors:
|
||||
if 'Hyrule Dungeon Cellblock' in sector.region_set():
|
||||
sector.bk_provided = True
|
||||
if 'Thieves Blind\'s Cell' in sector.region_set():
|
||||
sector.bk_required = True
|
||||
for region in sector.regions:
|
||||
for loc in region.locations:
|
||||
if '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2', 'Hyrule Castle - Big Key Drop']:
|
||||
if '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']:
|
||||
pass
|
||||
elif loc.event and 'Small Key' in loc.item.name:
|
||||
elif loc.forced_item and 'Small Key' in loc.item.name:
|
||||
sector.key_only_locations += 1
|
||||
elif loc.name not in dungeon_events:
|
||||
elif loc.forced_item and loc.forced_item.bigkey:
|
||||
sector.bk_provided = True
|
||||
elif loc.name not in dungeon_events and not loc.forced_item:
|
||||
sector.chest_locations += 1
|
||||
if '- Big Chest' in loc.name:
|
||||
if '- Big Chest' in loc.name or loc.name in ["Hyrule Castle - Zelda's Chest",
|
||||
"Thieves' Town - Blind's Cell"]:
|
||||
sector.bk_required = True
|
||||
sector.big_chest_present = True
|
||||
for ext in region.exits:
|
||||
door = ext.door
|
||||
if door is not None:
|
||||
@@ -1518,6 +1575,8 @@ def assign_crystal_switch_sectors(dungeon_map, crystal_switches, crystal_barrier
|
||||
valid = global_pole.is_valid_choice(dungeon_map, builder_choice, test_set)
|
||||
assign_sector(switch_choice, builder_choice, crystal_switches, global_pole)
|
||||
return crystal_switches
|
||||
if len(crystal_switches) == 0:
|
||||
raise GenerationException('No crystal switches to assign')
|
||||
sector_list = list(crystal_switches)
|
||||
choices = random.sample(sector_list, k=len(population))
|
||||
for i, choice in enumerate(choices):
|
||||
@@ -1775,16 +1834,6 @@ def loop_present(hook, opp, h_mag, other_mag):
|
||||
return h_mag[opp] >= h_mag[hook.value] - other_mag[opp]
|
||||
|
||||
|
||||
def is_entrance_sector(builder, sector):
|
||||
if builder is None:
|
||||
return False
|
||||
for entrance in builder.all_entrances:
|
||||
r_set = sector.region_set()
|
||||
if entrance in r_set:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def is_satisfied(door_dict_list):
|
||||
for door_dict in door_dict_list:
|
||||
for door_list in door_dict.values():
|
||||
@@ -2580,6 +2629,8 @@ def categorize_groupings(sectors):
|
||||
|
||||
|
||||
def valid_assignment(builder, sector_list, builder_info):
|
||||
if not valid_entrance(builder, sector_list, builder_info):
|
||||
return False
|
||||
if not valid_c_switch(builder, sector_list):
|
||||
return False
|
||||
if not valid_polarized_assignment(builder, sector_list):
|
||||
@@ -2588,6 +2639,34 @@ def valid_assignment(builder, sector_list, builder_info):
|
||||
return resolved
|
||||
|
||||
|
||||
def valid_entrance(builder, sector_list, builder_info):
|
||||
is_dead_end = False
|
||||
if len(builder.sectors) == 0:
|
||||
is_dead_end = True
|
||||
else:
|
||||
entrances, splits, c_tuple, world, player = builder_info
|
||||
if builder.name not in entrances.keys():
|
||||
name_parts = builder.name.rsplit(' ', 1)
|
||||
entrance_list = splits[name_parts[0]][name_parts[1]]
|
||||
entrances = []
|
||||
for sector in builder.sectors:
|
||||
if sector.is_entrance_sector():
|
||||
sector.region_set()
|
||||
entrances.append(sector)
|
||||
all_dead = True
|
||||
for sector in entrances:
|
||||
for region in entrance_list:
|
||||
if region in sector.region_set():
|
||||
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == region), None)
|
||||
if portal and not portal.deadEnd:
|
||||
all_dead = False
|
||||
break
|
||||
if not all_dead:
|
||||
break
|
||||
is_dead_end = all_dead
|
||||
return len(sector_list) == 0 if is_dead_end else True
|
||||
|
||||
|
||||
def valid_c_switch(builder, sector_list):
|
||||
if builder.c_switch_present:
|
||||
return True
|
||||
@@ -2604,7 +2683,11 @@ def valid_c_switch(builder, sector_list):
|
||||
|
||||
def valid_connected_assignment(builder, sector_list):
|
||||
full_list = sector_list + builder.sectors
|
||||
if len(full_list) == 1 and sum_magnitude(full_list) == [0, 0, 0]:
|
||||
return True
|
||||
for sector in full_list:
|
||||
if sector.is_entrance_sector():
|
||||
continue
|
||||
others = [x for x in full_list if x != sector]
|
||||
other_mag = sum_magnitude(others)
|
||||
sector_mag = sector.magnitude()
|
||||
@@ -2678,17 +2761,40 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
||||
builder.split_dungeon_map[name].valid_proposal = proposal
|
||||
return builder.split_dungeon_map # we made this earlier in gen, just use it
|
||||
|
||||
attempts, comb_w_replace = 0, None
|
||||
attempts, comb_w_replace, merge_attempt, merge_limit = 0, None, 0, len(split_list) - 1
|
||||
while attempts < 5: # does not solve coin flips 3% of the time
|
||||
try:
|
||||
candidate_sectors = dict.fromkeys(builder.sectors)
|
||||
global_pole = GlobalPolarity(candidate_sectors)
|
||||
|
||||
dungeon_map = {}
|
||||
dungeon_map, sub_builder, merge_keys = {}, None, []
|
||||
if merge_attempt > 0:
|
||||
candidates = []
|
||||
for name, split_entrances in split_list.items():
|
||||
if len(split_entrances) > 1:
|
||||
candidates.append(name)
|
||||
continue
|
||||
elif len(split_entrances) <= 0:
|
||||
continue
|
||||
ents, splits, c_tuple, world, player = builder_info
|
||||
r_name = split_entrances[0]
|
||||
p = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == r_name), None)
|
||||
if p and not p.deadEnd:
|
||||
candidates.append(name)
|
||||
merge_keys = random.sample(candidates, merge_attempt+1) if len(candidates) >= merge_attempt+1 else []
|
||||
for name, split_entrances in split_list.items():
|
||||
key = builder.name + ' ' + name
|
||||
if merge_keys and name in merge_keys:
|
||||
other_keys = [builder.name + ' ' + x for x in merge_keys if x != name]
|
||||
other_key = next((x for x in other_keys if x in dungeon_map), None)
|
||||
if other_key:
|
||||
key = other_key
|
||||
sub_builder = dungeon_map[other_key]
|
||||
sub_builder.all_entrances.extend(split_entrances)
|
||||
if key not in dungeon_map:
|
||||
dungeon_map[key] = sub_builder = DungeonBuilder(key)
|
||||
sub_builder.all_entrances = split_entrances
|
||||
sub_builder.split_flag = True
|
||||
sub_builder.all_entrances = list(split_entrances)
|
||||
for r_name in split_entrances:
|
||||
assign_sector(find_sector(r_name, candidate_sectors), sub_builder, candidate_sectors, global_pole)
|
||||
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
||||
@@ -2698,10 +2804,16 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
||||
attempts += 5 # all the combinations were tried already, no use repeating
|
||||
else:
|
||||
attempts += 1
|
||||
if attempts >= 5 and merge_attempt < merge_limit:
|
||||
merge_attempt, attempts = merge_attempt + 1, 0
|
||||
|
||||
raise GenerationException('Unable to resolve in 5 attempts')
|
||||
|
||||
|
||||
def balance_split(candidate_sectors, dungeon_map, global_pole, builder_info):
|
||||
dungeon_entrances, split_dungeon_entrances, connections_tuple, world, player = builder_info
|
||||
for name, builder in dungeon_map.items():
|
||||
calc_allowance_and_dead_ends(builder, connections_tuple, world, player)
|
||||
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
||||
if comb_w_replace <= 10000:
|
||||
combinations = list(itertools.product(dungeon_map.keys(), repeat=len(candidate_sectors)))
|
||||
@@ -2714,8 +2826,8 @@ def balance_split(candidate_sectors, dungeon_map, global_pole, builder_info):
|
||||
for i, choice in enumerate(choices):
|
||||
chosen_sectors[choice].append(main_sector_list[i])
|
||||
all_valid = True
|
||||
for name, sector_list in chosen_sectors.items():
|
||||
if not valid_assignment(dungeon_map[name], sector_list, builder_info):
|
||||
for name, builder in dungeon_map.items():
|
||||
if not valid_assignment(builder, chosen_sectors[name], builder_info):
|
||||
all_valid = False
|
||||
break
|
||||
if all_valid:
|
||||
@@ -2831,6 +2943,8 @@ def check_for_forced_crystal_single(builder, candidate_sectors):
|
||||
for sector in builder.sectors:
|
||||
for door in sector.outstanding_doors:
|
||||
builder_doors[hook_from_door(door)][door] = sector
|
||||
if len(builder_doors) == 0:
|
||||
return False
|
||||
candidate_doors = defaultdict(dict)
|
||||
for sector in candidate_sectors:
|
||||
for door in sector.outstanding_doors:
|
||||
@@ -2838,6 +2952,7 @@ def check_for_forced_crystal_single(builder, candidate_sectors):
|
||||
for hook in builder_doors.keys():
|
||||
for door in builder_doors[hook].keys():
|
||||
opp = opposite_h_type(hook)
|
||||
if opp in builder_doors.keys():
|
||||
for d, sector in builder_doors[opp].items():
|
||||
if d != door and (not sector.blue_barrier or sector.c_switch):
|
||||
return False
|
||||
@@ -3118,21 +3233,33 @@ def identify_branching_issues(dungeon_map, builder_info):
|
||||
|
||||
|
||||
def check_for_valid_layout(builder, sector_list, builder_info):
|
||||
dungeon_entrances, split_dungeon_entrances, world, player = builder_info
|
||||
dungeon_entrances, split_dungeon_entrances, c_tuple, world, player = builder_info
|
||||
if builder.name in split_dungeon_entrances.keys():
|
||||
try:
|
||||
temp_builder = DungeonBuilder(builder.name)
|
||||
for s in sector_list + builder.sectors:
|
||||
assign_sector_helper(s, temp_builder)
|
||||
split_list = split_region_starts[builder.name]
|
||||
split_list = split_dungeon_entrances[builder.name]
|
||||
builder.split_dungeon_map = split_dungeon_builder(temp_builder, split_list, builder_info)
|
||||
builder.valid_proposal = {}
|
||||
possible_regions = set()
|
||||
for portal in world.dungeon_portals[player]:
|
||||
if not portal.destination and portal.name in dungeon_portals[builder.name]:
|
||||
possible_regions.add(portal.door.entrance.parent_region.name)
|
||||
if builder.name in dungeon_drops.keys():
|
||||
possible_regions.update(dungeon_drops[builder.name])
|
||||
for name, split_build in builder.split_dungeon_map.items():
|
||||
name_bits = name.split(" ")
|
||||
orig_name = " ".join(name_bits[:-1])
|
||||
entrance_regions = split_dungeon_entrances[orig_name][name_bits[-1]]
|
||||
# todo: this is hardcoded information for random entrances
|
||||
entrance_regions = [x for x in entrance_regions if x not in split_check_entrance_invalid]
|
||||
for sector in split_build.sectors:
|
||||
match_set = set(sector.region_set()).intersection(possible_regions)
|
||||
if len(match_set) > 0:
|
||||
for r_name in match_set:
|
||||
if r_name not in entrance_regions:
|
||||
entrance_regions.append(r_name)
|
||||
# entrance_regions = [x for x in entrance_regions if x not in split_check_entrance_invalid]
|
||||
proposal = generate_dungeon_find_proposal(split_build, entrance_regions, True, world, player)
|
||||
# record split proposals
|
||||
builder.valid_proposal[name] = proposal
|
||||
@@ -3150,7 +3277,7 @@ def check_for_valid_layout(builder, sector_list, builder_info):
|
||||
|
||||
def resolve_equations(builder, sector_list):
|
||||
unreached_doors = defaultdict(list)
|
||||
equations = copy_door_equations(builder, sector_list)
|
||||
equations = {x: y for x, y in copy_door_equations(builder, sector_list).items() if len(y) > 0}
|
||||
current_access = {}
|
||||
sector_split = {} # those sectors that belong to a certain sector
|
||||
if builder.name in split_region_starts.keys():
|
||||
@@ -3252,7 +3379,7 @@ def find_priority_equation(equations, access_id, current_access):
|
||||
if len(filtered_candidates) == 1:
|
||||
return filtered_candidates[0]
|
||||
|
||||
neutral_candidates = [x for x in filtered_candidates if x[0].neutral_profit() or x[0].neutral()]
|
||||
neutral_candidates = [x for x in filtered_candidates if (x[0].neutral_profit() or x[0].neutral()) and x[0].profit(current_access) == local_profit_map[x[2]]]
|
||||
if len(neutral_candidates) == 0:
|
||||
neutral_candidates = filtered_candidates
|
||||
if len(neutral_candidates) == 1:
|
||||
@@ -3531,7 +3658,7 @@ def copy_door_equations(builder, sector_list):
|
||||
|
||||
def calc_sector_equations(sector, builder):
|
||||
equations = []
|
||||
is_entrance = is_entrance_sector(builder, sector) and not sector.destination_entrance
|
||||
is_entrance = sector.is_entrance_sector() and not sector.destination_entrance
|
||||
if is_entrance:
|
||||
flagged_equations = []
|
||||
for door in sector.outstanding_doors:
|
||||
@@ -3693,6 +3820,22 @@ default_dungeon_entrances = {
|
||||
'Ganons Tower': ['GT Lobby']
|
||||
}
|
||||
|
||||
drop_entrances = {
|
||||
'Hyrule Castle': ['Sewers Rat Path'],
|
||||
'Eastern Palace': [],
|
||||
'Desert Palace': [],
|
||||
'Tower of Hera': [],
|
||||
'Agahnims Tower': [],
|
||||
'Palace of Darkness': [],
|
||||
'Swamp Palace': [],
|
||||
'Skull Woods': ['Skull Pinball', 'Skull Left Drop', 'Skull Pot Circle', 'Skull Back Drop'],
|
||||
'Thieves Town': [],
|
||||
'Ice Palace': [],
|
||||
'Misery Mire': [],
|
||||
'Turtle Rock': [],
|
||||
'Ganons Tower': []
|
||||
}
|
||||
|
||||
|
||||
# todo: calculate these for ER - the multi entrance dungeons anyway
|
||||
dungeon_dead_end_allowance = {
|
||||
@@ -3724,14 +3867,28 @@ dead_entrances = [
|
||||
'TR Big Chest Entrance'
|
||||
]
|
||||
|
||||
destination_entrances = [
|
||||
'Sanctuary', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Rat Path', 'Desert East Lobby',
|
||||
'Desert West Lobby', 'Skull Pinball', 'Skull Pot Circle', 'Skull Left Drop', 'Skull 2 West Lobby',
|
||||
'Skull Back Drop', 'TR Big Chest Entrance', 'TR Eye Bridge', 'TR Lazy Eyes'
|
||||
]
|
||||
|
||||
split_check_entrance_invalid = [
|
||||
'Desert East Lobby', 'Skull 2 West Lobby'
|
||||
]
|
||||
|
||||
dungeon_portals = {
|
||||
'Hyrule Castle': ['Hyrule Castle South', 'Hyrule Castle West', 'Hyrule Castle East', 'Sanctuary'],
|
||||
'Eastern Palace': ['Eastern'],
|
||||
'Desert Palace': ['Desert Back', 'Desert South', 'Desert West', 'Desert East'],
|
||||
'Tower of Hera': ['Hera'],
|
||||
'Agahnims Tower': ['Agahnims Tower'],
|
||||
'Palace of Darkness': ['Palace of Darkness'],
|
||||
'Swamp Palace': ['Swamp'],
|
||||
'Skull Woods': ['Skull 1', 'Skull 2 East', 'Skull 2 West', 'Skull 3'],
|
||||
'Thieves Town': ['Thieves Town'],
|
||||
'Ice Palace': ['Ice'],
|
||||
'Misery Mire': ['Mire'],
|
||||
'Turtle Rock': ['Turtle Rock Main', 'Turtle Rock Lazy Eyes', 'Turtle Rock Chest', 'Turtle Rock Eye Bridge'],
|
||||
'Ganons Tower': ['Ganons Tower']
|
||||
}
|
||||
|
||||
dungeon_drops = {
|
||||
'Hyrule Castle': ['Sewers Rat Path'],
|
||||
'Skull Woods': ['Skull Pot Circle', 'Skull Pinball', 'Skull Left Drop', 'Skull Back Drop'],
|
||||
}
|
||||
|
||||
|
||||
+42
-33
@@ -7,27 +7,28 @@ from Items import ItemFactory
|
||||
|
||||
|
||||
def create_dungeons(world, player):
|
||||
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
|
||||
dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro[player] else small_keys, dungeon_items, player)
|
||||
def make_dungeon(name, id, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
|
||||
dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro[player] else small_keys,
|
||||
dungeon_items, player, id)
|
||||
dungeon.boss = BossFactory(default_boss, player)
|
||||
for region in dungeon.regions:
|
||||
world.get_region(region, player).dungeon = dungeon
|
||||
dungeon.world = world
|
||||
return dungeon
|
||||
|
||||
ES = make_dungeon('Hyrule Castle', None, hyrule_castle_regions, None, [ItemFactory('Small Key (Escape)', player)], [ItemFactory('Map (Escape)', player)])
|
||||
EP = make_dungeon('Eastern Palace', 'Armos Knights', eastern_regions, ItemFactory('Big Key (Eastern Palace)', player), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], player))
|
||||
DP = make_dungeon('Desert Palace', 'Lanmolas', desert_regions, ItemFactory('Big Key (Desert Palace)', player), [ItemFactory('Small Key (Desert Palace)', player)], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
|
||||
ToH = make_dungeon('Tower of Hera', 'Moldorm', hera_regions, ItemFactory('Big Key (Tower of Hera)', player), [ItemFactory('Small Key (Tower of Hera)', player)], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
|
||||
PoD = make_dungeon('Palace of Darkness', 'Helmasaur King', pod_regions, ItemFactory('Big Key (Palace of Darkness)', player), ItemFactory(['Small Key (Palace of Darkness)'] * 6, player), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], player))
|
||||
TT = make_dungeon('Thieves Town', 'Blind', thieves_regions, ItemFactory('Big Key (Thieves Town)', player), [ItemFactory('Small Key (Thieves Town)', player)], ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'], player))
|
||||
SW = make_dungeon('Skull Woods', 'Mothula', skull_regions, ItemFactory('Big Key (Skull Woods)', player), ItemFactory(['Small Key (Skull Woods)'] * 3, player), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player))
|
||||
SP = make_dungeon('Swamp Palace', 'Arrghus', swamp_regions, ItemFactory('Big Key (Swamp Palace)', player), [ItemFactory('Small Key (Swamp Palace)', player)], ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], player))
|
||||
IP = make_dungeon('Ice Palace', 'Kholdstare', ice_regions, ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
|
||||
MM = make_dungeon('Misery Mire', 'Vitreous', mire_regions, ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
|
||||
TR = make_dungeon('Turtle Rock', 'Trinexx', tr_regions, ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
|
||||
AT = make_dungeon('Agahnims Tower', 'Agahnim', tower_regions, None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
|
||||
GT = make_dungeon('Ganons Tower', 'Agahnim2', gt_regions, ItemFactory('Big Key (Ganons Tower)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
|
||||
ES = make_dungeon('Hyrule Castle', 1, None, hyrule_castle_regions, None, [ItemFactory('Small Key (Escape)', player)], [ItemFactory('Map (Escape)', player)])
|
||||
EP = make_dungeon('Eastern Palace', 2, 'Armos Knights', eastern_regions, ItemFactory('Big Key (Eastern Palace)', player), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], player))
|
||||
DP = make_dungeon('Desert Palace', 3, 'Lanmolas', desert_regions, ItemFactory('Big Key (Desert Palace)', player), [ItemFactory('Small Key (Desert Palace)', player)], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
|
||||
ToH = make_dungeon('Tower of Hera', 10, 'Moldorm', hera_regions, ItemFactory('Big Key (Tower of Hera)', player), [ItemFactory('Small Key (Tower of Hera)', player)], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
|
||||
PoD = make_dungeon('Palace of Darkness', 6, 'Helmasaur King', pod_regions, ItemFactory('Big Key (Palace of Darkness)', player), ItemFactory(['Small Key (Palace of Darkness)'] * 6, player), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], player))
|
||||
TT = make_dungeon('Thieves Town', 11, 'Blind', thieves_regions, ItemFactory('Big Key (Thieves Town)', player), [ItemFactory('Small Key (Thieves Town)', player)], ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'], player))
|
||||
SW = make_dungeon('Skull Woods', 8, 'Mothula', skull_regions, ItemFactory('Big Key (Skull Woods)', player), ItemFactory(['Small Key (Skull Woods)'] * 3, player), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player))
|
||||
SP = make_dungeon('Swamp Palace', 5, 'Arrghus', swamp_regions, ItemFactory('Big Key (Swamp Palace)', player), [ItemFactory('Small Key (Swamp Palace)', player)], ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], player))
|
||||
IP = make_dungeon('Ice Palace', 9, 'Kholdstare', ice_regions, ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
|
||||
MM = make_dungeon('Misery Mire', 7, 'Vitreous', mire_regions, ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
|
||||
TR = make_dungeon('Turtle Rock', 12, 'Trinexx', tr_regions, ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
|
||||
AT = make_dungeon('Agahnims Tower', 4, 'Agahnim', tower_regions, None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
|
||||
GT = make_dungeon('Ganons Tower', 13, 'Agahnim2', gt_regions, ItemFactory('Big Key (Ganons Tower)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
|
||||
|
||||
GT.bosses['bottom'] = BossFactory('Armos Knights', player)
|
||||
GT.bosses['middle'] = BossFactory('Lanmolas', player)
|
||||
@@ -166,9 +167,11 @@ hyrule_castle_regions = [
|
||||
'Hyrule Dungeon Map Room', 'Hyrule Dungeon North Abyss', 'Hyrule Dungeon North Abyss Catwalk',
|
||||
'Hyrule Dungeon South Abyss', 'Hyrule Dungeon South Abyss Catwalk', 'Hyrule Dungeon Guardroom',
|
||||
'Hyrule Dungeon Armory Main', 'Hyrule Dungeon Armory Boomerang', 'Hyrule Dungeon Armory North Branch',
|
||||
'Hyrule Dungeon Staircase', 'Hyrule Dungeon Cellblock', 'Sewers Behind Tapestry', 'Sewers Rope Room',
|
||||
'Sewers Dark Cross', 'Sewers Water', 'Sewers Key Rat', 'Sewers Rat Path', 'Sewers Secret Room Blocked Path',
|
||||
'Sewers Secret Room', 'Sewers Yet More Rats', 'Sewers Pull Switch', 'Sanctuary'
|
||||
'Hyrule Dungeon Staircase', 'Hyrule Dungeon Cellblock', 'Hyrule Dungeon Cell', 'Sewers Behind Tapestry',
|
||||
'Sewers Rope Room', 'Sewers Dark Cross', 'Sewers Water', 'Sewers Key Rat', 'Sewers Rat Path',
|
||||
'Sewers Secret Room Blocked Path', 'Sewers Secret Room', 'Sewers Yet More Rats', 'Sewers Pull Switch', 'Sanctuary',
|
||||
'Sanctuary Portal', 'Hyrule Castle West Portal', 'Hyrule Castle South Portal', 'Hyrule Castle East Portal'
|
||||
|
||||
]
|
||||
|
||||
eastern_regions = [
|
||||
@@ -178,7 +181,7 @@ eastern_regions = [
|
||||
'Eastern Compass Room', 'Eastern Hint Tile', 'Eastern Hint Tile Blocked Path', 'Eastern Courtyard',
|
||||
'Eastern Fairies', 'Eastern Map Valley', 'Eastern Dark Square', 'Eastern Dark Pots', 'Eastern Big Key',
|
||||
'Eastern Darkness', 'Eastern Rupees', 'Eastern Attic Start', 'Eastern False Switches', 'Eastern Cannonball Hell',
|
||||
'Eastern Single Eyegore', 'Eastern Duo Eyegores', 'Eastern Boss'
|
||||
'Eastern Single Eyegore', 'Eastern Duo Eyegores', 'Eastern Boss', 'Eastern Portal'
|
||||
]
|
||||
|
||||
desert_regions = [
|
||||
@@ -187,20 +190,21 @@ desert_regions = [
|
||||
'Desert North Hall', 'Desert Map Room', 'Desert Sandworm Corner', 'Desert Bonk Torch', 'Desert Circle of Pots',
|
||||
'Desert Big Chest Room', 'Desert West Wing', 'Desert West Lobby', 'Desert Fairy Fountain', 'Desert Back Lobby',
|
||||
'Desert Tiles 1', 'Desert Bridge', 'Desert Four Statues', 'Desert Beamos Hall', 'Desert Tiles 2',
|
||||
'Desert Wall Slide', 'Desert Boss'
|
||||
'Desert Wall Slide', 'Desert Boss', 'Desert West Portal', 'Desert South Portal', 'Desert East Portal',
|
||||
'Desert Back Portal'
|
||||
]
|
||||
|
||||
hera_regions = [
|
||||
'Hera Lobby', 'Hera Basement Cage', 'Hera Tile Room', 'Hera Tridorm', 'Hera Torches', 'Hera Beetles',
|
||||
'Hera Startile Corner', 'Hera Startile Wide', 'Hera 4F', 'Hera Big Chest Landing', 'Hera 5F',
|
||||
'Hera Fairies', 'Hera Boss'
|
||||
'Hera Fairies', 'Hera Boss', 'Hera Portal'
|
||||
]
|
||||
|
||||
tower_regions = [
|
||||
'Tower Lobby', 'Tower Gold Knights', 'Tower Room 03', 'Tower Lone Statue', 'Tower Dark Maze', 'Tower Dark Chargers',
|
||||
'Tower Dual Statues', 'Tower Dark Pits', 'Tower Dark Archers', 'Tower Red Spears', 'Tower Red Guards',
|
||||
'Tower Circle of Pots', 'Tower Pacifist Run', 'Tower Push Statue', 'Tower Catwalk', 'Tower Antechamber',
|
||||
'Tower Altar', 'Tower Agahnim 1'
|
||||
'Tower Altar', 'Tower Agahnim 1', 'Agahnims Tower Portal'
|
||||
]
|
||||
|
||||
pod_regions = [
|
||||
@@ -210,7 +214,7 @@ pod_regions = [
|
||||
'PoD Stalfos Basement', 'PoD Basement Ledge', 'PoD Big Key Landing', 'PoD Falling Bridge',
|
||||
'PoD Falling Bridge Ledge', 'PoD Dark Maze', 'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement',
|
||||
'PoD Harmless Hellway', 'PoD Mimics 2', 'PoD Bow Statue', 'PoD Dark Pegs', 'PoD Lonely Turtle', 'PoD Turtle Party',
|
||||
'PoD Dark Alley', 'PoD Callback', 'PoD Boss'
|
||||
'PoD Dark Alley', 'PoD Callback', 'PoD Boss', 'Palace of Darkness Portal'
|
||||
]
|
||||
|
||||
swamp_regions = [
|
||||
@@ -222,7 +226,8 @@ swamp_regions = [
|
||||
'Swamp West Shallows', 'Swamp West Block Path', 'Swamp West Ledge', 'Swamp Barrier Ledge', 'Swamp Barrier',
|
||||
'Swamp Attic', 'Swamp Push Statue', 'Swamp Shooters', 'Swamp Left Elbow', 'Swamp Right Elbow', 'Swamp Drain Left',
|
||||
'Swamp Drain Right', 'Swamp Flooded Room', 'Swamp Flooded Spot', 'Swamp Basement Shallows', 'Swamp Waterfall Room',
|
||||
'Swamp Refill', 'Swamp Behind Waterfall', 'Swamp C', 'Swamp Waterway', 'Swamp I', 'Swamp T', 'Swamp Boss'
|
||||
'Swamp Refill', 'Swamp Behind Waterfall', 'Swamp C', 'Swamp Waterway', 'Swamp I', 'Swamp T', 'Swamp Boss',
|
||||
'Swamp Portal'
|
||||
]
|
||||
|
||||
skull_regions = [
|
||||
@@ -230,7 +235,8 @@ skull_regions = [
|
||||
'Skull Pot Prison', 'Skull Compass Room', 'Skull Left Drop', 'Skull 2 East Lobby', 'Skull Big Key',
|
||||
'Skull Lone Pot', 'Skull Small Hall', 'Skull Back Drop', 'Skull 2 West Lobby', 'Skull X Room', 'Skull 3 Lobby',
|
||||
'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits', 'Skull Torch Room', 'Skull Vines',
|
||||
'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss'
|
||||
'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss', 'Skull 1 Portal', 'Skull 2 East Portal',
|
||||
'Skull 2 West Portal', 'Skull 3 Portal'
|
||||
]
|
||||
|
||||
thieves_regions = [
|
||||
@@ -238,9 +244,10 @@ thieves_regions = [
|
||||
'Thieves Big Chest Nook', 'Thieves Hallway', 'Thieves Boss', 'Thieves Pot Alcove Mid', 'Thieves Pot Alcove Bottom',
|
||||
'Thieves Pot Alcove Top', 'Thieves Conveyor Maze', 'Thieves Spike Track', 'Thieves Hellway',
|
||||
'Thieves Hellway N Crystal', 'Thieves Hellway S Crystal', 'Thieves Triple Bypass', 'Thieves Spike Switch',
|
||||
'Thieves Attic', 'Thieves Attic Hint', 'Thieves Cricket Hall Left', 'Thieves Cricket Hall Right', 'Thieves Attic Window',
|
||||
'Thieves Basement Block', 'Thieves Blocked Entry', 'Thieves Lonely Zazak', 'Thieves Blind\'s Cell',
|
||||
'Thieves Conveyor Bridge', 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap'
|
||||
'Thieves Attic', 'Thieves Attic Hint', 'Thieves Cricket Hall Left', 'Thieves Cricket Hall Right',
|
||||
'Thieves Attic Window', 'Thieves Basement Block', 'Thieves Blocked Entry', 'Thieves Lonely Zazak',
|
||||
"Thieves Blind's Cell", "Thieves Blind's Cell Interior", 'Thieves Conveyor Bridge', 'Thieves Conveyor Block',
|
||||
'Thieves Big Chest Room', 'Thieves Trap', 'Thieves Town Portal'
|
||||
]
|
||||
|
||||
ice_regions = [
|
||||
@@ -251,7 +258,8 @@ ice_regions = [
|
||||
'Ice Tongue Pull', 'Ice Freezors', 'Ice Freezors Ledge', 'Ice Tall Hint', 'Ice Hookshot Ledge',
|
||||
'Ice Hookshot Balcony', 'Ice Spikeball', 'Ice Lonely Freezor', 'Iced T', 'Ice Catwalk', 'Ice Many Pots',
|
||||
'Ice Crystal Right', 'Ice Crystal Left', 'Ice Crystal Block', 'Ice Big Chest View', 'Ice Big Chest Landing',
|
||||
'Ice Backwards Room', 'Ice Anti-Fairy', 'Ice Switch Room', 'Ice Refill', 'Ice Fairy', 'Ice Antechamber', 'Ice Boss'
|
||||
'Ice Backwards Room', 'Ice Anti-Fairy', 'Ice Switch Room', 'Ice Refill', 'Ice Fairy', 'Ice Antechamber', 'Ice Boss',
|
||||
'Ice Portal'
|
||||
]
|
||||
|
||||
mire_regions = [
|
||||
@@ -265,7 +273,7 @@ mire_regions = [
|
||||
'Mire Torches Top', 'Mire Torches Bottom', 'Mire Attic Hint', 'Mire Dark Shooters', 'Mire Key Rupees',
|
||||
'Mire Block X', 'Mire Tall Dark and Roomy', 'Mire Crystal Right', 'Mire Crystal Mid', 'Mire Crystal Left',
|
||||
'Mire Crystal Top', 'Mire Shooter Rupees', 'Mire Falling Foes', 'Mire Firesnake Skip', 'Mire Antechamber',
|
||||
'Mire Boss'
|
||||
'Mire Boss', 'Mire Portal'
|
||||
]
|
||||
|
||||
tr_regions = [
|
||||
@@ -274,7 +282,8 @@ tr_regions = [
|
||||
'TR Lava Island', 'TR Lava Escape', 'TR Pokey 2', 'TR Twin Pokeys', 'TR Hallway', 'TR Dodgers', 'TR Big View',
|
||||
'TR Big Chest', 'TR Big Chest Entrance', 'TR Lazy Eyes', 'TR Dash Room', 'TR Tongue Pull', 'TR Rupees',
|
||||
'TR Crystaroller', 'TR Dark Ride', 'TR Dash Bridge', 'TR Eye Bridge', 'TR Crystal Maze', 'TR Crystal Maze End',
|
||||
'TR Final Abyss', 'TR Boss'
|
||||
'TR Final Abyss', 'TR Boss', 'Turtle Rock Main Portal', 'Turtle Rock Lazy Eyes Portal', 'Turtle Rock Chest Portal',
|
||||
'Turtle Rock Eye Bridge Portal'
|
||||
]
|
||||
|
||||
gt_regions = [
|
||||
@@ -294,7 +303,7 @@ gt_regions = [
|
||||
'GT Dashing Bridge', 'GT Wizzrobes 2', 'GT Conveyor Bridge', 'GT Torch Cross', 'GT Staredown', 'GT Falling Torches',
|
||||
'GT Mini Helmasaur Room', 'GT Bomb Conveyor', 'GT Crystal Circles', 'GT Left Moldorm Ledge',
|
||||
'GT Right Moldorm Ledge', 'GT Moldorm', 'GT Moldorm Pit', 'GT Validation', 'GT Validation Door', 'GT Frozen Over',
|
||||
'GT Brightly Lit Hall', 'GT Agahnim 2'
|
||||
'GT Brightly Lit Hall', 'GT Agahnim 2', 'Ganons Tower Portal'
|
||||
]
|
||||
|
||||
|
||||
@@ -332,7 +341,7 @@ region_starts = {
|
||||
}
|
||||
|
||||
standard_starts = {
|
||||
'Hyrule Castle': ['Hyrule Castle Lobby']
|
||||
'Hyrule Castle': ['Hyrule Castle South']
|
||||
}
|
||||
|
||||
split_region_starts = {
|
||||
|
||||
+65
-53
@@ -18,6 +18,8 @@ def link_entrances(world, player):
|
||||
for exitname, regionname in mandatory_connections:
|
||||
connect_simple(world, exitname, regionname, player)
|
||||
|
||||
connect_custom(world, player)
|
||||
|
||||
# if we do not shuffle, set default connections
|
||||
if world.shuffle[player] == 'vanilla':
|
||||
for exitname, regionname in default_connections:
|
||||
@@ -325,7 +327,6 @@ def link_entrances(world, player):
|
||||
elif world.doorShuffle[player] != 'vanilla':
|
||||
connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)')], player)
|
||||
|
||||
|
||||
# place old man, has limited options
|
||||
# exit has to come from specific set of doors, the entrance is free to move about
|
||||
old_man_entrances = [door for door in old_man_entrances if door in lw_entrances]
|
||||
@@ -711,7 +712,7 @@ def link_entrances(world, player):
|
||||
|
||||
# handle remaining caves
|
||||
while caves:
|
||||
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits accross worlds left to fill
|
||||
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill
|
||||
cave_candidate = (None, 0)
|
||||
for i, cave in enumerate(caves):
|
||||
if isinstance(cave, str):
|
||||
@@ -1062,7 +1063,7 @@ def link_entrances(world, player):
|
||||
raise NotImplementedError('Shuffling not supported yet')
|
||||
|
||||
# check for swamp palace fix
|
||||
if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Lobby':
|
||||
if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Portal':
|
||||
world.swamp_patch_required[player] = True
|
||||
|
||||
# check for potion shop location
|
||||
@@ -1074,7 +1075,7 @@ def link_entrances(world, player):
|
||||
world.ganon_at_pyramid[player] = False
|
||||
|
||||
# check for Ganon's Tower location
|
||||
if world.get_entrance('Ganons Tower', player).connected_region.name != 'GT Lobby':
|
||||
if world.get_entrance('Ganons Tower', player).connected_region.name != 'Ganons Tower Portal':
|
||||
world.ganonstower_vanilla[player] = False
|
||||
|
||||
def link_inverted_entrances(world, player):
|
||||
@@ -1781,6 +1782,15 @@ def link_inverted_entrances(world, player):
|
||||
if world.get_entrance('Inverted Ganons Tower', player).connected_region.name != 'GT Lobby':
|
||||
world.ganonstower_vanilla[player] = False
|
||||
|
||||
|
||||
def connect_custom(world, player):
|
||||
if hasattr(world, 'custom_entrances') and world.custom_entrances[player]:
|
||||
for exit_name, region_name in world.custom_entrances[player]:
|
||||
# doesn't actually change addresses
|
||||
connect_simple(world, exit_name, region_name, player)
|
||||
# this needs to remove custom connections from the pool
|
||||
|
||||
|
||||
def connect_simple(world, exitname, regionname, player):
|
||||
world.get_entrance(exitname, player).connect(world.get_region(regionname, player))
|
||||
|
||||
@@ -2864,6 +2874,7 @@ mandatory_connections = [('Links House S&Q', 'Links House'),
|
||||
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||
('Death Mountain Return Ledge Drop', 'Light World'),
|
||||
('Old Man Cave Dropdown', 'Old Man Cave'),
|
||||
('Old Man House Front to Back', 'Old Man House Back'),
|
||||
('Old Man House Back to Front', 'Old Man House'),
|
||||
('Broken Bridge (West)', 'East Death Mountain (Bottom)'),
|
||||
@@ -2973,6 +2984,7 @@ inverted_mandatory_connections = [('Links House S&Q', 'Inverted Links House'),
|
||||
('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||
('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'),
|
||||
('Death Mountain Return Ledge Drop', 'Light World'),
|
||||
('Old Man Cave Dropdown', 'Old Man Cave'),
|
||||
('Old Man House Front to Back', 'Old Man House Back'),
|
||||
('Old Man House Back to Front', 'Old Man House'),
|
||||
('Broken Bridge (West)', 'East Death Mountain (Bottom)'),
|
||||
@@ -3165,7 +3177,7 @@ default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'),
|
||||
('Two Brothers House Exit (East)', 'Light World'),
|
||||
('Two Brothers House Exit (West)', 'Maze Race Ledge'),
|
||||
|
||||
('Sanctuary', 'Sanctuary'),
|
||||
('Sanctuary', 'Sanctuary Portal'),
|
||||
('Sanctuary Grave', 'Sewer Drop'),
|
||||
('Sanctuary Exit', 'Light World'),
|
||||
|
||||
@@ -3400,114 +3412,114 @@ inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'
|
||||
('Inverted Pyramid Entrance', 'Bottom of Pyramid')]
|
||||
|
||||
# non shuffled dungeons
|
||||
default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Main Lobby'),
|
||||
('Desert Palace Entrance (West)', 'Desert West Lobby'),
|
||||
('Desert Palace Entrance (North)', 'Desert Back Lobby'),
|
||||
('Desert Palace Entrance (East)', 'Desert East Lobby'),
|
||||
default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert South Portal'),
|
||||
('Desert Palace Entrance (West)', 'Desert West Portal'),
|
||||
('Desert Palace Entrance (North)', 'Desert Back Portal'),
|
||||
('Desert Palace Entrance (East)', 'Desert East Portal'),
|
||||
('Desert Palace Exit (South)', 'Desert Palace Stairs'),
|
||||
('Desert Palace Exit (West)', 'Desert Ledge'),
|
||||
('Desert Palace Exit (East)', 'Desert Palace Lone Stairs'),
|
||||
('Desert Palace Exit (North)', 'Desert Palace Entrance (North) Spot'),
|
||||
|
||||
('Eastern Palace', 'Eastern Lobby'),
|
||||
('Eastern Palace', 'Eastern Portal'),
|
||||
('Eastern Palace Exit', 'Light World'),
|
||||
('Tower of Hera', 'Hera Lobby'),
|
||||
('Tower of Hera', 'Hera Portal'),
|
||||
('Tower of Hera Exit', 'Death Mountain (Top)'),
|
||||
|
||||
('Hyrule Castle Entrance (South)', 'Hyrule Castle Lobby'),
|
||||
('Hyrule Castle Entrance (West)', 'Hyrule Castle West Lobby'),
|
||||
('Hyrule Castle Entrance (East)', 'Hyrule Castle East Lobby'),
|
||||
('Hyrule Castle Entrance (South)', 'Hyrule Castle South Portal'),
|
||||
('Hyrule Castle Entrance (West)', 'Hyrule Castle West Portal'),
|
||||
('Hyrule Castle Entrance (East)', 'Hyrule Castle East Portal'),
|
||||
('Hyrule Castle Exit (South)', 'Hyrule Castle Courtyard'),
|
||||
('Hyrule Castle Exit (West)', 'Hyrule Castle Ledge'),
|
||||
('Hyrule Castle Exit (East)', 'Hyrule Castle Ledge'),
|
||||
('Agahnims Tower', 'Tower Lobby'),
|
||||
('Agahnims Tower', 'Agahnims Tower Portal'),
|
||||
('Agahnims Tower Exit', 'Hyrule Castle Ledge'),
|
||||
|
||||
('Thieves Town', 'Thieves Lobby'),
|
||||
('Thieves Town', 'Thieves Town Portal'),
|
||||
('Thieves Town Exit', 'West Dark World'),
|
||||
('Skull Woods First Section Hole (East)', 'Skull Pinball'),
|
||||
('Skull Woods First Section Hole (West)', 'Skull Left Drop'),
|
||||
('Skull Woods First Section Hole (North)', 'Skull Pot Circle'),
|
||||
('Skull Woods First Section Door', 'Skull 1 Lobby'),
|
||||
('Skull Woods First Section Door', 'Skull 1 Portal'),
|
||||
('Skull Woods First Section Exit', 'Skull Woods Forest'),
|
||||
('Skull Woods Second Section Hole', 'Skull Back Drop'),
|
||||
('Skull Woods Second Section Door (East)', 'Skull 2 East Lobby'),
|
||||
('Skull Woods Second Section Door (West)', 'Skull 2 West Lobby'),
|
||||
('Skull Woods Second Section Door (East)', 'Skull 2 East Portal'),
|
||||
('Skull Woods Second Section Door (West)', 'Skull 2 West Portal'),
|
||||
('Skull Woods Second Section Exit (East)', 'Skull Woods Forest'),
|
||||
('Skull Woods Second Section Exit (West)', 'Skull Woods Forest (West)'),
|
||||
('Skull Woods Final Section', 'Skull 3 Lobby'),
|
||||
('Skull Woods Final Section', 'Skull 3 Portal'),
|
||||
('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'),
|
||||
('Ice Palace', 'Ice Lobby'),
|
||||
('Ice Palace', 'Ice Portal'),
|
||||
('Ice Palace Exit', 'Dark Lake Hylia Central Island'),
|
||||
('Misery Mire', 'Mire Lobby'),
|
||||
('Misery Mire', 'Mire Portal'),
|
||||
('Misery Mire Exit', 'Dark Desert'),
|
||||
('Palace of Darkness', 'PoD Lobby'),
|
||||
('Palace of Darkness', 'Palace of Darkness Portal'),
|
||||
('Palace of Darkness Exit', 'East Dark World'),
|
||||
('Swamp Palace', 'Swamp Lobby'), # requires additional patch for flooding moat if moved
|
||||
('Swamp Palace', 'Swamp Portal'), # requires additional patch for flooding moat if moved
|
||||
('Swamp Palace Exit', 'South Dark World'),
|
||||
|
||||
('Turtle Rock', 'TR Main Lobby'),
|
||||
('Turtle Rock', 'Turtle Rock Main Portal'),
|
||||
('Turtle Rock Exit (Front)', 'Dark Death Mountain (Top)'),
|
||||
('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'),
|
||||
('Turtle Rock Ledge Exit (East)', 'Dark Death Mountain Ledge'),
|
||||
('Dark Death Mountain Ledge (West)', 'TR Lazy Eyes'),
|
||||
('Dark Death Mountain Ledge (East)', 'TR Big Chest Entrance'),
|
||||
('Dark Death Mountain Ledge (West)', 'Turtle Rock Lazy Eyes Portal'),
|
||||
('Dark Death Mountain Ledge (East)', 'Turtle Rock Chest Portal'),
|
||||
('Turtle Rock Isolated Ledge Exit', 'Dark Death Mountain Isolated Ledge'),
|
||||
('Turtle Rock Isolated Ledge Entrance', 'TR Eye Bridge'),
|
||||
('Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Eye Bridge Portal'),
|
||||
|
||||
('Ganons Tower', 'GT Lobby'),
|
||||
('Ganons Tower', 'Ganons Tower Portal'),
|
||||
('Ganons Tower Exit', 'Dark Death Mountain (Top)')
|
||||
]
|
||||
|
||||
inverted_default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Main Lobby'),
|
||||
('Desert Palace Entrance (West)', 'Desert West Lobby'),
|
||||
('Desert Palace Entrance (North)', 'Desert Back Lobby'),
|
||||
('Desert Palace Entrance (East)', 'Desert East Lobby'),
|
||||
inverted_default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert South Portal'),
|
||||
('Desert Palace Entrance (West)', 'Desert West Portal'),
|
||||
('Desert Palace Entrance (North)', 'Desert Back Portal'),
|
||||
('Desert Palace Entrance (East)', 'Desert East Portal'),
|
||||
('Desert Palace Exit (South)', 'Desert Palace Stairs'),
|
||||
('Desert Palace Exit (West)', 'Desert Ledge'),
|
||||
('Desert Palace Exit (East)', 'Desert Palace Lone Stairs'),
|
||||
('Desert Palace Exit (North)', 'Desert Palace Entrance (North) Spot'),
|
||||
('Eastern Palace', 'Eastern Lobby'),
|
||||
('Eastern Palace', 'Eastern Portal'),
|
||||
('Eastern Palace Exit', 'Light World'),
|
||||
('Tower of Hera', 'Hera Lobby'),
|
||||
('Tower of Hera', 'Hera Portal'),
|
||||
('Tower of Hera Exit', 'Death Mountain (Top)'),
|
||||
('Hyrule Castle Entrance (South)', 'Hyrule Castle Lobby'),
|
||||
('Hyrule Castle Entrance (West)', 'Hyrule Castle West Lobby'),
|
||||
('Hyrule Castle Entrance (East)', 'Hyrule Castle East Lobby'),
|
||||
('Hyrule Castle Entrance (South)', 'Hyrule Castle South Portal'),
|
||||
('Hyrule Castle Entrance (West)', 'Hyrule Castle West Portal'),
|
||||
('Hyrule Castle Entrance (East)', 'Hyrule Castle East Portal'),
|
||||
('Hyrule Castle Exit (South)', 'Light World'),
|
||||
('Hyrule Castle Exit (West)', 'Hyrule Castle Ledge'),
|
||||
('Hyrule Castle Exit (East)', 'Hyrule Castle Ledge'),
|
||||
('Thieves Town', 'Thieves Lobby'),
|
||||
('Thieves Town', 'Thieves Town Portal'),
|
||||
('Thieves Town Exit', 'West Dark World'),
|
||||
('Skull Woods First Section Hole (East)', 'Skull Pinball'),
|
||||
('Skull Woods First Section Hole (West)', 'Skull Left Drop'),
|
||||
('Skull Woods First Section Hole (North)', 'Skull Pot Circle'),
|
||||
('Skull Woods First Section Door', 'Skull 1 Lobby'),
|
||||
('Skull Woods First Section Door', 'Skull 1 Portal'),
|
||||
('Skull Woods First Section Exit', 'Skull Woods Forest'),
|
||||
('Skull Woods Second Section Hole', 'Skull Back Drop'),
|
||||
('Skull Woods Second Section Door (East)', 'Skull 2 East Lobby'),
|
||||
('Skull Woods Second Section Door (West)', 'Skull 2 West Lobby'),
|
||||
('Skull Woods Second Section Door (East)', 'Skull 2 East Portal'),
|
||||
('Skull Woods Second Section Door (West)', 'Skull 2 West Portal'),
|
||||
('Skull Woods Second Section Exit (East)', 'Skull Woods Forest'),
|
||||
('Skull Woods Second Section Exit (West)', 'Skull Woods Forest (West)'),
|
||||
('Skull Woods Final Section', 'Skull 3 Lobby'),
|
||||
('Skull Woods Final Section', 'Skull 3 Portal'),
|
||||
('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'),
|
||||
('Ice Palace', 'Ice Lobby'),
|
||||
('Misery Mire', 'Mire Lobby'),
|
||||
('Ice Palace', 'Ice Portal'),
|
||||
('Misery Mire', 'Mire Portal'),
|
||||
('Misery Mire Exit', 'Dark Desert'),
|
||||
('Palace of Darkness', 'PoD Lobby'),
|
||||
('Palace of Darkness', 'Palace of Darkness Portal'),
|
||||
('Palace of Darkness Exit', 'East Dark World'),
|
||||
('Swamp Palace', 'Swamp Lobby'), # requires additional patch for flooding moat if moved
|
||||
('Swamp Palace', 'Swamp Portal'), # requires additional patch for flooding moat if moved
|
||||
('Swamp Palace Exit', 'South Dark World'),
|
||||
('Turtle Rock', 'TR Main Lobby'),
|
||||
('Turtle Rock', 'Turtle Rock Main Portal'),
|
||||
('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'),
|
||||
('Turtle Rock Ledge Exit (East)', 'Dark Death Mountain Ledge'),
|
||||
('Dark Death Mountain Ledge (West)', 'TR Lazy Eyes'),
|
||||
('Dark Death Mountain Ledge (East)', 'TR Big Chest Entrance'),
|
||||
('Dark Death Mountain Ledge (West)', 'Turtle Rock Lazy Eyes Portal'),
|
||||
('Dark Death Mountain Ledge (East)', 'Turtle Rock Chest Portal'),
|
||||
('Turtle Rock Isolated Ledge Exit', 'Dark Death Mountain Isolated Ledge'),
|
||||
('Turtle Rock Isolated Ledge Entrance', 'TR Eye Bridge'),
|
||||
('Inverted Ganons Tower', 'GT Lobby'),
|
||||
('Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Eye Bridge Portal'),
|
||||
('Inverted Ganons Tower', 'Ganons Tower Portal'),
|
||||
('Inverted Ganons Tower Exit', 'Hyrule Castle Ledge'),
|
||||
('Inverted Agahnims Tower', 'Tower Lobby'),
|
||||
('Inverted Agahnims Tower', 'Agahnims Tower Portal'),
|
||||
('Inverted Agahnims Tower Exit', 'Dark Death Mountain'),
|
||||
('Turtle Rock Exit (Front)', 'Dark Death Mountain'),
|
||||
('Ice Palace Exit', 'Dark Lake Hylia')
|
||||
|
||||
+4
-3
@@ -1,12 +1,12 @@
|
||||
import collections
|
||||
from BaseClasses import RegionType
|
||||
from Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
|
||||
from Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region, create_menu_region
|
||||
|
||||
|
||||
def create_inverted_regions(world, player):
|
||||
|
||||
world.regions += [
|
||||
create_dw_region(player, 'Menu', None, ['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
|
||||
create_menu_region(player, 'Menu', None, ['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
|
||||
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest', 'Bombos Tablet'],
|
||||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam',
|
||||
'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||
@@ -107,7 +107,8 @@ def create_inverted_regions(world, player):
|
||||
create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']),
|
||||
create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
|
||||
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower', 'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']),
|
||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']),
|
||||
create_cave_region(player, 'Old Man Cave Ledge', 'a connector', None, ['Old Man Cave Exit (West)', 'Old Man Cave Dropdown']),
|
||||
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave',
|
||||
|
||||
+29
-2
@@ -6,7 +6,7 @@ from BaseClasses import Region, RegionType, Shop, ShopType, Location
|
||||
from Bosses import place_bosses
|
||||
from Dungeons import get_dungeon_item_pool
|
||||
from EntranceShuffle import connect_entrance
|
||||
from Fill import FillError, fill_restrictive
|
||||
from Fill import FillError, fill_restrictive, fast_fill
|
||||
from Items import ItemFactory
|
||||
|
||||
import source.classes.constants as CONST
|
||||
@@ -266,7 +266,7 @@ def generate_itempool(world, player):
|
||||
amt = world.pool_adjustment[player]
|
||||
if amt < 0:
|
||||
for _ in range(amt, 0):
|
||||
pool.remove('Rupees (20)')
|
||||
pool.remove(next(iter([x for x in pool if x in ['Rupees (20)', 'Rupees (5)', 'Rupee (1)']])))
|
||||
elif amt > 0:
|
||||
for _ in range(0, amt):
|
||||
pool.append('Rupees (20)')
|
||||
@@ -355,9 +355,12 @@ def generate_itempool(world, player):
|
||||
|
||||
if world.retro[player]:
|
||||
set_up_take_anys(world, player)
|
||||
if world.keydropshuffle[player]:
|
||||
world.itempool += [ItemFactory('Small Key (Universal)', player)] * 32
|
||||
|
||||
create_dynamic_shop_locations(world, player)
|
||||
|
||||
|
||||
take_any_locations = [
|
||||
'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut',
|
||||
'Fortune Teller (Light)', 'Lake Hylia Fortune Teller', 'Lumberjack House', 'Bonk Fairy (Light)',
|
||||
@@ -735,3 +738,27 @@ def test():
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
||||
|
||||
def fill_specific_items(world):
|
||||
keypool = [item for item in world.itempool if item.smallkey]
|
||||
cage = world.get_location('Tower of Hera - Basement Cage', 1)
|
||||
c_dungeon = cage.parent_region.dungeon
|
||||
key_item = next(x for x in keypool if c_dungeon.name in x.name or (c_dungeon.name == 'Hyrule Castle' and 'Escape' in x.name))
|
||||
world.itempool.remove(key_item)
|
||||
all_state = world.get_all_state(True)
|
||||
fill_restrictive(world, all_state, [cage], [key_item])
|
||||
|
||||
location = world.get_location('Tower of Hera - Map Chest', 1)
|
||||
key_item = next(x for x in world.itempool if 'Byrna' in x.name)
|
||||
world.itempool.remove(key_item)
|
||||
fast_fill(world, [key_item], [location])
|
||||
|
||||
|
||||
# somaria = next(item for item in world.itempool if item.name == 'Cane of Somaria')
|
||||
# shooter = world.get_location('Palace of Darkness - Shooter Room', 1)
|
||||
# world.itempool.remove(somaria)
|
||||
# all_state = world.get_all_state(True)
|
||||
# fill_restrictive(world, all_state, [shooter], [somaria])
|
||||
|
||||
|
||||
|
||||
+85
-49
@@ -3,9 +3,9 @@ import logging
|
||||
from collections import defaultdict, deque
|
||||
|
||||
from BaseClasses import DoorType
|
||||
from Regions import dungeon_events, key_only_locations
|
||||
from Regions import dungeon_events
|
||||
from Dungeons import dungeon_keys, dungeon_bigs
|
||||
from DungeonGenerator import ExplorationState
|
||||
from DungeonGenerator import ExplorationState, special_big_key_doors
|
||||
|
||||
|
||||
class KeyLayout(object):
|
||||
@@ -169,28 +169,18 @@ class KeyCounter(object):
|
||||
self.big_key_opened = False
|
||||
self.important_location = False
|
||||
self.other_locations = {}
|
||||
self.important_locations = {}
|
||||
|
||||
def used_smalls_loc(self, reserve=0):
|
||||
return max(self.used_keys + reserve - len(self.key_only_locations), 0)
|
||||
|
||||
def copy(self):
|
||||
ret = KeyCounter(self.max_chests)
|
||||
ret.free_locations.update(self.free_locations)
|
||||
ret.key_only_locations.update(self.key_only_locations)
|
||||
ret.child_doors.update(self.child_doors)
|
||||
ret.used_keys = self.used_keys
|
||||
ret.open_doors.update(self.open_doors)
|
||||
ret.big_key_opened = self.big_key_opened
|
||||
ret.important_location = self.important_location
|
||||
return ret
|
||||
|
||||
|
||||
def build_key_layout(builder, start_regions, proposal, world, player):
|
||||
key_layout = KeyLayout(builder.master_sector, start_regions, proposal)
|
||||
key_layout.flat_prop = flatten_pair_list(key_layout.proposal)
|
||||
key_layout.max_drops = count_key_drops(key_layout.sector)
|
||||
key_layout.max_chests = calc_max_chests(builder, key_layout, world, player)
|
||||
key_layout.big_key_special = 'Hyrule Dungeon Cellblock' in key_layout.sector.region_set()
|
||||
key_layout.big_key_special = check_bk_special(key_layout.sector.region_set(), world, player)
|
||||
key_layout.all_locations = find_all_locations(key_layout.sector)
|
||||
return key_layout
|
||||
|
||||
@@ -199,7 +189,7 @@ def count_key_drops(sector):
|
||||
cnt = 0
|
||||
for region in sector.regions:
|
||||
for loc in region.locations:
|
||||
if loc.event and 'Small Key' in loc.item.name:
|
||||
if loc.forced_item and 'Small Key' in loc.item.name:
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
@@ -243,18 +233,17 @@ def analyze_dungeon(key_layout, world, player):
|
||||
possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop)
|
||||
avail_bigs = exist_relevant_big_doors(key_counter, key_layout) or exist_big_chest(key_counter)
|
||||
non_big_locs = count_locations_big_optional(key_counter.free_locations)
|
||||
if not key_counter.big_key_opened:
|
||||
big_avail = key_counter.big_key_opened or (key_layout.big_key_special and any(x for x in key_counter.other_locations.keys() if x.forced_item and x.forced_item.bigkey))
|
||||
if not big_avail:
|
||||
if chest_keys == non_big_locs and chest_keys > 0 and available <= possible_smalls and not avail_bigs:
|
||||
key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations))
|
||||
# try to relax the rules here? - smallest requirement that doesn't force a softlock
|
||||
child_queue = deque()
|
||||
for child in key_counter.child_doors.keys():
|
||||
if not child.bigKey or not key_layout.big_key_special or key_counter.big_key_opened:
|
||||
if not child.bigKey or not key_layout.big_key_special or big_avail:
|
||||
odd_counter = create_odd_key_counter(child, key_counter, key_layout, world, player)
|
||||
empty_flag = empty_counter(odd_counter)
|
||||
child_queue.append((child, odd_counter, empty_flag))
|
||||
if child in doors_completed and child in key_logic.door_rules.keys():
|
||||
rule = key_logic.door_rules[child]
|
||||
while len(child_queue) > 0:
|
||||
child, odd_counter, empty_flag = child_queue.popleft()
|
||||
if not child.bigKey and child not in doors_completed:
|
||||
@@ -541,6 +530,9 @@ def relative_empty_counter(odd_counter, key_counter):
|
||||
return False
|
||||
if len(set(odd_counter.free_locations).difference(key_counter.free_locations)) > 0:
|
||||
return False
|
||||
# important only
|
||||
if len(set(odd_counter.important_locations).difference(key_counter.important_locations)) > 0:
|
||||
return False
|
||||
new_child_door = False
|
||||
for child in odd_counter.child_doors:
|
||||
if unique_child_door(child, key_counter):
|
||||
@@ -556,6 +548,9 @@ def relative_empty_counter_2(odd_counter, key_counter):
|
||||
return False
|
||||
if len(set(odd_counter.free_locations).difference(key_counter.free_locations)) > 0:
|
||||
return False
|
||||
# important only
|
||||
if len(set(odd_counter.important_locations).difference(key_counter.important_locations)) > 0:
|
||||
return False
|
||||
for child in odd_counter.child_doors:
|
||||
if unique_child_door_2(child, key_counter):
|
||||
return False
|
||||
@@ -650,17 +645,17 @@ def find_worst_counter(door, odd_counter, key_counter, key_layout, skip_bk): #
|
||||
def find_potential_open_doors(key_counter, ignored_doors, key_layout, skip_bk, reserve=1):
|
||||
small_doors = []
|
||||
big_doors = []
|
||||
if key_layout.big_key_special:
|
||||
big_key_available = any(x for x in key_counter.other_locations.keys() if x.forced_item and x.forced_item.bigkey)
|
||||
else:
|
||||
big_key_available = len(key_counter.free_locations) - key_counter.used_smalls_loc(reserve) > 0
|
||||
for other in key_counter.child_doors:
|
||||
if other not in ignored_doors and other.dest not in ignored_doors:
|
||||
if other.bigKey:
|
||||
if not skip_bk and (not key_layout.big_key_special or key_counter.big_key_opened):
|
||||
if not skip_bk and (not key_layout.big_key_special or big_key_available):
|
||||
big_doors.append(other)
|
||||
elif other.dest not in small_doors:
|
||||
small_doors.append(other)
|
||||
if key_layout.big_key_special:
|
||||
big_key_available = key_counter.big_key_opened
|
||||
else:
|
||||
big_key_available = len(key_counter.free_locations) - key_counter.used_smalls_loc(reserve) > 0
|
||||
if len(small_doors) == 0 and (not skip_bk and (len(big_doors) == 0 or not big_key_available)):
|
||||
return None
|
||||
return small_doors + big_doors
|
||||
@@ -832,6 +827,13 @@ def available_chest_small_keys_logic(key_counter, world, player, sm_restricted):
|
||||
return key_counter.max_chests
|
||||
|
||||
|
||||
def big_key_drop_available(key_counter):
|
||||
for loc in key_counter.other_locations:
|
||||
if loc.forced_big_key():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def bk_restricted_rules(rule, door, odd_counter, empty_flag, key_counter, key_layout, world, player):
|
||||
if key_counter.big_key_opened:
|
||||
return
|
||||
@@ -878,7 +880,7 @@ def find_worst_counter_wo_bk(small_key_num, accessible_set, door, odd_ctr, key_c
|
||||
|
||||
|
||||
def open_a_door(door, child_state, flat_proposal):
|
||||
if door.bigKey:
|
||||
if door.bigKey or door.name in special_big_key_doors:
|
||||
child_state.big_key_opened = True
|
||||
child_state.avail_doors.extend(child_state.big_doors)
|
||||
child_state.opened_doors.extend(set([d.door for d in child_state.big_doors]))
|
||||
@@ -972,11 +974,26 @@ def filter_big_chest(locations):
|
||||
return [x for x in locations if '- Big Chest' not in x.name]
|
||||
|
||||
|
||||
def count_locations_exclude_logic(locations, key_logic):
|
||||
cnt = 0
|
||||
for loc in locations:
|
||||
if not location_is_bk_locked(loc, key_logic) and not loc.forced_item and not prize_or_event(loc):
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
|
||||
def location_is_bk_locked(loc, key_logic):
|
||||
return loc in key_logic.bk_chests or loc in key_logic.bk_locked
|
||||
|
||||
|
||||
def prize_or_event(loc):
|
||||
return loc.name in dungeon_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']
|
||||
|
||||
|
||||
def count_free_locations(state):
|
||||
cnt = 0
|
||||
for loc in state.found_locations:
|
||||
if '- Prize' not in loc.name and loc.name not in dungeon_events and not loc.forced_item:
|
||||
if loc.name not in ['Agahnim 1', 'Agahnim 2']:
|
||||
if not prize_or_event(loc) and not loc.forced_item:
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
@@ -984,9 +1001,7 @@ def count_free_locations(state):
|
||||
def count_locations_exclude_big_chest(state):
|
||||
cnt = 0
|
||||
for loc in state.found_locations:
|
||||
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events:
|
||||
if not loc.forced_item and loc.name not in ['Agahnim 1', 'Agahnim 2', "Hyrule Castle - Zelda's Chest",
|
||||
"Thieves' Town - Blind's Cell"]:
|
||||
if '- Big Chest' not in loc.name and not loc.forced_item and not prize_or_event(loc):
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
@@ -1138,16 +1153,20 @@ def check_rules_deep(original_counter, key_layout, world, player):
|
||||
bail = 0
|
||||
last_counter = counter
|
||||
chest_keys = available_chest_small_keys_logic(counter, world, player, key_logic.sm_restricted)
|
||||
big_avail = counter.big_key_opened
|
||||
big_maybe_not_found = not counter.big_key_opened
|
||||
bk_drop = big_key_drop_available(counter)
|
||||
big_avail = counter.big_key_opened or bk_drop
|
||||
big_maybe_not_found = not counter.big_key_opened and not bk_drop # better named as big_missing?
|
||||
if not key_layout.big_key_special and not big_avail:
|
||||
if world.bigkeyshuffle[player]:
|
||||
big_avail = True
|
||||
else:
|
||||
for location in counter.free_locations:
|
||||
if location not in key_logic.bk_restricted:
|
||||
big_avail = True
|
||||
break
|
||||
outstanding_big_locs = {x for x in big_locations if x not in counter.free_locations}
|
||||
if big_maybe_not_found:
|
||||
if len(outstanding_big_locs) == 0:
|
||||
if len(outstanding_big_locs) == 0 and not key_layout.big_key_special:
|
||||
big_maybe_not_found = False
|
||||
big_uses_chest = big_avail and not key_layout.big_key_special
|
||||
collected_alt = len(counter.key_only_locations) + chest_keys
|
||||
@@ -1155,7 +1174,7 @@ def check_rules_deep(original_counter, key_layout, world, player):
|
||||
chest_keys -= 1
|
||||
collected = len(counter.key_only_locations) + chest_keys
|
||||
can_progress = len(counter.child_doors) == 0
|
||||
smalls_opened = False
|
||||
smalls_opened, big_opened = False, False
|
||||
small_rules = []
|
||||
for door in counter.child_doors.keys():
|
||||
can_open = False
|
||||
@@ -1229,6 +1248,15 @@ def set_paired_rules(key_logic, world, player):
|
||||
rule.opposite = key_logic.door_rules[door.dest.name]
|
||||
|
||||
|
||||
def check_bk_special(regions, world, player):
|
||||
for r_name in regions:
|
||||
region = world.get_region(r_name, player)
|
||||
for loc in region.locations:
|
||||
if loc.forced_big_key():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Soft lock stuff
|
||||
def validate_key_layout(key_layout, world, player):
|
||||
# retro is all good - except for hyrule castle in standard mode
|
||||
@@ -1237,7 +1265,7 @@ def validate_key_layout(key_layout, world, player):
|
||||
flat_proposal = key_layout.flat_prop
|
||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
||||
state.key_locations = key_layout.max_chests
|
||||
state.big_key_special = world.get_region('Hyrule Dungeon Cellblock', player) in key_layout.sector.regions
|
||||
state.big_key_special = check_bk_special(key_layout.sector.regions, world, player)
|
||||
for region in key_layout.start_regions:
|
||||
state.visit_region(region, key_checks=True)
|
||||
state.add_all_doors_check_keys(region, flat_proposal, world, player)
|
||||
@@ -1259,7 +1287,10 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
||||
return False
|
||||
# todo: allow more key shuffles - refine placement rules
|
||||
# if (not smalls_avail or available_small_locations == 0) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0):
|
||||
if (not smalls_avail or not enough_small_locations(state, available_small_locations)) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0):
|
||||
found_forced_bk = state.found_forced_bk()
|
||||
smalls_done = not smalls_avail or not enough_small_locations(state, available_small_locations)
|
||||
bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk)
|
||||
if smalls_done and bk_done:
|
||||
return False
|
||||
else:
|
||||
if smalls_avail and available_small_locations > 0:
|
||||
@@ -1278,9 +1309,10 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
||||
valid = checked_states[code]
|
||||
if not valid:
|
||||
return False
|
||||
if not state.big_key_opened and available_big_locations >= num_bigs > 0:
|
||||
if not state.big_key_opened and (available_big_locations >= num_bigs > 0 or (found_forced_bk and num_bigs > 0)):
|
||||
state_copy = state.copy()
|
||||
open_a_door(state.big_doors[0].door, state_copy, flat_proposal)
|
||||
if not found_forced_bk:
|
||||
state_copy.used_locations += 1
|
||||
code = state_id(state_copy, flat_proposal)
|
||||
if code not in checked_states.keys():
|
||||
@@ -1347,7 +1379,12 @@ def create_key_counters(key_layout, world, player):
|
||||
state.key_locations = len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
||||
else:
|
||||
state.key_locations = world.dungeon_layouts[player][key_layout.sector.name].key_doors_num
|
||||
state.big_key_special = world.get_region('Hyrule Dungeon Cellblock', player) in key_layout.sector.regions
|
||||
state.big_key_special, special_region = False, None
|
||||
for region in key_layout.sector.regions:
|
||||
for location in region.locations:
|
||||
if location.forced_big_key():
|
||||
state.big_key_special = True
|
||||
special_region = region
|
||||
for region in key_layout.start_regions:
|
||||
state.visit_region(region, key_checks=True)
|
||||
state.add_all_doors_check_keys(region, flat_proposal, world, player)
|
||||
@@ -1359,10 +1396,10 @@ def create_key_counters(key_layout, world, player):
|
||||
next_key_counter, parent_state = queue.popleft()
|
||||
for door in next_key_counter.child_doors:
|
||||
child_state = parent_state.copy()
|
||||
if door.bigKey:
|
||||
if door.bigKey or door.name in special_big_key_doors:
|
||||
key_layout.key_logic.bk_doors.add(door)
|
||||
# open the door, if possible
|
||||
if not door.bigKey or not child_state.big_key_special or child_state.big_key_opened:
|
||||
if not door.bigKey or not child_state.big_key_special or child_state.visited_at_all(special_region):
|
||||
open_a_door(door, child_state, flat_proposal)
|
||||
expand_key_state(child_state, flat_proposal, world, player)
|
||||
code = state_id(child_state, key_layout.flat_prop)
|
||||
@@ -1380,6 +1417,7 @@ def create_key_counter(state, key_layout, world, player):
|
||||
if important_location(loc, world, player):
|
||||
key_counter.important_location = True
|
||||
key_counter.other_locations[loc] = None
|
||||
key_counter.important_locations[loc] = None
|
||||
elif loc.forced_item and loc.item.name == key_layout.key_logic.small_key_name:
|
||||
key_counter.key_only_locations[loc] = None
|
||||
elif loc.forced_item and loc.item.name == key_layout.key_logic.bk_name:
|
||||
@@ -1390,9 +1428,6 @@ def create_key_counter(state, key_layout, world, player):
|
||||
key_counter.other_locations[loc] = None
|
||||
key_counter.open_doors.update(dict.fromkeys(state.opened_doors))
|
||||
key_counter.used_keys = count_unique_sm_doors(state.opened_doors)
|
||||
if state.big_key_special:
|
||||
key_counter.big_key_opened = state.visited(world.get_region('Hyrule Dungeon Cellblock', player))
|
||||
else:
|
||||
key_counter.big_key_opened = state.big_key_opened
|
||||
return key_counter
|
||||
|
||||
@@ -1412,7 +1447,7 @@ def imp_locations_factory(world, player):
|
||||
|
||||
|
||||
def important_location(loc, world, player):
|
||||
return '- Prize' in loc.name or loc.name in imp_locations_factory(world, player) or (loc.forced_item is not None and loc.item.bigkey)
|
||||
return '- Prize' in loc.name or loc.name in imp_locations_factory(world, player) or (loc.forced_big_key())
|
||||
|
||||
|
||||
def create_odd_key_counter(door, parent_counter, key_layout, world, player):
|
||||
@@ -1422,6 +1457,7 @@ def create_odd_key_counter(door, parent_counter, key_layout, world, player):
|
||||
odd_counter.key_only_locations = dict_difference(next_counter.key_only_locations, parent_counter.key_only_locations)
|
||||
odd_counter.child_doors = dict_difference(next_counter.child_doors, parent_counter.child_doors)
|
||||
odd_counter.other_locations = dict_difference(next_counter.other_locations, parent_counter.other_locations)
|
||||
odd_counter.important_locations = dict_difference(next_counter.important_locations, parent_counter.important_locations)
|
||||
for loc in odd_counter.other_locations:
|
||||
if important_location(loc, world, player):
|
||||
odd_counter.important_location = True
|
||||
@@ -1679,13 +1715,13 @@ def validate_key_placement(key_layout, world, player):
|
||||
max_counter = find_max_counter(key_layout)
|
||||
keys_outside = 0
|
||||
big_key_outside = False
|
||||
dungeon = world.get_dungeon(key_layout.sector.name, player)
|
||||
smallkey_name = 'Small Key (%s)' % (key_layout.sector.name if key_layout.sector.name != 'Hyrule Castle' else 'Escape')
|
||||
smallkey_name = dungeon_keys[key_layout.sector.name]
|
||||
bigkey_name = dungeon_bigs[key_layout.sector.name]
|
||||
if world.keyshuffle[player]:
|
||||
keys_outside = key_layout.max_chests - sum(1 for i in max_counter.free_locations if i.item is not None and i.item.name == smallkey_name and i.item.player == player)
|
||||
if world.bigkeyshuffle[player]:
|
||||
max_counter = find_max_counter(key_layout)
|
||||
big_key_outside = dungeon.big_key not in (l.item for l in max_counter.free_locations)
|
||||
big_key_outside = bigkey_name not in (l.item.name for l in max_counter.free_locations if l.item)
|
||||
|
||||
for code, counter in key_layout.key_counters.items():
|
||||
if len(counter.child_doors) == 0:
|
||||
@@ -1693,7 +1729,7 @@ def validate_key_placement(key_layout, world, player):
|
||||
if key_layout.big_key_special:
|
||||
big_found = any(i.forced_item is not None and i.item.bigkey for i in counter.other_locations) or big_key_outside
|
||||
else:
|
||||
big_found = any(i.item is not None and i.item == dungeon.big_key for i in counter.free_locations if "- Big Chest" not in i.name) or big_key_outside
|
||||
big_found = any(i.item is not None and i.item.name == bigkey_name for i in counter.free_locations if "- Big Chest" not in i.name) or big_key_outside
|
||||
if counter.big_key_opened and not big_found:
|
||||
continue # Can't get to this state
|
||||
found_locations = set(i for i in counter.free_locations if big_found or "- Big Chest" not in i.name)
|
||||
@@ -1703,7 +1739,7 @@ def validate_key_placement(key_layout, world, player):
|
||||
found_keys > counter.used_keys and any(not d.bigKey for d in counter.child_doors)
|
||||
if not can_progress:
|
||||
missing_locations = set(max_counter.free_locations.keys()).difference(found_locations)
|
||||
missing_items = [l for l in missing_locations if l.item is None or (l.item.name != smallkey_name and l.item != dungeon.big_key) or "- Boss" in l.name]
|
||||
missing_items = [l for l in missing_locations if l.item is None or (l.item.name != smallkey_name and l.item.name != bigkey_name) or "- Boss" in l.name]
|
||||
# missing_key_only = set(max_counter.key_only_locations.keys()).difference(counter.key_only_locations.keys()) # do freestanding keys matter for locations?
|
||||
if len(missing_items) > 0: # world.accessibility[player]=='locations' and (len(missing_locations)>0 or len(missing_key_only) > 0):
|
||||
logging.getLogger('').error("Keylock - can't open locations: ")
|
||||
|
||||
@@ -11,20 +11,21 @@ import zlib
|
||||
from BaseClasses import World, CollectionState, Item, Region, Location, Shop, Entrance
|
||||
from Items import ItemFactory
|
||||
from KeyDoorShuffle import validate_key_placement
|
||||
from Regions import create_regions, create_shops, mark_light_world_regions, create_dungeon_regions
|
||||
from PotShuffle import shuffle_pots
|
||||
from Regions import create_regions, create_shops, mark_light_world_regions, create_dungeon_regions, adjust_locations
|
||||
from InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
||||
from EntranceShuffle import link_entrances, link_inverted_entrances
|
||||
from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, JsonRom, get_hash_string
|
||||
from Doors import create_doors
|
||||
from DoorShuffle import link_doors
|
||||
from DoorShuffle import link_doors, connect_portal_copy
|
||||
from RoomData import create_rooms
|
||||
from Rules import set_rules
|
||||
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
||||
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression
|
||||
from ItemList import generate_itempool, difficulties, fill_prizes
|
||||
from ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
|
||||
from Utils import output_path, parse_player_names
|
||||
|
||||
__version__ = '0.1.1-dev'
|
||||
__version__ = '0.2.0-dev'
|
||||
|
||||
class EnemizerError(RuntimeError):
|
||||
pass
|
||||
@@ -56,6 +57,8 @@ def main(args, seed=None, fish=None):
|
||||
world.bigkeyshuffle = args.bigkeyshuffle.copy()
|
||||
world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)}
|
||||
world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)}
|
||||
world.crystals_ganon_orig = args.crystals_ganon.copy()
|
||||
world.crystals_gt_orig = args.crystals_gt.copy()
|
||||
world.open_pyramid = args.openpyramid.copy()
|
||||
world.boss_shuffle = args.shufflebosses.copy()
|
||||
world.enemy_shuffle = args.shuffleenemies.copy()
|
||||
@@ -65,7 +68,11 @@ def main(args, seed=None, fish=None):
|
||||
world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)}
|
||||
world.experimental = args.experimental.copy()
|
||||
world.dungeon_counters = args.dungeon_counters.copy()
|
||||
world.potshuffle = args.shufflepots.copy()
|
||||
world.fish = fish
|
||||
world.keydropshuffle = args.keydropshuffle.copy()
|
||||
world.mixed_travel = args.mixed_travel.copy()
|
||||
world.standardize_palettes = args.standardize_palettes.copy()
|
||||
|
||||
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
|
||||
|
||||
@@ -105,6 +112,13 @@ def main(args, seed=None, fish=None):
|
||||
create_doors(world, player)
|
||||
create_rooms(world, player)
|
||||
create_dungeons(world, player)
|
||||
adjust_locations(world, player)
|
||||
|
||||
if any(world.potshuffle.values()):
|
||||
logger.info(world.fish.translate("cli", "cli", "shuffling.pots"))
|
||||
for player in range(1, world.players + 1):
|
||||
if world.potshuffle[player]:
|
||||
shuffle_pots(world, player)
|
||||
|
||||
logger.info(world.fish.translate("cli","cli","shuffling.world"))
|
||||
|
||||
@@ -123,6 +137,7 @@ def main(args, seed=None, fish=None):
|
||||
else:
|
||||
mark_dark_world_regions(world, player)
|
||||
logger.info(world.fish.translate("cli","cli","generating.itempool"))
|
||||
logger.info(world.fish.translate("cli","cli","generating.itempool"))
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
generate_itempool(world, player)
|
||||
@@ -136,6 +151,9 @@ def main(args, seed=None, fish=None):
|
||||
|
||||
fill_prizes(world)
|
||||
|
||||
# used for debugging
|
||||
# fill_specific_items(world)
|
||||
|
||||
logger.info(world.fish.translate("cli","cli","placing.dungeon.items"))
|
||||
|
||||
shuffled_locations = None
|
||||
@@ -199,7 +217,10 @@ def main(args, seed=None, fish=None):
|
||||
sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit'
|
||||
use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none'
|
||||
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
||||
or args.shufflepots[player] or sprite_random_on_hit)
|
||||
or sprite_random_on_hit)
|
||||
|
||||
if use_enemizer:
|
||||
base_patch = LocalRom(args.rom) # update base2current.json
|
||||
|
||||
rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom)
|
||||
|
||||
@@ -207,7 +228,7 @@ def main(args, seed=None, fish=None):
|
||||
if args.rom and not(os.path.isfile(args.rom)):
|
||||
raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom)
|
||||
if os.path.exists(args.enemizercli):
|
||||
patch_enemizer(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player], sprite_random_on_hit)
|
||||
patch_enemizer(world, player, rom, args.rom, args.enemizercli, sprite_random_on_hit)
|
||||
enemized = True
|
||||
if not args.jsonout:
|
||||
rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000)
|
||||
@@ -330,6 +351,7 @@ def main(args, seed=None, fish=None):
|
||||
|
||||
return world
|
||||
|
||||
|
||||
def copy_world(world):
|
||||
# ToDo: Not good yet
|
||||
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
|
||||
@@ -363,6 +385,8 @@ def copy_world(world):
|
||||
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
||||
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
||||
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
|
||||
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
|
||||
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
|
||||
ret.open_pyramid = world.open_pyramid.copy()
|
||||
ret.boss_shuffle = world.boss_shuffle.copy()
|
||||
ret.enemy_shuffle = world.enemy_shuffle.copy()
|
||||
@@ -371,6 +395,9 @@ def copy_world(world):
|
||||
ret.beemizer = world.beemizer.copy()
|
||||
ret.intensity = world.intensity.copy()
|
||||
ret.experimental = world.experimental.copy()
|
||||
ret.keydropshuffle = world.keydropshuffle.copy()
|
||||
ret.mixed_travel = world.mixed_travel.copy()
|
||||
ret.standardize_palettes = world.standardize_palettes.copy()
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
if world.mode[player] != 'inverted':
|
||||
@@ -405,20 +432,26 @@ def copy_world(world):
|
||||
copied_region = ret.get_region(region.name, region.player)
|
||||
copied_region.is_light_world = region.is_light_world
|
||||
copied_region.is_dark_world = region.is_dark_world
|
||||
copied_region.dungeon = region.dungeon
|
||||
copied_region.locations = [Location(location.player, location.name, parent=copied_region) for location in region.locations]
|
||||
for entrance in region.entrances:
|
||||
ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
|
||||
|
||||
# fill locations
|
||||
for location in world.get_locations():
|
||||
new_location = ret.get_location(location.name, location.player)
|
||||
if location.item is not None:
|
||||
item = Item(location.item.name, location.item.advancement, location.item.priority, location.item.type, player = location.item.player)
|
||||
ret.get_location(location.name, location.player).item = item
|
||||
item.location = ret.get_location(location.name, location.player)
|
||||
new_location.item = item
|
||||
item.location = new_location
|
||||
item.world = ret
|
||||
if location.event:
|
||||
ret.get_location(location.name, location.player).event = True
|
||||
new_location.event = True
|
||||
if location.locked:
|
||||
ret.get_location(location.name, location.player).locked = True
|
||||
new_location.locked = True
|
||||
# these need to be modified properly by set_rules
|
||||
new_location.access_rule = lambda state: True
|
||||
new_location.item_rule = lambda state: True
|
||||
|
||||
# copy remaining itempool. No item in itempool should have an assigned location
|
||||
for item in world.itempool:
|
||||
@@ -441,6 +474,11 @@ def copy_world(world):
|
||||
ret.inaccessible_regions = world.inaccessible_regions
|
||||
ret.dungeon_layouts = world.dungeon_layouts
|
||||
ret.key_logic = world.key_logic
|
||||
ret.dungeon_portals = world.dungeon_portals
|
||||
for player, portals in world.dungeon_portals.items():
|
||||
for portal in portals:
|
||||
connect_portal_copy(portal, ret, player)
|
||||
ret.sanc_portal = world.sanc_portal
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
set_rules(ret, player)
|
||||
@@ -579,7 +617,7 @@ def create_playthrough(world):
|
||||
|
||||
old_world.spoiler.paths = dict()
|
||||
for player in range(1, world.players + 1):
|
||||
old_world.spoiler.paths.update({ str(location) : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player})
|
||||
old_world.spoiler.paths.update({location.gen_name(): get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player})
|
||||
for _, path in dict(old_world.spoiler.paths).items():
|
||||
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
|
||||
if world.mode[player] != 'inverted':
|
||||
@@ -590,4 +628,4 @@ def create_playthrough(world):
|
||||
# we can finally output our playthrough
|
||||
old_world.spoiler.playthrough = OrderedDict([("0", [str(item) for item in world.precollected_items if item.advancement])])
|
||||
for i, sphere in enumerate(collection_spheres):
|
||||
old_world.spoiler.playthrough[str(i + 1)] = {str(location): str(location.item) for location in sphere}
|
||||
old_world.spoiler.playthrough[str(i + 1)] = {location.gen_name(): str(location.item) for location in sphere}
|
||||
|
||||
+45
-9
@@ -126,23 +126,34 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
|
||||
'Desert Palace - Map Chest': (0x74, 0x10),
|
||||
'Desert Palace - Compass Chest': (0x85, 0x10),
|
||||
'Desert Palace - Big Key Chest': (0x75, 0x10),
|
||||
'Desert Palace - Desert Tiles 1 Pot Key': (0x63, 0x400),
|
||||
'Desert Palace - Beamos Hall Pot Key': (0x53, 0x400),
|
||||
'Desert Palace - Desert Tiles 2 Pot Key': (0x43, 0x400),
|
||||
'Desert Palace - Boss': (0x33, 0x800),
|
||||
'Eastern Palace - Compass Chest': (0xa8, 0x10),
|
||||
'Eastern Palace - Big Chest': (0xa9, 0x10),
|
||||
'Eastern Palace - Dark Square Pot Key': (0xba, 0x400),
|
||||
'Eastern Palace - Dark Eyegore Key Drop': (0x99, 0x400),
|
||||
'Eastern Palace - Cannonball Chest': (0xb9, 0x10),
|
||||
'Eastern Palace - Big Key Chest': (0xb8, 0x10),
|
||||
'Eastern Palace - Map Chest': (0xaa, 0x10),
|
||||
'Eastern Palace - Boss': (0xc8, 0x800),
|
||||
'Hyrule Castle - Boomerang Chest': (0x71, 0x10),
|
||||
'Hyrule Castle - Boomerang Guard Key Drop': (0x71, 0x400),
|
||||
'Hyrule Castle - Map Chest': (0x72, 0x10),
|
||||
'Hyrule Castle - Map Guard Key Drop': (0x72, 0x400),
|
||||
"Hyrule Castle - Zelda's Chest": (0x80, 0x10),
|
||||
'Hyrule Castle - Big Key Drop': (0x80, 0x400),
|
||||
'Sewers - Dark Cross': (0x32, 0x10),
|
||||
'Hyrule Castle - Key Rat Key Drop': (0x21, 0x400),
|
||||
'Sewers - Secret Room - Left': (0x11, 0x10),
|
||||
'Sewers - Secret Room - Middle': (0x11, 0x20),
|
||||
'Sewers - Secret Room - Right': (0x11, 0x40),
|
||||
'Sanctuary': (0x12, 0x10),
|
||||
'Castle Tower - Room 03': (0xe0, 0x10),
|
||||
'Castle Tower - Dark Maze': (0xd0, 0x10),
|
||||
'Castle Tower - Dark Archer Key Drop': (0xc0, 0x400),
|
||||
'Castle Tower - Circle of Pots Key Drop': (0xb0, 0x400),
|
||||
'Spectacle Rock Cave': (0xea, 0x400),
|
||||
'Paradox Cave Lower - Far Left': (0xef, 0x10),
|
||||
'Paradox Cave Lower - Left': (0xef, 0x20),
|
||||
@@ -181,18 +192,25 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
|
||||
'Mimic Cave': (0x10c, 0x10),
|
||||
'Swamp Palace - Entrance': (0x28, 0x10),
|
||||
'Swamp Palace - Map Chest': (0x37, 0x10),
|
||||
'Swamp Palace - Pot Row Pot Key': (0x38, 0x400),
|
||||
'Swamp Palace - Trench 1 Pot Key': (0x37, 0x400),
|
||||
'Swamp Palace - Hookshot Pot Key': (0x36, 0x400),
|
||||
'Swamp Palace - Big Chest': (0x36, 0x10),
|
||||
'Swamp Palace - Compass Chest': (0x46, 0x10),
|
||||
'Swamp Palace - Trench 2 Pot Key': (0x35, 0x400),
|
||||
'Swamp Palace - Big Key Chest': (0x35, 0x10),
|
||||
'Swamp Palace - West Chest': (0x34, 0x10),
|
||||
'Swamp Palace - Flooded Room - Left': (0x76, 0x10),
|
||||
'Swamp Palace - Flooded Room - Right': (0x76, 0x20),
|
||||
'Swamp Palace - Waterfall Room': (0x66, 0x10),
|
||||
'Swamp Palace - Waterway Pot Key': (0x16, 0x400),
|
||||
'Swamp Palace - Boss': (0x6, 0x800),
|
||||
"Thieves' Town - Big Key Chest": (0xdb, 0x20),
|
||||
"Thieves' Town - Map Chest": (0xdb, 0x10),
|
||||
"Thieves' Town - Compass Chest": (0xdc, 0x10),
|
||||
"Thieves' Town - Ambush Chest": (0xcb, 0x10),
|
||||
"Thieves' Town - Hallway Pot Key": (0xbc, 0x400),
|
||||
"Thieves' Town - Spike Switch Pot Key": (0xab, 0x400),
|
||||
"Thieves' Town - Attic": (0x65, 0x10),
|
||||
"Thieves' Town - Big Chest": (0x44, 0x10),
|
||||
"Thieves' Town - Blind's Cell": (0x45, 0x10),
|
||||
@@ -203,28 +221,39 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
|
||||
'Skull Woods - Pot Prison': (0x57, 0x20),
|
||||
'Skull Woods - Pinball Room': (0x68, 0x10),
|
||||
'Skull Woods - Big Key Chest': (0x57, 0x10),
|
||||
'Skull Woods - West Lobby Pot Key': (0x56, 0x400),
|
||||
'Skull Woods - Bridge Room': (0x59, 0x10),
|
||||
'Skull Woods - Spike Corner Key Drop': (0x39, 0x400),
|
||||
'Skull Woods - Boss': (0x29, 0x800),
|
||||
'Ice Palace - Jelly Key Drop': (0x0e, 0x400),
|
||||
'Ice Palace - Compass Chest': (0x2e, 0x10),
|
||||
'Ice Palace - Conveyor Key Drop': (0x3e, 0x400),
|
||||
'Ice Palace - Freezor Chest': (0x7e, 0x10),
|
||||
'Ice Palace - Big Chest': (0x9e, 0x10),
|
||||
'Ice Palace - Iced T Room': (0xae, 0x10),
|
||||
'Ice Palace - Many Pots Pot Key': (0x9f, 0x400),
|
||||
'Ice Palace - Spike Room': (0x5f, 0x10),
|
||||
'Ice Palace - Big Key Chest': (0x1f, 0x10),
|
||||
'Ice Palace - Hammer Block Key Drop': (0x3f, 0x400),
|
||||
'Ice Palace - Map Chest': (0x3f, 0x10),
|
||||
'Ice Palace - Boss': (0xde, 0x800),
|
||||
'Misery Mire - Big Chest': (0xc3, 0x10),
|
||||
'Misery Mire - Map Chest': (0xc3, 0x20),
|
||||
'Misery Mire - Main Lobby': (0xc2, 0x10),
|
||||
'Misery Mire - Bridge Chest': (0xa2, 0x10),
|
||||
'Misery Mire - Spikes Pot Key': (0xb3, 0x400),
|
||||
'Misery Mire - Spike Chest': (0xb3, 0x10),
|
||||
'Misery Mire - Fishbone Pot Key': (0xa1, 0x400),
|
||||
'Misery Mire - Conveyor Crystal Key Drop': (0xc1, 0x400),
|
||||
'Misery Mire - Compass Chest': (0xc1, 0x10),
|
||||
'Misery Mire - Big Key Chest': (0xd1, 0x10),
|
||||
'Misery Mire - Boss': (0x90, 0x800),
|
||||
'Turtle Rock - Compass Chest': (0xd6, 0x10),
|
||||
'Turtle Rock - Roller Room - Left': (0xb7, 0x10),
|
||||
'Turtle Rock - Roller Room - Right': (0xb7, 0x20),
|
||||
'Turtle Rock - Pokey 1 Key Drop': (0xb6, 0x400),
|
||||
'Turtle Rock - Chain Chomps': (0xb6, 0x10),
|
||||
'Turtle Rock - Pokey 2 Key Drop': (0x13, 0x400),
|
||||
'Turtle Rock - Big Key Chest': (0x14, 0x10),
|
||||
'Turtle Rock - Big Chest': (0x24, 0x10),
|
||||
'Turtle Rock - Crystaroller Room': (0x4, 0x10),
|
||||
@@ -247,6 +276,7 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
|
||||
'Palace of Darkness - Big Chest': (0x1a, 0x10),
|
||||
'Palace of Darkness - Harmless Hellway': (0x1a, 0x40),
|
||||
'Palace of Darkness - Boss': (0x5a, 0x800),
|
||||
'Ganons Tower - Conveyor Cross Pot Key': (0x8b, 0x400),
|
||||
"Ganons Tower - Bob's Torch": (0x8c, 0x400),
|
||||
'Ganons Tower - Hope Room - Left': (0x8c, 0x20),
|
||||
'Ganons Tower - Hope Room - Right': (0x8c, 0x40),
|
||||
@@ -255,11 +285,13 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
|
||||
'Ganons Tower - Compass Room - Top Right': (0x9d, 0x20),
|
||||
'Ganons Tower - Compass Room - Bottom Left': (0x9d, 0x40),
|
||||
'Ganons Tower - Compass Room - Bottom Right': (0x9d, 0x80),
|
||||
'Ganons Tower - Conveyor Star Pits Pot Key': (0x7b, 0x400),
|
||||
'Ganons Tower - DMs Room - Top Left': (0x7b, 0x10),
|
||||
'Ganons Tower - DMs Room - Top Right': (0x7b, 0x20),
|
||||
'Ganons Tower - DMs Room - Bottom Left': (0x7b, 0x40),
|
||||
'Ganons Tower - DMs Room - Bottom Right': (0x7b, 0x80),
|
||||
'Ganons Tower - Map Chest': (0x8b, 0x10),
|
||||
'Ganons Tower - Double Switch Pot Key': (0x9b, 0x400),
|
||||
'Ganons Tower - Firesnake Room': (0x7d, 0x10),
|
||||
'Ganons Tower - Randomizer Room - Top Left': (0x7c, 0x10),
|
||||
'Ganons Tower - Randomizer Room - Top Right': (0x7c, 0x20),
|
||||
@@ -272,6 +304,7 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
|
||||
'Ganons Tower - Big Key Chest': (0x1c, 0x10),
|
||||
'Ganons Tower - Mini Helmasaur Room - Left': (0x3d, 0x10),
|
||||
'Ganons Tower - Mini Helmasaur Room - Right': (0x3d, 0x20),
|
||||
'Ganons Tower - Mini Helmasaur Key Drop': (0x3d, 0x400),
|
||||
'Ganons Tower - Pre-Moldorm Chest': (0x3d, 0x40),
|
||||
'Ganons Tower - Validation Chest': (0x4d, 0x10)}
|
||||
location_table_npc = {'Mushroom': 0x1000,
|
||||
@@ -630,7 +663,7 @@ async def process_server_cmd(ctx : Context, cmd, args):
|
||||
ctx.player_names = {p: n for p, n in args[1]}
|
||||
msgs = []
|
||||
if ctx.locations_checked:
|
||||
msgs.append(['LocationChecks', [Regions.location_table[loc][0] for loc in ctx.locations_checked]])
|
||||
msgs.append(['LocationChecks', [Regions.lookup_name_to_id[loc] for loc in ctx.locations_checked]])
|
||||
if ctx.locations_scouted:
|
||||
msgs.append(['LocationScouts', list(ctx.locations_scouted)])
|
||||
if msgs:
|
||||
@@ -643,7 +676,7 @@ async def process_server_cmd(ctx : Context, cmd, args):
|
||||
elif start_index != len(ctx.items_received):
|
||||
sync_msg = [['Sync']]
|
||||
if ctx.locations_checked:
|
||||
sync_msg.append(['LocationChecks', [Regions.location_table[loc][0] for loc in ctx.locations_checked]])
|
||||
sync_msg.append(['LocationChecks', [Regions.lookup_name_to_id[loc] for loc in ctx.locations_checked]])
|
||||
await send_msgs(ctx.socket, sync_msg)
|
||||
if start_index == len(ctx.items_received):
|
||||
for item in items:
|
||||
@@ -655,7 +688,7 @@ async def process_server_cmd(ctx : Context, cmd, args):
|
||||
if location not in ctx.locations_info:
|
||||
replacements = {0xA2: 'Small Key', 0x9D: 'Big Key', 0x8D: 'Compass', 0x7D: 'Map'}
|
||||
item_name = replacements.get(item, get_item_name_from_id(item))
|
||||
logging.info(f"Saw {color(item_name, 'red', 'bold')} at {list(Regions.location_table.keys())[location - 1]}")
|
||||
logging.info(f"Saw {color(item_name, 'red', 'bold')} at {list(Regions.lookup_id_to_name.keys())[location - 1]}")
|
||||
ctx.locations_info[location] = (item, player)
|
||||
ctx.watcher_event.set()
|
||||
|
||||
@@ -736,7 +769,7 @@ async def console_loop(ctx : Context):
|
||||
get_location_name_from_address(item.location), index, len(ctx.items_received)))
|
||||
|
||||
if command[0] == '/missing':
|
||||
for location in [k for k, v in Regions.location_table.items() if type(v[0]) is int]:
|
||||
for location in [k for k, v in Regions.lookup_name_to_id.items() if type(v) is int]:
|
||||
if location not in ctx.locations_checked:
|
||||
logging.info('Missing: ' + location)
|
||||
if command[0] == '/getitem' and len(command) > 1:
|
||||
@@ -751,23 +784,26 @@ async def console_loop(ctx : Context):
|
||||
|
||||
await snes_flush_writes(ctx)
|
||||
|
||||
|
||||
def get_item_name_from_id(code):
|
||||
items = [k for k, i in Items.item_table.items() if type(i[3]) is int and i[3] == code]
|
||||
return items[0] if items else 'Unknown item'
|
||||
return items[0] if items else f'Unknown item (ID:{code})'
|
||||
|
||||
|
||||
def get_location_name_from_address(address):
|
||||
if type(address) is str:
|
||||
return address
|
||||
|
||||
locs = [k for k, l in Regions.location_table.items() if type(l[0]) is int and l[0] == address]
|
||||
return locs[0] if locs else 'Unknown location'
|
||||
return Regions.lookup_id_to_name.get(address, f'Unknown location (ID:{address})')
|
||||
|
||||
|
||||
async def track_locations(ctx : Context, roomid, roomdata):
|
||||
new_locations = []
|
||||
|
||||
def new_check(location):
|
||||
ctx.locations_checked.add(location)
|
||||
logging.info("New check: %s (%d/216)" % (location, len(ctx.locations_checked)))
|
||||
new_locations.append(Regions.location_table[location][0])
|
||||
new_locations.append(Regions.lookup_name_to_id[location])
|
||||
|
||||
for location, (loc_roomid, loc_mask) in location_table_uw.items():
|
||||
if location not in ctx.locations_checked and loc_roomid == roomid and (roomdata << 4) & loc_mask != 0:
|
||||
@@ -882,7 +918,7 @@ async def game_watcher(ctx : Context):
|
||||
|
||||
if scout_location > 0 and scout_location not in ctx.locations_scouted:
|
||||
ctx.locations_scouted.add(scout_location)
|
||||
logging.info(f'Scouting item at {list(Regions.location_table.keys())[scout_location - 1]}')
|
||||
logging.info(f'Scouting item at {list(Regions.lookup_id_to_name.keys())[scout_location - 1]}')
|
||||
await send_msgs(ctx.socket, [['LocationScouts', [scout_location]]])
|
||||
await track_locations(ctx, roomid, roomdata)
|
||||
|
||||
|
||||
+5
-4
@@ -154,7 +154,8 @@ def send_new_items(ctx : Context):
|
||||
client.send_index = len(items)
|
||||
|
||||
def forfeit_player(ctx : Context, team, slot):
|
||||
all_locations = [values[0] for values in Regions.location_table.values() if type(values[0]) is int]
|
||||
all_locations = {values[0] for values in Regions.location_table.values() if type(values[0]) is int}
|
||||
all_locations.update({values[1] for values in Regions.key_drop_data.values()})
|
||||
notify_all(ctx, "%s (Team #%d) has forfeited" % (ctx.player_names[(team, slot)], team + 1))
|
||||
register_location_checks(ctx, team, slot, all_locations)
|
||||
|
||||
@@ -248,11 +249,11 @@ async def process_client_cmd(ctx : Context, client : Client, cmd, args):
|
||||
return
|
||||
locs = []
|
||||
for location in args:
|
||||
if type(location) is not int or 0 >= location > len(Regions.location_table):
|
||||
if type(location) is not int or 0 >= location > len(Regions.lookup_id_to_name.keys()):
|
||||
await send_msgs(client.socket, [['InvalidArguments', 'LocationScouts']])
|
||||
return
|
||||
loc_name = list(Regions.location_table.keys())[location - 1]
|
||||
target_item, target_player = ctx.locations[(Regions.location_table[loc_name][0], client.slot)]
|
||||
loc_name = list(Regions.lookup_id_to_name.keys())[location - 1]
|
||||
target_item, target_player = ctx.locations[(Regions.lookup_name_to_id[loc_name], client.slot)]
|
||||
|
||||
replacements = {'SmallKey': 0xA2, 'BigKey': 0x9D, 'Compass': 0x8D, 'Map': 0x7D}
|
||||
item_type = [i[2] for i in Items.item_table.values() if type(i[3]) is int and i[3] == target_item]
|
||||
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from BaseClasses import PotItem, Pot, PotFlags, CrystalBarrier
|
||||
from Regions import key_drop_data
|
||||
|
||||
movable_switch_rooms = defaultdict(lambda: [],
|
||||
{'PoD Stalfos Basement': ['PoD Basement Ledge'],
|
||||
'Thieves Attic': ['Thieves Attic Hint'],
|
||||
'Mire Hub Switch': ['Mire Hub', 'Mire Hub Right']})
|
||||
|
||||
invalid_key_rooms = {
|
||||
'Swamp Big Key Ledge',
|
||||
'Swamp Trench 2 Departure',
|
||||
'Desert Wall Slide',
|
||||
'Skull X Room', # only required for 100% locations
|
||||
'GT Map Room',
|
||||
'GT Warp Maze - Mid Section'
|
||||
}
|
||||
|
||||
vanilla_pots = {
|
||||
2: [Pot(80, 6, PotItem.Nothing, 'Sewers Yet More Rats'), Pot(80, 8, PotItem.Nothing, 'Sewers Yet More Rats'), Pot(44, 8, PotItem.Nothing, 'Sewers Yet More Rats'), Pot(44, 10, PotItem.Nothing, 'Sewers Yet More Rats')],
|
||||
4: [Pot(162, 25, PotItem.Nothing, 'TR Dash Room'), Pot(152, 25, PotItem.Nothing, 'TR Dash Room'), Pot(152, 22, PotItem.Nothing, 'TR Dash Room'), Pot(162, 22, PotItem.Nothing, 'TR Dash Room'), Pot(204, 19, PotItem.Bomb, 'TR Tongue Pull'),
|
||||
Pot(240, 19, PotItem.Bomb, 'TR Tongue Pull')],
|
||||
9: [Pot(12, 4, PotItem.OneRupee, 'PoD Shooter Room'), Pot(48, 4, PotItem.Heart, 'PoD Shooter Room'), Pot(12, 12, PotItem.Switch, 'PoD Shooter Room')],
|
||||
10: [Pot(96, 8, PotItem.Heart, 'PoD Stalfos Basement'), Pot(104, 8, PotItem.Heart, 'PoD Stalfos Basement'), Pot(204, 11, PotItem.Switch, 'PoD Stalfos Basement'), Pot(100, 9, PotItem.Nothing, 'PoD Stalfos Basement'),
|
||||
Pot(156, 17, PotItem.Bomb, 'PoD Basement Ledge', PotFlags.SwitchLogicChange), Pot(160, 17, PotItem.FiveArrows, 'PoD Basement Ledge', PotFlags.SwitchLogicChange)],
|
||||
11: [Pot(202, 3, PotItem.Bomb, 'PoD Dark Pegs'), Pot(202, 12, PotItem.Bomb, 'PoD Dark Pegs')],
|
||||
17: [Pot(152, 19, PotItem.Nothing, 'Sewers Secret Room'), Pot(152, 15, PotItem.Nothing, 'Sewers Secret Room'), Pot(144, 15, PotItem.Heart, 'Sewers Secret Room'), Pot(160, 15, PotItem.Heart, 'Sewers Secret Room'),
|
||||
Pot(144, 19, PotItem.Heart, 'Sewers Secret Room'), Pot(160, 19, PotItem.Heart, 'Sewers Secret Room')],
|
||||
21: [Pot(96, 4, PotItem.Bomb, 'TR Pipe Pit'), Pot(100, 4, PotItem.SmallMagic, 'TR Pipe Pit'), Pot(104, 4, PotItem.Heart, 'TR Pipe Pit'), Pot(108, 4, PotItem.SmallMagic, 'TR Pipe Pit'), Pot(112, 4, PotItem.FiveArrows, 'TR Pipe Pit'),
|
||||
Pot(12, 6, PotItem.OneRupee, 'TR Pipe Pit'), Pot(16, 6, PotItem.FiveArrows, 'TR Pipe Pit'), Pot(20, 6, PotItem.FiveRupees, 'TR Pipe Pit'), Pot(70, 11, PotItem.BigMagic, 'TR Pipe Ledge')],
|
||||
22: [Pot(188, 3, PotItem.Heart, 'Swamp I'), Pot(192, 3, PotItem.Heart, 'Swamp I'), Pot(188, 4, PotItem.SmallMagic, 'Swamp I'), Pot(192, 4, PotItem.SmallMagic, 'Swamp I'), Pot(188, 5, PotItem.FiveArrows, 'Swamp I'),
|
||||
Pot(192, 5, PotItem.FiveArrows, 'Swamp I'), Pot(188, 6, PotItem.Bomb, 'Swamp I'), Pot(192, 6, PotItem.Bomb, 'Swamp I'), Pot(240, 19, PotItem.Key, 'Swamp Waterway')],
|
||||
23: [Pot(100, 13, PotItem.Heart, 'Hera 5F'), Pot(100, 14, PotItem.Heart, 'Hera 5F'), Pot(100, 15, PotItem.Heart, 'Hera 5F'), Pot(100, 16, PotItem.Heart, 'Hera 5F'), Pot(100, 17, PotItem.Heart, 'Hera 5F'), Pot(100, 18, PotItem.Heart, 'Hera 5F'),
|
||||
Pot(104, 13, PotItem.Heart, 'Hera 5F'), Pot(104, 14, PotItem.Heart, 'Hera 5F'), Pot(104, 15, PotItem.Heart, 'Hera 5F'), Pot(104, 16, PotItem.Heart, 'Hera 5F'), Pot(104, 17, PotItem.Heart, 'Hera 5F'), Pot(104, 18, PotItem.Heart, 'Hera 5F')],
|
||||
26: [Pot(28, 5, PotItem.Bomb, 'PoD Falling Bridge Ledge'), Pot(32, 5, PotItem.Bomb, 'PoD Falling Bridge Ledge'), Pot(28, 27, PotItem.Bomb, 'PoD Falling Bridge'), Pot(32, 27, PotItem.Bomb, 'PoD Falling Bridge'),
|
||||
Pot(232, 19, PotItem.Nothing, 'PoD Harmless Hellway'), Pot(212, 19, PotItem.Nothing, 'PoD Harmless Hellway')],
|
||||
27: [Pot(20, 23, PotItem.FiveArrows, 'PoD Mimics 2'), Pot(40, 23, PotItem.FiveArrows, 'PoD Mimics 2')],
|
||||
30: [Pot(84, 9, PotItem.Bomb, 'Ice Bomb Drop')],
|
||||
31: [Pot(28, 25, PotItem.Switch, 'Ice Pengator Switch'), Pot(28, 23, PotItem.Nothing, 'Ice Pengator Switch'), Pot(86, 26, PotItem.Nothing, 'Ice Big Key'), Pot(86, 27, PotItem.Nothing, 'Ice Big Key')],
|
||||
33: [Pot(160, 20, PotItem.Nothing, 'Sewers Key Rat'), Pot(168, 24, PotItem.SmallMagic, 'Sewers Key Rat'), Pot(48, 28, PotItem.Heart, 'Sewers Key Rat'), Pot(82, 28, PotItem.SmallMagic, 'Sewers Key Rat'),
|
||||
Pot(100, 28, PotItem.Nothing, 'Sewers Key Rat'), Pot(104, 28, PotItem.Nothing, 'Sewers Key Rat')],
|
||||
35: [Pot(86, 26, PotItem.OneRupee, 'TR Lazy Eyes'), Pot(90, 26, PotItem.Heart, 'TR Lazy Eyes'), Pot(94, 26, PotItem.OneRupee, 'TR Lazy Eyes'), Pot(98, 26, PotItem.Bomb, 'TR Lazy Eyes'), Pot(102, 26, PotItem.OneRupee, 'TR Lazy Eyes')],
|
||||
36: [Pot(12, 4, PotItem.FiveRupees, 'TR Twin Pokeys'), Pot(48, 4, PotItem.Heart, 'TR Twin Pokeys'), Pot(12, 12, PotItem.SmallMagic, 'TR Twin Pokeys'), Pot(48, 12, PotItem.OneRupee, 'TR Twin Pokeys')],
|
||||
38: [Pot(28, 4, PotItem.Bomb, 'Swamp Shooters'), Pot(12, 8, PotItem.SmallMagic, 'Swamp Shooters'), Pot(150, 19, PotItem.Switch, 'Swamp Push Statue'), Pot(22, 26, PotItem.FiveRupees, 'Swamp Push Statue'),
|
||||
Pot(220, 26, PotItem.FiveArrows, 'Swamp Push Statue', PotFlags.SwitchLogicChange)],
|
||||
39: [Pot(214, 19, PotItem.Nothing, 'Hera 4F'), Pot(214, 20, PotItem.Nothing, 'Hera 4F'), Pot(166, 20, PotItem.Bomb, 'Hera 4F'), Pot(214, 21, PotItem.Heart, 'Hera 4F'), Pot(40, 28, PotItem.OneRupee, 'Hera 4F'),
|
||||
Pot(44, 28, PotItem.OneRupee, 'Hera 4F'), Pot(80, 28, PotItem.FiveRupees, 'Hera 4F'), Pot(84, 28, PotItem.FiveRupees, 'Hera 4F'), Pot(102, 17, PotItem.Nothing, 'Hera 4F'), Pot(98, 17, PotItem.Nothing, 'Hera 4F'),
|
||||
Pot(106, 17, PotItem.Nothing, 'Hera 4F'), Pot(166, 21, PotItem.Nothing, 'Hera 4F'), Pot(166, 19, PotItem.Nothing, 'Hera 4F'), Pot(92, 12, PotItem.Nothing, 'Hera 4F'), Pot(160, 12, PotItem.Nothing, 'Hera 4F')],
|
||||
42: [Pot(80, 12, PotItem.OneRupee, 'PoD Arena Main'), Pot(80, 19, PotItem.Heart, 'PoD Arena Main')],
|
||||
43: [Pot(16, 5, PotItem.Heart, 'PoD Sexy Statue'), Pot(44, 5, PotItem.Switch, 'PoD Sexy Statue'), Pot(16, 6, PotItem.Heart, 'PoD Sexy Statue'), Pot(44, 6, PotItem.Bomb, 'PoD Sexy Statue'), Pot(16, 7, PotItem.Heart, 'PoD Sexy Statue'),
|
||||
Pot(44, 7, PotItem.Bomb, 'PoD Sexy Statue'), Pot(146, 21, PotItem.Bomb, 'PoD Map Balcony'), Pot(170, 21, PotItem.FiveArrows, 'PoD Map Balcony'), Pot(146, 22, PotItem.Bomb, 'PoD Map Balcony'),
|
||||
Pot(170, 22, PotItem.FiveArrows, 'PoD Map Balcony')],
|
||||
44: [Pot(108, 24, PotItem.Heart, 'Hookshot Cave'), Pot(112, 24, PotItem.Heart, 'Hookshot Cave')],
|
||||
47: [Pot(28, 7, PotItem.Heart, 'Kakariko Well (top)'), Pot(32, 7, PotItem.Heart, 'Kakariko Well (top)'), Pot(28, 9, PotItem.FiveRupees, 'Kakariko Well (top)'), Pot(32, 9, PotItem.FiveRupees, 'Kakariko Well (top)'),
|
||||
Pot(172, 19, PotItem.FiveRupees, 'Kakariko Well (top)'), Pot(180, 19, PotItem.FiveRupees, 'Kakariko Well (top)'), Pot(104, 27, PotItem.Heart, 'Kakariko Well (bottom)'), Pot(104, 28, PotItem.Heart, 'Kakariko Well (bottom)')],
|
||||
49: [Pot(92, 28, PotItem.Bomb, 'Hera Beetles'), Pot(96, 28, PotItem.Nothing, 'Hera Beetles')],
|
||||
50: [Pot(28, 13, PotItem.SmallMagic, 'Sewers Dark Cross')],
|
||||
52: [Pot(78, 8, PotItem.FiveRupees, 'Swamp Barrier Ledge'), Pot(92, 8, PotItem.FiveRupees, 'Swamp Barrier Ledge')],
|
||||
53: [Pot(60, 6, PotItem.Key, 'Swamp Trench 2 Alcove'), Pot(20, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(24, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(28, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'),
|
||||
Pot(32, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(36, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(48, 20, PotItem.Heart, 'Swamp Trench 2 Departure'), Pot(76, 23, PotItem.Nothing, 'Swamp Trench 2 Pots'),
|
||||
Pot(88, 23, PotItem.Nothing, 'Swamp Trench 2 Pots'), Pot(100, 27, PotItem.Nothing, 'Swamp Trench 2 Pots'), Pot(242, 28, PotItem.Nothing, 'Swamp Trench 2 Pots'), Pot(240, 22, PotItem.Heart, 'Swamp Trench 2 Pots'),
|
||||
Pot(76, 28, PotItem.Heart, 'Swamp Trench 2 Pots')],
|
||||
54: [Pot(108, 4, PotItem.Bomb, 'Swamp Hub Dead Ledge'), Pot(112, 4, PotItem.FiveRupees, 'Swamp Hub Dead Ledge'), Pot(10, 16, PotItem.Heart, 'Swamp Hub'), Pot(154, 15, PotItem.Nothing, 'Swamp Hub'), Pot(114, 16, PotItem.Key, 'Swamp Hub'),
|
||||
Pot(222, 15, PotItem.Nothing, 'Swamp Hub'), Pot(188, 5, PotItem.Nothing, 'Swamp Hub North Ledge'), Pot(192, 5, PotItem.Nothing, 'Swamp Hub North Ledge')],
|
||||
55: [Pot(60, 6, PotItem.Key, 'Swamp Trench 1 Alcove'), Pot(48, 20, PotItem.Nothing, 'Swamp Trench 1 Key Ledge')],
|
||||
56: [Pot(164, 12, PotItem.Bomb, 'Swamp Pot Row'), Pot(164, 13, PotItem.FiveRupees, 'Swamp Pot Row'), Pot(164, 18, PotItem.Bomb, 'Swamp Pot Row'), Pot(164, 19, PotItem.Key, 'Swamp Pot Row')],
|
||||
57: [Pot(12, 20, PotItem.Heart, 'Skull Spike Corner'), Pot(48, 28, PotItem.FiveArrows, 'Skull Spike Corner'), Pot(100, 22, PotItem.SmallMagic, 'Skull Final Drop'), Pot(100, 26, PotItem.FiveArrows, 'Skull Final Drop')],
|
||||
60: [Pot(24, 8, PotItem.SmallMagic, 'Hookshot Cave'), Pot(64, 12, PotItem.FiveRupees, 'Hookshot Cave'), Pot(20, 14, PotItem.OneRupee, 'Hookshot Cave'), Pot(20, 19, PotItem.Nothing, 'Hookshot Cave'),
|
||||
Pot(68, 18, PotItem.FiveRupees, 'Hookshot Cave'), Pot(96, 19, PotItem.Heart, 'Hookshot Cave'), Pot(64, 20, PotItem.FiveRupees, 'Hookshot Cave'), Pot(64, 26, PotItem.FiveRupees, 'Hookshot Cave')],
|
||||
61: [Pot(76, 12, PotItem.Bomb, 'GT Mini Helmasaur Room'), Pot(112, 12, PotItem.Bomb, 'GT Mini Helmasaur Room'), Pot(24, 22, PotItem.Heart, 'GT Crystal Circles'), Pot(40, 22, PotItem.FiveArrows, 'GT Crystal Circles'),
|
||||
Pot(32, 24, PotItem.Heart, 'GT Crystal Circles'), Pot(20, 26, PotItem.FiveRupees, 'GT Crystal Circles'), Pot(36, 26, PotItem.BigMagic, 'GT Crystal Circles')],
|
||||
62: [Pot(96, 6, PotItem.Bomb, 'Ice Stalfos Hint'), Pot(100, 6, PotItem.SmallMagic, 'Ice Stalfos Hint'), Pot(88, 10, PotItem.Heart, 'Ice Stalfos Hint'), Pot(92, 10, PotItem.SmallMagic, 'Ice Stalfos Hint')],
|
||||
63: [Pot(12, 25, PotItem.OneRupee, 'Ice Hammer Block'), Pot(20, 25, PotItem.OneRupee, 'Ice Hammer Block'), Pot(12, 26, PotItem.Bomb, 'Ice Hammer Block'), Pot(20, 26, PotItem.Bomb, 'Ice Hammer Block'),
|
||||
Pot(12, 27, PotItem.Switch, 'Ice Hammer Block'), Pot(20, 27, PotItem.Heart, 'Ice Hammer Block'), Pot(28, 23, PotItem.Key, 'Ice Hammer Block')],
|
||||
65: [Pot(100, 10, PotItem.Heart, 'Sewers Behind Tapestry'), Pot(52, 15, PotItem.OneRupee, 'Sewers Behind Tapestry'), Pot(52, 16, PotItem.SmallMagic, 'Sewers Behind Tapestry'), Pot(148, 22, PotItem.SmallMagic, 'Sewers Behind Tapestry')],
|
||||
67: [Pot(66, 4, PotItem.FiveArrows, 'Desert Wall Slide'), Pot(78, 4, PotItem.SmallMagic, 'Desert Wall Slide'), Pot(66, 9, PotItem.Heart, 'Desert Wall Slide'), Pot(78, 9, PotItem.Heart, 'Desert Wall Slide'),
|
||||
Pot(112, 28, PotItem.Nothing, 'Desert Tiles 2'), Pot(76, 28, PotItem.Nothing, 'Desert Tiles 2'), Pot(76, 20, PotItem.Nothing, 'Desert Tiles 2'), Pot(112, 20, PotItem.Key, 'Desert Tiles 2')],
|
||||
69: [Pot(12, 4, PotItem.FiveArrows, 'Thieves Basement Block'), Pot(48, 12, PotItem.FiveArrows, 'Thieves Basement Block'), Pot(92, 11, PotItem.Nothing, "Thieves Blind's Cell"), Pot(108, 11, PotItem.Heart, "Thieves Blind's Cell"),
|
||||
Pot(220, 16, PotItem.SmallMagic, "Thieves Blind's Cell"), Pot(236, 16, PotItem.Heart, "Thieves Blind's Cell")],
|
||||
70: [Pot(96, 5, PotItem.Heart, 'Swamp Donut Top'), Pot(28, 27, PotItem.Heart, 'Swamp Donut Bottom')],
|
||||
73: [Pot(104, 15, PotItem.SmallMagic, 'Skull Torch Room'), Pot(104, 16, PotItem.SmallMagic, 'Skull Torch Room'), Pot(156, 27, PotItem.Nothing, 'Skull Star Pits'), Pot(172, 24, PotItem.Nothing, 'Skull Star Pits'),
|
||||
Pot(172, 23, PotItem.Nothing, 'Skull Star Pits'), Pot(144, 20, PotItem.Nothing, 'Skull Star Pits'), Pot(144, 19, PotItem.SmallMagic, 'Skull Star Pits'), Pot(172, 20, PotItem.Heart, 'Skull Star Pits'),
|
||||
Pot(144, 27, PotItem.Heart, 'Skull Star Pits'), Pot(172, 28, PotItem.SmallMagic, 'Skull Star Pits'), Pot(160, 27, PotItem.Nothing, 'Skull Star Pits')],
|
||||
74: [Pot(14, 5, PotItem.Switch, 'PoD Left Cage'), Pot(32, 5, PotItem.Bomb, 'PoD Left Cage'), Pot(14, 11, PotItem.Heart, 'PoD Left Cage'), Pot(32, 11, PotItem.OneRupee, 'PoD Left Cage'), Pot(56, 8, PotItem.Bomb, 'PoD Middle Cage'),
|
||||
Pot(68, 8, PotItem.Bomb, 'PoD Middle Cage'), Pot(92, 5, PotItem.Bomb, 'PoD Middle Cage'), Pot(110, 5, PotItem.Switch, 'PoD Middle Cage'), Pot(92, 11, PotItem.OneRupee, 'PoD Middle Cage'), Pot(110, 11, PotItem.Heart, 'PoD Middle Cage')],
|
||||
75: [Pot(20, 6, PotItem.FiveArrows, 'PoD Mimics 1'), Pot(40, 6, PotItem.Heart, 'PoD Mimics 1')],
|
||||
78: [Pot(140, 7, PotItem.Nothing, 'Ice Bomb Jump Catwalk'), Pot(48, 10, PotItem.Nothing, 'Ice Bomb Jump Catwalk'), Pot(140, 11, PotItem.Switch, 'Ice Bomb Jump Catwalk'), Pot(28, 12, PotItem.Heart, 'Ice Bomb Jump Catwalk'),
|
||||
Pot(112, 12, PotItem.SmallMagic, 'Ice Narrow Corridor')],
|
||||
80: [Pot(96, 38, PotItem.Heart, 'Hyrule Castle West Hall'), Pot(100, 38, PotItem.Heart, 'Hyrule Castle West Hall')],
|
||||
82: [Pot(138, 3, PotItem.Heart, 'Hyrule Castle East Hall'), Pot(194, 26, PotItem.Heart, 'Hyrule Castle East Hall')],
|
||||
83: [Pot(92, 11, PotItem.Heart, 'Desert Beamos Hall'), Pot(96, 11, PotItem.SmallMagic, 'Desert Beamos Hall'), Pot(100, 11, PotItem.Key, 'Desert Beamos Hall'), Pot(104, 11, PotItem.Heart, 'Desert Beamos Hall')],
|
||||
84: [Pot(186, 25, PotItem.FiveRupees, 'Swamp Attic'), Pot(186, 26, PotItem.Heart, 'Swamp Attic'), Pot(186, 27, PotItem.Heart, 'Swamp Attic'), Pot(186, 28, PotItem.Heart, 'Swamp Attic')],
|
||||
85: [Pot(230, 24, PotItem.SmallMagic, 'Secret Passage'), Pot(230, 25, PotItem.SmallMagic, 'Secret Passage')],
|
||||
86: [Pot(100, 6, PotItem.Nothing, 'Skull Back Drop'), Pot(96, 10, PotItem.Nothing, 'Skull Back Drop'), Pot(92, 10, PotItem.Nothing, 'Skull Back Drop'), Pot(20, 6, PotItem.SmallMagic, 'Skull X Room'),
|
||||
Pot(40, 6, PotItem.SmallMagic, 'Skull X Room'), Pot(24, 7, PotItem.SmallMagic, 'Skull X Room'), Pot(36, 7, PotItem.SmallMagic, 'Skull X Room'), Pot(12, 8, PotItem.Heart, 'Skull X Room'), Pot(48, 8, PotItem.Heart, 'Skull X Room'),
|
||||
Pot(24, 9, PotItem.SmallMagic, 'Skull X Room'), Pot(36, 9, PotItem.SmallMagic, 'Skull X Room'), Pot(20, 10, PotItem.FiveRupees, 'Skull X Room'), Pot(40, 10, PotItem.FiveRupees, 'Skull X Room'), Pot(12, 20, PotItem.Key, 'Skull 2 West Lobby'),
|
||||
Pot(48, 20, PotItem.Nothing, 'Skull 2 West Lobby')],
|
||||
87: [Pot(92, 7, PotItem.BigMagic, 'Skull Lone Pot'), Pot(32, 4, PotItem.Nothing, 'Skull Big Key'), Pot(92, 23, PotItem.Bomb, 'Skull Pot Prison'), Pot(100, 23, PotItem.SmallMagic, 'Skull Pot Prison'),
|
||||
Pot(84, 25, PotItem.FiveRupees, 'Skull Pot Prison'), Pot(76, 27, PotItem.Heart, 'Skull Pot Prison'), Pot(12, 20, PotItem.SmallMagic, 'Skull 2 East Lobby'), Pot(48, 20, PotItem.SmallMagic, 'Skull 2 East Lobby'),
|
||||
Pot(30, 22, PotItem.Switch, 'Skull 2 East Lobby')],
|
||||
88: [Pot(12, 7, PotItem.SmallMagic, 'Skull Pull Switch'), Pot(16, 7, PotItem.Nothing, 'Skull Pull Switch'), Pot(16, 8, PotItem.SmallMagic, 'Skull Pull Switch'), Pot(12, 12, PotItem.Nothing, 'Skull Pull Switch'),
|
||||
Pot(96, 9, PotItem.Nothing, 'Skull Pot Circle'), Pot(92, 8, PotItem.Nothing, 'Skull Pot Circle'), Pot(108, 8, PotItem.Nothing, 'Skull Pot Circle'), Pot(108, 6, PotItem.Nothing, 'Skull Pot Circle'),
|
||||
Pot(104, 5, PotItem.Nothing, 'Skull Pot Circle'), Pot(92, 6, PotItem.Nothing, 'Skull Pot Circle'), Pot(96, 5, PotItem.Bomb, 'Skull Pot Circle'), Pot(100, 5, PotItem.SmallMagic, 'Skull Pot Circle'),
|
||||
Pot(92, 7, PotItem.Heart, 'Skull Pot Circle'), Pot(108, 7, PotItem.Heart, 'Skull Pot Circle'), Pot(100, 9, PotItem.SmallMagic, 'Skull Pot Circle'), Pot(104, 9, PotItem.Bomb, 'Skull Pot Circle')],
|
||||
89: [Pot(26, 43, PotItem.Heart, 'Skull 3 Lobby'), Pot(32, 40, PotItem.Nothing, 'Skull 3 Lobby'), Pot(76, 28, PotItem.Nothing, 'Skull East Bridge'), Pot(112, 28, PotItem.Nothing, 'Skull East Bridge')],
|
||||
91: [Pot(218, 37, PotItem.Nothing, 'GT Hidden Spikes'), Pot(222, 37, PotItem.Switch, 'GT Hidden Spikes'), Pot(226, 37, PotItem.Nothing, 'GT Hidden Spikes')],
|
||||
92: [Pot(228, 25, PotItem.Nothing, 'GT Refill'), Pot(104, 24, PotItem.Nothing, 'GT Refill'), Pot(228, 22, PotItem.Nothing, 'GT Refill'), Pot(216, 25, PotItem.Nothing, 'GT Refill'), Pot(84, 24, PotItem.Nothing, 'GT Refill'),
|
||||
Pot(216, 22, PotItem.Nothing, 'GT Refill'), Pot(94, 22, PotItem.Bomb, 'GT Refill'), Pot(94, 26, PotItem.BigMagic, 'GT Refill')],
|
||||
93: [Pot(16, 5, PotItem.Bomb, 'GT Gauntlet 2'), Pot(44, 5, PotItem.FiveRupees, 'GT Gauntlet 2'), Pot(16, 11, PotItem.OneRupee, 'GT Gauntlet 2'), Pot(44, 11, PotItem.FiveArrows, 'GT Gauntlet 2'), Pot(12, 20, PotItem.FiveArrows, 'GT Gauntlet 3'),
|
||||
Pot(48, 20, PotItem.Bomb, 'GT Gauntlet 3'), Pot(12, 28, PotItem.SmallMagic, 'GT Gauntlet 3'), Pot(48, 28, PotItem.Bomb, 'GT Gauntlet 3')],
|
||||
94: [Pot(92, 4, PotItem.SmallMagic, 'Ice Falling Square'), Pot(96, 4, PotItem.SmallMagic, 'Ice Falling Square'), Pot(76, 8, PotItem.Heart, 'Ice Falling Square'), Pot(112, 8, PotItem.Heart, 'Ice Falling Square')],
|
||||
95: [Pot(44, 27, PotItem.Switch, 'Ice Spike Room')],
|
||||
96: [Pot(76, 4, PotItem.Heart, 'Hyrule Castle West Lobby'), Pot(112, 4, PotItem.Heart, 'Hyrule Castle West Lobby')],
|
||||
98: [Pot(208, 21, PotItem.Heart, 'Hyrule Castle East Lobby')],
|
||||
99: [Pot(48, 4, PotItem.Nothing, 'Desert Tiles 1'), Pot(12, 4, PotItem.Nothing, 'Desert Tiles 1'), Pot(12, 8, PotItem.Nothing, 'Desert Tiles 1'), Pot(48, 12, PotItem.Nothing, 'Desert Tiles 1'), Pot(48, 8, PotItem.Heart, 'Desert Tiles 1'),
|
||||
Pot(12, 12, PotItem.Key, 'Desert Tiles 1')],
|
||||
100: [Pot(12, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange), Pot(16, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange), Pot(20, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange),
|
||||
Pot(36, 28, PotItem.Bomb, 'Thieves Attic'), Pot(40, 28, PotItem.SmallMagic, 'Thieves Attic'),
|
||||
Pot(44, 28, PotItem.SmallMagic, 'Thieves Attic'), Pot(48, 28, PotItem.Switch, 'Thieves Attic')],
|
||||
101: [Pot(100, 28, PotItem.Bomb, 'Thieves Attic Window'), Pot(104, 28, PotItem.Bomb, 'Thieves Attic Window')],
|
||||
102: [Pot(48, 37, PotItem.FiveArrows, 'Swamp Refill'), Pot(52, 37, PotItem.Bomb, 'Swamp Refill'), Pot(56, 37, PotItem.FiveRupees, 'Swamp Refill'), Pot(48, 38, PotItem.FiveArrows, 'Swamp Refill'), Pot(52, 38, PotItem.Bomb, 'Swamp Refill'),
|
||||
Pot(56, 38, PotItem.FiveRupees, 'Swamp Refill'), Pot(84, 5, PotItem.Heart, 'Swamp Behind Waterfall'), Pot(104, 5, PotItem.FiveArrows, 'Swamp Behind Waterfall'), Pot(84, 6, PotItem.Heart, 'Swamp Behind Waterfall'),
|
||||
Pot(104, 6, PotItem.Bomb, 'Swamp Behind Waterfall')],
|
||||
103: [Pot(22, 26, PotItem.Nothing, 'Skull Left Drop'), Pot(18, 22, PotItem.Nothing, 'Skull Left Drop'), Pot(12, 7, PotItem.FiveArrows, 'Skull Left Drop'), Pot(48, 7, PotItem.SmallMagic, 'Skull Left Drop'),
|
||||
Pot(18, 23, PotItem.SmallMagic, 'Skull Left Drop'), Pot(18, 26, PotItem.Heart, 'Skull Left Drop'), Pot(96, 19, PotItem.Heart, 'Skull Compass Room'), Pot(74, 20, PotItem.SmallMagic, 'Skull Compass Room'),
|
||||
Pot(92, 9, PotItem.Nothing, 'Skull Compass Room'), Pot(84, 28, PotItem.Nothing, 'Skull Compass Room'), Pot(104, 28, PotItem.Heart, 'Skull Compass Room')],
|
||||
104: [Pot(84, 14, PotItem.Nothing, 'Skull Pinball'), Pot(84, 13, PotItem.Nothing, 'Skull Pinball'), Pot(88, 12, PotItem.Nothing, 'Skull Pinball'), Pot(88, 6, PotItem.Nothing, 'Skull Pinball'), Pot(88, 5, PotItem.Nothing, 'Skull Pinball'),
|
||||
Pot(88, 4, PotItem.Nothing, 'Skull Pinball'), Pot(64, 17, PotItem.Nothing, 'Skull Pinball'), Pot(64, 15, PotItem.Nothing, 'Skull Pinball'), Pot(64, 7, PotItem.Heart, 'Skull Pinball'), Pot(88, 7, PotItem.SmallMagic, 'Skull Pinball'),
|
||||
Pot(64, 16, PotItem.Heart, 'Skull Pinball'), Pot(64, 24, PotItem.SmallMagic, 'Skull Pinball'), Pot(64, 25, PotItem.Heart, 'Skull Pinball')],
|
||||
107: [Pot(28, 5, PotItem.Heart, 'GT Crystal Paths'), Pot(44, 5, PotItem.Nothing, 'GT Crystal Paths'), Pot(28, 8, PotItem.Nothing, 'GT Crystal Paths'), Pot(44, 8, PotItem.SmallMagic, 'GT Crystal Paths'),
|
||||
Pot(28, 11, PotItem.SmallMagic, 'GT Crystal Paths'), Pot(44, 11, PotItem.Nothing, 'GT Crystal Paths'), Pot(94, 25, PotItem.Nothing, 'GT Mimics 2'), Pot(98, 25, PotItem.FiveArrows, 'GT Mimics 2')],
|
||||
108: [Pot(20, 6, PotItem.Heart, 'GT Quad Pot'), Pot(40, 6, PotItem.FiveArrows, 'GT Quad Pot'), Pot(20, 10, PotItem.Bomb, 'GT Quad Pot'), Pot(40, 10, PotItem.SmallMagic, 'GT Quad Pot')],
|
||||
109: [Pot(28, 26, PotItem.Heart, 'GT Gauntlet 5'), Pot(32, 26, PotItem.Heart, 'GT Gauntlet 5'), Pot(28, 27, PotItem.SmallMagic, 'GT Gauntlet 5'), Pot(32, 27, PotItem.SmallMagic, 'GT Gauntlet 5')],
|
||||
115: [Pot(154, 21, PotItem.FiveArrows, 'Desert Circle of Pots'), Pot(158, 21, PotItem.OneRupee, 'Desert Circle of Pots'), Pot(20, 23, PotItem.Switch, 'Desert Circle of Pots'), Pot(36, 23, PotItem.FiveRupees, 'Desert Circle of Pots'),
|
||||
Pot(144, 24, PotItem.Heart, 'Desert Circle of Pots'), Pot(168, 24, PotItem.FiveArrows, 'Desert Circle of Pots'), Pot(20, 26, PotItem.SmallMagic, 'Desert Circle of Pots'), Pot(36, 26, PotItem.Heart, 'Desert Circle of Pots'),
|
||||
Pot(154, 27, PotItem.OneRupee, 'Desert Circle of Pots'), Pot(158, 27, PotItem.FiveRupees, 'Desert Circle of Pots')],
|
||||
116: [Pot(30, 5, PotItem.SmallMagic, 'Desert Map Room'), Pot(62, 5, PotItem.Switch, 'Desert Map Room'), Pot(94, 5, PotItem.SmallMagic, 'Desert Map Room'), Pot(14, 11, PotItem.Heart, 'Desert Map Room'),
|
||||
Pot(46, 11, PotItem.FiveArrows, 'Desert Map Room'), Pot(78, 11, PotItem.FiveArrows, 'Desert Map Room'), Pot(110, 11, PotItem.Heart, 'Desert Map Room')],
|
||||
117: [Pot(148, 22, PotItem.SmallMagic, 'Desert Arrow Pot Corner'), Pot(160, 22, PotItem.FiveArrows, 'Desert Arrow Pot Corner'), Pot(172, 22, PotItem.Heart, 'Desert Arrow Pot Corner')],
|
||||
118: [Pot(112, 12, PotItem.Heart, 'Swamp Drain Right'), Pot(84, 23, PotItem.Heart, 'Swamp Flooded Spot'), Pot(96, 23, PotItem.Heart, 'Swamp Flooded Spot')],
|
||||
123: [Pot(48, 10, PotItem.Nothing, 'GT Conveyor Star Pits'), Pot(88, 10, PotItem.Nothing, 'GT Conveyor Star Pits'), Pot(76, 7, PotItem.Nothing, 'GT Conveyor Star Pits'), Pot(60, 4, PotItem.Heart, 'GT Conveyor Star Pits'),
|
||||
Pot(64, 4, PotItem.Key, 'GT Conveyor Star Pits')],
|
||||
124: [Pot(36, 21, PotItem.Nothing, 'GT Falling Bridge'), Pot(24, 11, PotItem.Nothing, 'GT Falling Bridge'), Pot(28, 4, PotItem.Heart, 'GT Falling Bridge'), Pot(32, 4, PotItem.Heart, 'GT Falling Bridge')],
|
||||
125: [Pot(44, 12, PotItem.Nothing, 'GT Firesnake Room'), Pot(44, 6, PotItem.Nothing, 'GT Firesnake Room'), Pot(112, 6, PotItem.Heart, 'GT Firesnake Room'), Pot(108, 20, PotItem.FiveArrows, 'GT Warp Maze - Pot Rail'),
|
||||
Pot(114, 20, PotItem.Bomb, 'GT Petting Zoo'), Pot(76, 28, PotItem.Bomb, 'GT Petting Zoo')],
|
||||
126: [Pot(86, 15, PotItem.Heart, 'Ice Tall Hint'), Pot(82, 26, PotItem.SmallMagic, 'Ice Tall Hint'), Pot(100, 26, PotItem.Switch, 'Ice Tall Hint'), Pot(104, 26, PotItem.Nothing, 'Ice Tall Hint')],
|
||||
128: [Pot(48, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock'), Pot(52, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock'), Pot(56, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock')],
|
||||
130: [Pot(50, 5, PotItem.Nothing, 'Hyrule Dungeon South Abyss'), Pot(50, 10, PotItem.Nothing, 'Hyrule Dungeon South Abyss'), Pot(76, 50, PotItem.Heart, 'Hyrule Dungeon South Abyss')],
|
||||
131: [Pot(76, 4, PotItem.FiveArrows, 'Desert West Wing'), Pot(80, 4, PotItem.OneRupee, 'Desert West Wing'), Pot(76, 28, PotItem.FiveRupees, 'Desert West Wing'), Pot(80, 28, PotItem.FiveArrows, 'Desert West Wing')],
|
||||
132: [Pot(64, 17, PotItem.Nothing, 'Desert Main Lobby'), Pot(60, 17, PotItem.Nothing, 'Desert Main Lobby'), Pot(80, 14, PotItem.Nothing, 'Desert Main Lobby'), Pot(44, 14, PotItem.Nothing, 'Desert Main Lobby'),
|
||||
Pot(100, 6, PotItem.Nothing, 'Desert Main Lobby'), Pot(24, 6, PotItem.Nothing, 'Desert Main Lobby'), Pot(24, 7, PotItem.FiveArrows, 'Desert Main Lobby'), Pot(100, 7, PotItem.FiveArrows, 'Desert Main Lobby')],
|
||||
133: [Pot(44, 28, PotItem.Heart, 'Desert East Wing'), Pot(48, 28, PotItem.FiveArrows, 'Desert East Wing')],
|
||||
135: [Pot(12, 11, PotItem.Nothing, 'Hera Tile Room'), Pot(16, 12, PotItem.Nothing, 'Hera Tile Room'), Pot(40, 12, PotItem.Nothing, 'Hera Tile Room'), Pot(32, 12, PotItem.Nothing, 'Hera Tile Room'), Pot(24, 12, PotItem.Nothing, 'Hera Tile Room'),
|
||||
Pot(16, 11, PotItem.Nothing, 'Hera Tile Room'), Pot(76, 20, PotItem.SmallMagic, 'Hera Torches'), Pot(112, 20, PotItem.BigMagic, 'Hera Torches')],
|
||||
139: [Pot(76, 12, PotItem.Nothing, 'GT Conveyor Cross'), Pot(112, 12, PotItem.Key, 'GT Conveyor Cross'), Pot(32, 23, PotItem.Nothing, 'GT Hookshot South Platform'), Pot(28, 23, PotItem.Nothing, 'GT Hookshot South Platform'),
|
||||
Pot(32, 9, PotItem.SmallMagic, 'GT Hookshot East Platform'), Pot(76, 20, PotItem.Nothing, 'GT Map Room'), Pot(76, 28, PotItem.Heart, 'GT Map Room')],
|
||||
140: [Pot(76, 12, PotItem.Switch, 'GT Hope Room'), Pot(112, 12, PotItem.SmallMagic, 'GT Hope Room'), Pot(76, 20, PotItem.Bomb, "GT Bob's Room"), Pot(92, 20, PotItem.Bomb, "GT Bob's Room"), Pot(100, 21, PotItem.FiveArrows, "GT Bob's Room"),
|
||||
Pot(104, 26, PotItem.Bomb, "GT Bob's Room"), Pot(88, 27, PotItem.Bomb, "GT Bob's Room")],
|
||||
141: [Pot(204, 11, PotItem.Nothing, 'GT Speed Torch Upper'), Pot(204, 14, PotItem.BigMagic, 'GT Speed Torch Upper'), Pot(28, 23, PotItem.Heart, 'GT Pots n Blocks'), Pot(36, 23, PotItem.Heart, 'GT Pots n Blocks'),
|
||||
Pot(32, 24, PotItem.BigMagic, 'GT Pots n Blocks')],
|
||||
142: [Pot(80, 5, PotItem.FiveArrows, 'Ice Lonely Freezor'), Pot(80, 6, PotItem.Nothing, 'Ice Lonely Freezor')],
|
||||
145: [Pot(84, 4, PotItem.Heart, 'Mire Falling Foes'), Pot(104, 4, PotItem.SmallMagic, 'Mire Falling Foes')],
|
||||
146: [Pot(86, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy'), Pot(92, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy'), Pot(98, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy'), Pot(104, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy')],
|
||||
147: [Pot(28, 7, PotItem.Switch, 'Mire Dark Shooters'), Pot(96, 7, PotItem.Heart, 'Mire Dark Shooters', PotFlags.NoSwitch)],
|
||||
150: [Pot(14, 18, PotItem.Nothing, 'GT Torch Cross'), Pot(32, 5, PotItem.Nothing, 'GT Torch Cross'), Pot(32, 17, PotItem.SmallMagic, 'GT Torch Cross'), Pot(32, 24, PotItem.SmallMagic, 'GT Torch Cross'),
|
||||
Pot(14, 24, PotItem.Nothing, 'GT Torch Cross'), Pot(76, 21, PotItem.Heart, 'GT Staredown'), Pot(112, 21, PotItem.BigMagic, 'GT Staredown')],
|
||||
153: [Pot(40, 20, PotItem.SmallMagic, 'Eastern Darkness'), Pot(84, 20, PotItem.Heart, 'Eastern Darkness')],
|
||||
155: [Pot(48, 4, PotItem.SmallMagic, 'GT Double Switch Key Spot'), Pot(48, 12, PotItem.Key, 'GT Double Switch Key Spot'), Pot(28, 24, PotItem.Nothing, 'GT Warp Maze - Mid Section'), Pot(32, 24, PotItem.Nothing, 'GT Warp Maze - Mid Section')],
|
||||
156: [Pot(56, 8, PotItem.SmallMagic, 'GT Invisible Catwalk'), Pot(56, 9, PotItem.FiveArrows, 'GT Invisible Catwalk')],
|
||||
157: [Pot(76, 4, PotItem.Bomb, 'GT Crystal Conveyor'), Pot(84, 4, PotItem.SmallMagic, 'GT Crystal Conveyor'), Pot(32, 7, PotItem.Nothing, 'GT Compass Room'), Pot(40, 9, PotItem.Nothing, 'GT Compass Room')],
|
||||
159: [Pot(138, 20, PotItem.Nothing, 'Ice Many Pots'), Pot(138, 19, PotItem.Heart, 'Ice Many Pots'), Pot(178, 19, PotItem.Heart, 'Ice Many Pots'), Pot(40, 21, PotItem.Switch, 'Ice Many Pots'), Pot(138, 21, PotItem.Key, 'Ice Many Pots'),
|
||||
Pot(20, 27, PotItem.Heart, 'Ice Many Pots'), Pot(138, 27, PotItem.Heart, 'Ice Many Pots'), Pot(178, 28, PotItem.Heart, 'Ice Many Pots'), Pot(178, 21, PotItem.Nothing, 'Ice Many Pots'), Pot(178, 20, PotItem.Nothing, 'Ice Many Pots'),
|
||||
Pot(40, 27, PotItem.Nothing, 'Ice Many Pots'), Pot(178, 27, PotItem.Nothing, 'Ice Many Pots'), Pot(178, 26, PotItem.Nothing, 'Ice Many Pots'), Pot(138, 28, PotItem.Nothing, 'Ice Many Pots'), Pot(138, 26, PotItem.Nothing, 'Ice Many Pots'),
|
||||
Pot(20, 21, PotItem.Nothing, 'Ice Many Pots')],
|
||||
161: [Pot(150, 6, PotItem.Key, 'Mire Fishbone'), Pot(100, 11, PotItem.SmallMagic, 'Mire Fishbone'), Pot(104, 12, PotItem.Heart, 'Mire Fishbone'), Pot(108, 13, PotItem.SmallMagic, 'Mire Fishbone'), Pot(112, 14, PotItem.Heart, 'Mire Fishbone'),
|
||||
Pot(96, 27, PotItem.Nothing, 'Mire South Fish'), Pot(92, 21, PotItem.Nothing, 'Mire South Fish'), Pot(96, 23, PotItem.Heart, 'Mire South Fish'), Pot(92, 25, PotItem.Nothing, 'Mire South Fish'),
|
||||
Pot(76, 28, PotItem.Nothing, 'Mire South Fish'), Pot(112, 28, PotItem.Nothing, 'Mire South Fish')],
|
||||
162: [Pot(12, 28, PotItem.BigMagic, 'Mire Left Bridge')],
|
||||
168: [Pot(138, 28, PotItem.Nothing, 'Eastern Stalfos Spawn'), Pot(178, 28, PotItem.Nothing, 'Eastern Stalfos Spawn'), Pot(178, 19, PotItem.Nothing, 'Eastern Stalfos Spawn'), Pot(138, 19, PotItem.Heart, 'Eastern Stalfos Spawn'),
|
||||
Pot(30, 24, PotItem.OneRupee, 'Eastern Stalfos Spawn')],
|
||||
169: [Pot(144, 43, PotItem.FiveArrows, 'Eastern Courtyard'), Pot(236, 43, PotItem.FiveArrows, 'Eastern Courtyard'), Pot(144, 44, PotItem.FiveArrows, 'Eastern Courtyard'), Pot(236, 44, PotItem.Heart, 'Eastern Courtyard'),
|
||||
Pot(12, 19, PotItem.Nothing, 'Eastern Courtyard Ledge'), Pot(112, 19, PotItem.Nothing, 'Eastern Courtyard Ledge'), Pot(16, 20, PotItem.Heart, 'Eastern Courtyard Ledge'), Pot(108, 20, PotItem.Heart, 'Eastern Courtyard Ledge')],
|
||||
170: [Pot(212, 10, PotItem.Nothing, 'Eastern Pot Switch'), Pot(232, 10, PotItem.Nothing, 'Eastern Pot Switch'), Pot(232, 5, PotItem.Nothing, 'Eastern Pot Switch'), Pot(212, 5, PotItem.Heart, 'Eastern Pot Switch'),
|
||||
Pot(94, 8, PotItem.Switch, 'Eastern Pot Switch'), Pot(108, 55, PotItem.Heart, 'Eastern Map Balcony'), Pot(108, 56, PotItem.Heart, 'Eastern Map Balcony'), Pot(108, 57, PotItem.Heart, 'Eastern Map Balcony')],
|
||||
171: [Pot(20, 24, PotItem.Key, 'Thieves Spike Switch')],
|
||||
174: [Pot(76, 12, PotItem.Switch, 'Iced T')],
|
||||
176: [Pot(20, 27, PotItem.Nothing, 'Tower Circle of Pots'), Pot(24, 24, PotItem.Nothing, 'Tower Circle of Pots'), Pot(44, 25, PotItem.Nothing, 'Tower Circle of Pots'), Pot(20, 21, PotItem.Bomb, 'Tower Circle of Pots'),
|
||||
Pot(28, 21, PotItem.OneRupee, 'Tower Circle of Pots'), Pot(32, 21, PotItem.FiveRupees, 'Tower Circle of Pots'), Pot(40, 21, PotItem.FiveArrows, 'Tower Circle of Pots'), Pot(16, 23, PotItem.FiveRupees, 'Tower Circle of Pots'),
|
||||
Pot(44, 23, PotItem.OneRupee, 'Tower Circle of Pots'), Pot(36, 24, PotItem.Heart, 'Tower Circle of Pots'), Pot(16, 25, PotItem.Heart, 'Tower Circle of Pots'), Pot(28, 27, PotItem.FiveArrows, 'Tower Circle of Pots'),
|
||||
Pot(40, 27, PotItem.Bomb, 'Tower Circle of Pots'), Pot(32, 27, PotItem.Nothing, 'Tower Circle of Pots')],
|
||||
177: [Pot(76, 4, PotItem.Heart, 'Mire Spike Barrier'), Pot(112, 4, PotItem.OneRupee, 'Mire Spike Barrier')],
|
||||
178: [Pot(48, 40, PotItem.OneRupee, 'Mire BK Door Room'), Pot(76, 40, PotItem.OneRupee, 'Mire BK Door Room'), Pot(48, 41, PotItem.Nothing, 'Mire BK Door Room'), Pot(76, 41, PotItem.Heart, 'Mire BK Door Room'),
|
||||
Pot(48, 42, PotItem.Nothing, 'Mire BK Door Room'), Pot(76, 40, PotItem.Nothing, 'Mire BK Door Room')],
|
||||
179: [Pot(12, 20, PotItem.Key, 'Mire Spikes'), Pot(48, 20, PotItem.SmallMagic, 'Mire Spikes'), Pot(48, 28, PotItem.Switch, 'Mire Spikes')],
|
||||
180: [Pot(44, 28, PotItem.BigMagic, 'TR Final Abyss'), Pot(48, 28, PotItem.Heart, 'TR Final Abyss')],
|
||||
181: [Pot(112, 4, PotItem.FiveRupees, 'TR Dark Ride'), Pot(112, 15, PotItem.Heart, 'TR Dark Ride'), Pot(76, 16, PotItem.Switch, 'TR Dark Ride'), Pot(112, 16, PotItem.BigMagic, 'TR Dark Ride'), Pot(112, 17, PotItem.Heart, 'TR Dark Ride'),
|
||||
Pot(112, 28, PotItem.Bomb, 'TR Dark Ride')],
|
||||
182: [Pot(94, 9, PotItem.BigMagic, 'TR Refill')],
|
||||
183: [Pot(30, 5, PotItem.SmallMagic, 'TR Roller Room')],
|
||||
184: [Pot(96, 13, PotItem.Switch, 'Eastern Big Key'), Pot(88, 16, PotItem.Heart, 'Eastern Big Key'), Pot(104, 16, PotItem.Heart, 'Eastern Big Key')],
|
||||
185: [Pot(92, 18, PotItem.OneRupee, 'Eastern Cannonball'), Pot(96, 18, PotItem.FiveRupees, 'Eastern Cannonball'), Pot(104, 18, PotItem.FiveRupees, 'Eastern Cannonball'), Pot(108, 18, PotItem.OneRupee, 'Eastern Cannonball')],
|
||||
186: [Pot(100, 8, PotItem.Nothing, 'Eastern Dark Pots'), Pot(88, 8, PotItem.Nothing, 'Eastern Dark Pots'), Pot(94, 4, PotItem.OneRupee, 'Eastern Dark Pots'), Pot(76, 6, PotItem.Heart, 'Eastern Dark Pots'),
|
||||
Pot(112, 6, PotItem.Key, 'Eastern Dark Pots'), Pot(76, 10, PotItem.Heart, 'Eastern Dark Pots'), Pot(112, 10, PotItem.SmallMagic, 'Eastern Dark Pots'), Pot(94, 12, PotItem.OneRupee, 'Eastern Dark Pots')],
|
||||
188: [Pot(86, 4, PotItem.Heart, 'Thieves Hallway'), Pot(102, 4, PotItem.Key, 'Thieves Hallway'), Pot(138, 3, PotItem.Bomb, 'Thieves Conveyor Maze'), Pot(178, 3, PotItem.Switch, 'Thieves Conveyor Maze'),
|
||||
Pot(138, 12, PotItem.Heart, 'Thieves Conveyor Maze'), Pot(178, 12, PotItem.Bomb, 'Thieves Conveyor Maze'), Pot(12, 20, PotItem.Nothing, 'Thieves Pot Alcove Mid'), Pot(48, 20, PotItem.Bomb, 'Thieves Pot Alcove Mid'),
|
||||
Pot(12, 28, PotItem.Bomb, 'Thieves Pot Alcove Mid'), Pot(48, 28, PotItem.Bomb, 'Thieves Pot Alcove Mid'), Pot(28, 21, PotItem.FiveRupees, 'Thieves Pot Alcove Top'), Pot(32, 21, PotItem.FiveRupees, 'Thieves Pot Alcove Top'),
|
||||
Pot(28, 27, PotItem.FiveRupees, 'Thieves Pot Alcove Bottom'), Pot(32, 27, PotItem.FiveRupees, 'Thieves Pot Alcove Bottom')],
|
||||
190: [Pot(92, 25, PotItem.Switch, 'Ice Switch Room')],
|
||||
191: [Pot(40, 20, PotItem.FiveArrows, 'Ice Refill'), Pot(44, 20, PotItem.Heart, 'Ice Refill'), Pot(48, 20, PotItem.Bomb, 'Ice Refill'), Pot(40, 28, PotItem.SmallMagic, 'Ice Refill'), Pot(44, 28, PotItem.SmallMagic, 'Ice Refill'),
|
||||
Pot(48, 28, PotItem.SmallMagic, 'Ice Refill')],
|
||||
192: [Pot(48, 10, PotItem.Bomb, 'Tower Dark Pits'), Pot(12, 14, PotItem.FiveRupees, 'Tower Dark Pits'), Pot(12, 26, PotItem.Heart, 'Tower Dark Pits'), Pot(28, 27, PotItem.OneRupee, 'Tower Dark Pits')],
|
||||
194: [Pot(180, 7, PotItem.Switch, 'Mire Hub Switch'), Pot(100, 46, PotItem.SmallMagic, 'Mire Hub Right'), Pot(68, 48, PotItem.OneRupee, 'Mire Hub'), Pot(64, 52, PotItem.FiveArrows, 'Mire Hub')],
|
||||
196: [Pot(84, 9, PotItem.Bomb, 'TR Crystal Maze'), Pot(24, 14, PotItem.Heart, 'TR Crystal Maze'), Pot(56, 17, PotItem.FiveRupees, 'TR Crystal Maze'), Pot(84, 17, PotItem.Bomb, 'TR Crystal Maze'),
|
||||
Pot(12, 21, PotItem.FiveArrows, 'TR Crystal Maze'), Pot(76, 23, PotItem.OneRupee, 'TR Crystal Maze'), Pot(48, 25, PotItem.SmallMagic, 'TR Crystal Maze'), Pot(12, 26, PotItem.Heart, 'TR Crystal Maze')],
|
||||
198: [Pot(12, 7, PotItem.BigMagic, 'TR Hub'), Pot(12, 25, PotItem.Heart, 'TR Hub')],
|
||||
199: [Pot(12, 10, PotItem.Heart, 'TR Torches'), Pot(12, 11, PotItem.BigMagic, 'TR Torches'), Pot(12, 22, PotItem.SmallMagic, 'TR Torches Ledge'), Pot(12, 28, PotItem.FiveArrows, 'TR Torches Ledge')],
|
||||
201: [Pot(30, 22, PotItem.OneRupee, 'Eastern Lobby'), Pot(94, 22, PotItem.OneRupee, 'Eastern Lobby'), Pot(60, 22, PotItem.Switch, 'Eastern Lobby')],
|
||||
203: [Pot(80, 4, PotItem.Nothing, 'Thieves Ambush'), Pot(80, 28, PotItem.Nothing, 'Thieves Ambush'), Pot(88, 16, PotItem.Heart, 'Thieves Ambush'), Pot(88, 28, PotItem.FiveRupees, 'Thieves Ambush')],
|
||||
204: [Pot(36, 4, PotItem.FiveRupees, 'Thieves Rail Ledge'), Pot(36, 28, PotItem.FiveRupees, 'Thieves Rail Ledge'), Pot(112, 4, PotItem.Heart, 'Thieves BK Corner'), Pot(112, 28, PotItem.Bomb, 'Thieves BK Corner')],
|
||||
206: [Pot(76, 8, PotItem.SmallMagic, 'Ice Antechamber'), Pot(80, 8, PotItem.SmallMagic, 'Ice Antechamber'), Pot(108, 12, PotItem.Bomb, 'Ice Antechamber'), Pot(112, 12, PotItem.FiveArrows, 'Ice Antechamber'),
|
||||
Pot(204, 11, PotItem.Hole, 'Ice Antechamber')],
|
||||
208: [Pot(158, 5, PotItem.SmallMagic, 'Tower Dark Maze'), Pot(140, 11, PotItem.OneRupee, 'Tower Dark Maze'), Pot(42, 13, PotItem.SmallMagic, 'Tower Dark Maze'), Pot(48, 16, PotItem.Heart, 'Tower Dark Maze'),
|
||||
Pot(176, 20, PotItem.OneRupee, 'Tower Dark Maze'), Pot(146, 23, PotItem.FiveRupees, 'Tower Dark Maze'), Pot(12, 28, PotItem.Heart, 'Tower Dark Maze')],
|
||||
209: [Pot(48, 4, PotItem.BigMagic, 'Mire Conveyor Barrier'), Pot(168, 7, PotItem.OneRupee, 'Mire Conveyor Barrier'), Pot(76, 4, PotItem.OneRupee, 'Mire Neglected Room'), Pot(112, 4, PotItem.FiveArrows, 'Mire Neglected Room'),
|
||||
Pot(76, 12, PotItem.Nothing, 'Mire Neglected Room'), Pot(112, 12, PotItem.OneRupee, 'Mire Neglected Room')],
|
||||
214: [Pot(92, 22, PotItem.BigMagic, 'TR Main Lobby'), Pot(96, 22, PotItem.Bomb, 'TR Main Lobby')],
|
||||
216: [Pot(202, 8, PotItem.Heart, 'Eastern Duo Eyegores'), Pot(242, 8, PotItem.FiveArrows, 'Eastern Duo Eyegores'), Pot(202, 10, PotItem.FiveArrows, 'Eastern Duo Eyegores'), Pot(242, 10, PotItem.FiveArrows, 'Eastern Duo Eyegores'),
|
||||
Pot(202, 12, PotItem.FiveArrows, 'Eastern Duo Eyegores'), Pot(242, 12, PotItem.Heart, 'Eastern Duo Eyegores'), Pot(92, 24, PotItem.Heart, 'Eastern Single Eyegore'), Pot(96, 24, PotItem.FiveArrows, 'Eastern Single Eyegore')],
|
||||
217: [Pot(92, 20, PotItem.FiveArrows, 'Eastern False Switches'), Pot(92, 28, PotItem.Heart, 'Eastern False Switches')],
|
||||
218: [Pot(24, 23, PotItem.FiveArrows, 'Eastern Attic Start'), Pot(36, 23, PotItem.FiveArrows, 'Eastern Attic Start'), Pot(24, 25, PotItem.Switch, 'Eastern Attic Start'), Pot(36, 25, PotItem.Heart, 'Eastern Attic Start')],
|
||||
219: [Pot(12, 4, PotItem.Nothing, 'Thieves Lobby'), Pot(62, 19, PotItem.Nothing, 'Thieves Lobby'), Pot(112, 4, PotItem.FiveRupees, 'Thieves Lobby'), Pot(88, 16, PotItem.Heart, 'Thieves Lobby')],
|
||||
220: [Pot(56, 4, PotItem.FiveRupees, 'Thieves Compass Room'), Pot(112, 4, PotItem.Bomb, 'Thieves Compass Room'), Pot(68, 16, PotItem.Heart, 'Thieves Compass Room'), Pot(12, 28, PotItem.FiveArrows, 'Thieves Compass Room')],
|
||||
227: [Pot(88, 55, PotItem.Nothing, 'Bat Cave (right)'), Pot(100, 57, PotItem.OneRupee, 'Bat Cave (right)')],
|
||||
228: [Pot(32, 9, PotItem.FiveRupees, 'Old Man House'), Pot(112, 10, PotItem.OneRupee, 'Old Man House')],
|
||||
229: [Pot(48, 4, PotItem.OneRupee, 'Old Man House Back'), Pot(76, 4, PotItem.OneRupee, 'Old Man House Back'), Pot(112, 16, PotItem.OneRupee, 'Old Man House Back'), Pot(64, 18, PotItem.FiveRupees, 'Old Man House Back')],
|
||||
230: [Pot(108, 12, PotItem.FiveArrows, 'Death Mountain Return Cave'), Pot(88, 16, PotItem.Heart, 'Death Mountain Return Cave'), Pot(72, 20, PotItem.Nothing, 'Death Mountain Return Cave'),
|
||||
Pot(56, 24, PotItem.OneRupee, 'Death Mountain Return Cave')],
|
||||
231: [Pot(68, 5, PotItem.OneRupee, 'Death Mountain Return Cave'), Pot(72, 5, PotItem.OneRupee, 'Death Mountain Return Cave')],
|
||||
232: [Pot(96, 4, PotItem.Heart, 'Superbunny Cave')],
|
||||
235: [Pot(206, 8, PotItem.FiveRupees, 'Bumper Cave'), Pot(210, 8, PotItem.FiveRupees, 'Bumper Cave'), Pot(88, 14, PotItem.SmallMagic, 'Bumper Cave'), Pot(92, 14, PotItem.Heart, 'Bumper Cave'), Pot(96, 14, PotItem.SmallMagic, 'Bumper Cave')],
|
||||
241: [Pot(64, 5, PotItem.Heart, 'Old Man Cave')],
|
||||
248: [Pot(242, 13, PotItem.BigMagic, 'Superbunny Cave')],
|
||||
253: [Pot(88, 6, PotItem.FiveRupees, 'Fairy Ascension Cave (Top)'), Pot(100, 6, PotItem.FiveRupees, 'Fairy Ascension Cave (Top)'), Pot(84, 23, PotItem.FiveRupees, 'Fairy Ascension Cave (Bottom)'),
|
||||
Pot(84, 24, PotItem.FiveRupees, 'Fairy Ascension Cave (Bottom)')],
|
||||
255: [Pot(92, 8, PotItem.Heart, 'Paradox Cave'), Pot(96, 8, PotItem.Heart, 'Paradox Cave'), Pot(112, 28, PotItem.OneRupee, 'Paradox Cave Front')],
|
||||
257: [Pot(12, 20, PotItem.Heart, 'Snitch Lady (East)'), Pot(224, 19, PotItem.Chicken, 'Snitch Lady (West)'), Pot(228, 19, PotItem.Heart, 'Snitch Lady (West)')],
|
||||
258: [Pot(146, 19, PotItem.Heart, 'Sick Kids House'), Pot(150, 19, PotItem.Heart, 'Sick Kids House')],
|
||||
259: [Pot(140, 7, PotItem.Chicken, 'Tavern'), Pot(140, 9, PotItem.Nothing, 'Tavern'), Pot(12, 12, PotItem.Heart, 'Tavern (Front)')],
|
||||
260: [Pot(202, 21, PotItem.Heart, 'Links House'), Pot(202, 22, PotItem.Heart, 'Links House'), Pot(202, 23, PotItem.Heart, 'Links House')],
|
||||
261: [Pot(30, 20, PotItem.Heart, 'Sahasrahlas Hut'), Pot(28, 21, PotItem.Heart, 'Sahasrahlas Hut'), Pot(32, 21, PotItem.Heart, 'Sahasrahlas Hut')],
|
||||
263: [Pot(214, 23, PotItem.Bomb, 'Light World Bomb Hut'), Pot(222, 23, PotItem.FiveArrows, 'Light World Bomb Hut'), Pot(230, 23, PotItem.Bomb, 'Light World Bomb Hut'), Pot(214, 25, PotItem.OneRupee, 'Light World Bomb Hut'),
|
||||
Pot(222, 25, PotItem.Nothing, 'Light World Bomb Hut'), Pot(230, 25, PotItem.OneRupee, 'Light World Bomb Hut'), Pot(214, 27, PotItem.Bomb, 'Light World Bomb Hut'), Pot(230, 27, PotItem.Bomb, 'Light World Bomb Hut')],
|
||||
264: [Pot(166, 19, PotItem.Chicken, 'Chicken House')],
|
||||
268: [Pot(88, 14, PotItem.Heart, 'Hookshot Fairy')],
|
||||
276: [Pot(92, 4, PotItem.Heart, 'Dark Desert Hint'), Pot(96, 4, PotItem.Heart, 'Dark Desert Hint'), Pot(92, 5, PotItem.Bomb, 'Dark Desert Hint'), Pot(96, 5, PotItem.Bomb, 'Dark Desert Hint'), Pot(92, 10, PotItem.FiveArrows, 'Dark Desert Hint'),
|
||||
Pot(96, 10, PotItem.Heart, 'Dark Desert Hint')],
|
||||
279: [Pot(138, 3, PotItem.Heart, 'Spike Cave'), Pot(142, 3, PotItem.Heart, 'Spike Cave'), Pot(166, 3, PotItem.Heart, 'Spike Cave'), Pot(170, 3, PotItem.Heart, 'Spike Cave'), Pot(138, 4, PotItem.Heart, 'Spike Cave'),
|
||||
Pot(142, 4, PotItem.Heart, 'Spike Cave'), Pot(166, 4, PotItem.Heart, 'Spike Cave'), Pot(170, 4, PotItem.Heart, 'Spike Cave')],
|
||||
281: [Pot(44, 28, PotItem.Heart, 'Blinds Hideout'), Pot(48, 28, PotItem.OneRupee, 'Blinds Hideout'), Pot(76, 28, PotItem.Heart, 'Blinds Hideout'), Pot(80, 28, PotItem.Heart, 'Blinds Hideout')],
|
||||
282: [Pot(214, 10, PotItem.Heart, 'Palace of Darkness Hint'), Pot(218, 10, PotItem.Heart, 'Palace of Darkness Hint'), Pot(226, 10, PotItem.Heart, 'Palace of Darkness Hint'), Pot(230, 10, PotItem.Heart, 'Palace of Darkness Hint')],
|
||||
283: [Pot(24, 53, PotItem.Nothing, 'Cave 45'), Pot(24, 54, PotItem.Heart, 'Cave 45'), Pot(32, 54, PotItem.Heart, 'Cave 45'), Pot(40, 54, PotItem.Heart, 'Cave 45'), Pot(24, 55, PotItem.Heart, 'Cave 45'), Pot(28, 56, PotItem.Heart, 'Cave 45'),
|
||||
Pot(92, 22, PotItem.Bomb, 'Graveyard Cave'), Pot(96, 22, PotItem.Heart, 'Graveyard Cave'), Pot(92, 23, PotItem.Bomb, 'Graveyard Cave'), Pot(96, 23, PotItem.Heart, 'Graveyard Cave'), Pot(92, 24, PotItem.Bomb, 'Graveyard Cave'),
|
||||
Pot(96, 24, PotItem.Heart, 'Graveyard Cave'), Pot(92, 25, PotItem.Bomb, 'Graveyard Cave'), Pot(96, 25, PotItem.Heart, 'Graveyard Cave')],
|
||||
285: [Pot(60, 6, PotItem.FiveRupees, 'Blinds Hideout'), Pot(64, 6, PotItem.FiveRupees, 'Blinds Hideout'), Pot(60, 7, PotItem.FiveRupees, 'Blinds Hideout'), Pot(64, 7, PotItem.FiveRupees, 'Blinds Hideout'),
|
||||
Pot(60, 8, PotItem.FiveRupees, 'Blinds Hideout'), Pot(64, 8, PotItem.FiveRupees, 'Blinds Hideout')],
|
||||
287: [Pot(174, 28, PotItem.Heart, 'Lumberjack House'), Pot(178, 28, PotItem.Heart, 'Lumberjack House')],
|
||||
292: [Pot(20, 20, PotItem.FiveRupees, '50 Rupee Cave'), Pot(40, 20, PotItem.FiveRupees, '50 Rupee Cave'), Pot(20, 21, PotItem.FiveRupees, '50 Rupee Cave'), Pot(40, 21, PotItem.FiveRupees, '50 Rupee Cave'),
|
||||
Pot(20, 22, PotItem.FiveRupees, '50 Rupee Cave'), Pot(40, 22, PotItem.FiveRupees, '50 Rupee Cave'), Pot(24, 24, PotItem.FiveRupees, '50 Rupee Cave'), Pot(28, 24, PotItem.FiveRupees, '50 Rupee Cave'),
|
||||
Pot(32, 24, PotItem.FiveRupees, '50 Rupee Cave'), Pot(36, 24, PotItem.FiveRupees, '50 Rupee Cave')],
|
||||
293: [Pot(24, 25, PotItem.FiveRupees, '20 Rupee Cave'), Pot(28, 25, PotItem.FiveRupees, '20 Rupee Cave'), Pot(32, 25, PotItem.FiveRupees, '20 Rupee Cave'), Pot(36, 25, PotItem.FiveRupees, '20 Rupee Cave'),
|
||||
Pot(88, 22, PotItem.Heart, 'Dev Cave Hint'), Pot(100, 22, PotItem.Heart, 'Dev Cave Hint'), Pot(88, 28, PotItem.Heart, 'Dev Cave Hint'), Pot(100, 28, PotItem.Heart, 'Dev Cave Hint')],
|
||||
295: [Pot(24, 25, PotItem.Nothing, 'Hammer Pegs Cave'), Pot(28, 25, PotItem.Nothing, 'Hammer Pegs Cave'), Pot(32, 25, PotItem.Nothing, 'Hammer Pegs Cave'), Pot(36, 25, PotItem.Nothing, 'Hammer Pegs Cave')],
|
||||
}
|
||||
|
||||
|
||||
def shuffle_pots(world, player):
|
||||
import random
|
||||
|
||||
new_pot_contents = {}
|
||||
|
||||
for supertile in vanilla_pots:
|
||||
old_pots = vanilla_pots[supertile]
|
||||
new_pots = [Pot(pot.x, pot.y, PotItem.Nothing, pot.room, pot.flags) for pot in old_pots]
|
||||
# sort in the order Hole, Switch, Key, Other, Nothing
|
||||
sort_order = {PotItem.Hole: 4, PotItem.Switch: 3, PotItem.Key: 2, PotItem.Nothing: 0}
|
||||
old_pots = sorted(old_pots, key=lambda pot: sort_order.get(pot.item, 1), reverse=True)
|
||||
|
||||
for old_pot in old_pots:
|
||||
if old_pot.item == PotItem.Nothing:
|
||||
break
|
||||
elif old_pot.item == PotItem.Hole:
|
||||
# Can only go in vanilla position (or the other big rock)
|
||||
available_pots = (pot for pot in new_pots if pot.x == old_pot.x and pot.y == old_pot.y)
|
||||
elif old_pot.item == PotItem.Switch:
|
||||
available_pots = (pot for pot in new_pots if (pot.room == old_pot.room or pot.room in movable_switch_rooms[old_pot.room]) and not (pot.flags & PotFlags.NoSwitch))
|
||||
elif old_pot.item == PotItem.Key:
|
||||
if world.doorShuffle[player] == 'vanilla' and not world.retro[player] and not world.keydropshuffle[player] and world.logic != 'nologic':
|
||||
available_pots = (pot for pot in new_pots if pot.room not in invalid_key_rooms)
|
||||
else:
|
||||
available_pots = new_pots
|
||||
else:
|
||||
available_pots = new_pots
|
||||
|
||||
available_pots = [pot for pot in available_pots if pot.item == PotItem.Nothing]
|
||||
|
||||
new_pot = random.choice(available_pots)
|
||||
new_pot.item = old_pot.item
|
||||
if world.retro[player] and new_pot.item == PotItem.FiveArrows:
|
||||
new_pot.item = PotItem.FiveRupees
|
||||
|
||||
if new_pot.item == PotItem.Key and new_pot.room != old_pot.room:
|
||||
# Move pot key to new room
|
||||
key = next(location for location in world.get_region(old_pot.room, player).locations if location.name in key_drop_data)
|
||||
world.get_region(old_pot.room, player).locations.remove(key)
|
||||
world.get_region(new_pot.room, player).locations.append(key)
|
||||
key.parent_region = world.get_region(new_pot.room, player)
|
||||
elif new_pot.item == PotItem.Switch and (new_pot.flags & PotFlags.SwitchLogicChange):
|
||||
if new_pot.room == 'PoD Basement Ledge':
|
||||
basement = world.get_region(old_pot.room, player)
|
||||
ledge = world.get_region(new_pot.room, player)
|
||||
ledge.locations.append(basement.locations.pop())
|
||||
elif new_pot.room == 'Swamp Push Statue':
|
||||
from Rules import set_rule
|
||||
set_rule(world.get_entrance('Swamp Push Statue NE', player), lambda state: state.has('Cane of Somaria', player))
|
||||
world.get_door('Swamp Push Statue NW', player).blocked = True
|
||||
elif new_pot.room == 'Thieves Attic Hint':
|
||||
# Rule is created based on barrier
|
||||
world.get_door('Thieves Attic ES', player).barrier(CrystalBarrier.Orange)
|
||||
else:
|
||||
raise Exception("Switch locattion in room %s requires logic change" % new_pot.room)
|
||||
|
||||
new_pot_contents[supertile] = new_pots
|
||||
|
||||
world.pot_contents[player] = new_pot_contents
|
||||
@@ -14,7 +14,11 @@ Please just DM me on discord for now. I (Aerinon) can be found at the [ALTTP Ran
|
||||
|
||||
# Installation
|
||||
|
||||
Clone this repository and then run ```DungeonRandomizer.py``` (requires Python 3).
|
||||
Install Python 3
|
||||
|
||||
Run ```pip install python-bps-continued```. On Linux, you should use pip3. On Windows, you may need to run ```python -m pip install python-bps-continued``` or ```py -m pip install python-bps-continued```.
|
||||
|
||||
Clone this repository then run ```DungeonRandomizer.py```.
|
||||
|
||||
Alternatively, run ```Gui.py``` for a simple graphical user interface. (WIP)
|
||||
|
||||
@@ -22,7 +26,7 @@ Alternatively, run ```Gui.py``` for a simple graphical user interface. (WIP)
|
||||
|
||||
Only extra settings are found here. All entrance randomizer settings are supported. See their [readme](https://github.com/KevinCathcart/ALttPEntranceRandomizer/blob/master/README.md)
|
||||
|
||||
## Door Shuffle
|
||||
## Door Shuffle (--doorShuffle)
|
||||
|
||||
### Basic
|
||||
|
||||
@@ -36,15 +40,52 @@ Doors are shuffled between dungeons as well.
|
||||
|
||||
Doors are not shuffled.
|
||||
|
||||
## Intensity
|
||||
## Intensity (--intensity number)
|
||||
|
||||
#### Level 1
|
||||
Normal door and spiral staircases are shuffled
|
||||
#### Level 2
|
||||
Same as Level 1 plus open edges and straight staircases are shuffled.
|
||||
#### Level 3 (Coming soon)
|
||||
#### Level 3
|
||||
Same as Level 2 plus Dungeon Lobbies are shuffled
|
||||
|
||||
## KeyDropShuffle (--keydropshuffle)
|
||||
|
||||
Adds 33 new locations to the randomization pool. The 32 small keys found under pots and dropped by enemies and the Big
|
||||
Key drop location are added to the pool. The keys normally found there are added to the item pool. Retro adds
|
||||
32 generic keys to the pool instead.
|
||||
|
||||
## Crossed Dungeon Specific Settings
|
||||
|
||||
### Mixed Travel (--mixed_travel value)
|
||||
|
||||
Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump two sections of a supertile that are
|
||||
otherwise unconnected logically can be reached using these glitches. To prevent the player from unintentionally changing
|
||||
dungeons while doing these tricks, you may use one of the following options.
|
||||
|
||||
#### Prevent (default)
|
||||
|
||||
Rails are added the 3 spots to prevent this tricks. This setting is recommend for those learning crossed dungeon mode to
|
||||
learn what is dangerous and what is not. No logic seeds ignore this setting.
|
||||
|
||||
#### Allow
|
||||
|
||||
The rooms are left alone and it is up to the discretion of the player whether to use these tricks or not.
|
||||
|
||||
#### Force
|
||||
|
||||
The two disjointed sections are forced to be in the same dungeon but the glitches are never logically required to complete that game.
|
||||
|
||||
### Standardize Palettes (--standardize_palettes)
|
||||
No effect if door shuffle is not on crossed
|
||||
|
||||
#### Standardize (default)
|
||||
Rooms in the same dungeon have their palettes changed to match. Hyrule Castle is split between Sewer and HC palette.
|
||||
Rooms adjacent to sanctuary get their coloring to match the Sanctuary's original palette.
|
||||
|
||||
#### Original
|
||||
Rooms/supertiles keep their original palettes.
|
||||
|
||||
|
||||
## Map/Compass/Small Key/Big Key shuffle (aka Keysanity)
|
||||
|
||||
@@ -81,13 +122,31 @@ Use to batch generate multiple seeds with same settings. If a seed number is pro
|
||||
Show the help message and exit.
|
||||
|
||||
```
|
||||
--door_shuffle
|
||||
--door_shuffle <mode>
|
||||
```
|
||||
|
||||
For specifying the door shuffle you want as above. (default: basic)
|
||||
|
||||
```
|
||||
--intensity
|
||||
--intensity <number>
|
||||
```
|
||||
|
||||
For specifying the door shuffle intensity level you want as above. (default: 2)
|
||||
|
||||
```
|
||||
--keydropshuffle
|
||||
```
|
||||
|
||||
Include mobs and pots drop in the item pool. (default: not enabled)
|
||||
|
||||
```
|
||||
--mixed_travel <mode>
|
||||
```
|
||||
|
||||
How to handle certain glitches in crossed dungeon mode. (default: prevent)
|
||||
|
||||
```
|
||||
--standardize_palettes (mode)
|
||||
```
|
||||
|
||||
Whether to standardize dungeon palettes in crossed dungeon mode. (default: standardize)
|
||||
+155
-28
@@ -1,38 +1,165 @@
|
||||
# New Features
|
||||
|
||||
* Crossed Dungeon generation improvements
|
||||
* Standard mode generation improvements
|
||||
* Spoiler lists bosses (multiworld compatible)
|
||||
* Bombs escape not valid for Crossed Dungeon
|
||||
* Graph algorithm speed improvement for placements and playthrough
|
||||
* TT Attic Hint tile should have a crystal switch accessible now
|
||||
* Updated to v.31.0.5
|
||||
## Lobby shuffle added as Intensity level 3
|
||||
|
||||
* Standard notes:
|
||||
* The sanctuary is vanilla, and will be missing the exit door until Zelda is rescued
|
||||
* In entrance shuffle the hyrule castle left and right exit door will be missing until Zelda is rescued. This
|
||||
replaces the rails that used to block those lobby exits
|
||||
* In non-entrance shuffle, Agahnims tower can be in logic if you have cape and/or Master sword, but you are never
|
||||
required to beat Agahnim 1 until Zelda is rescued.
|
||||
* Open notes:
|
||||
* The Sanctuary is limited to be in a LW dungeon unless you have ER Crossed or higher enabled
|
||||
* Mirroring from the Sanctuary to the new "Sanctuary" lobby is now in logic, as is exiting there.
|
||||
* In ER crossed or higher, if the Sanctuary is in the Dark World, Link starts as Bunny there until the Moon Pearl
|
||||
is found. Nothing inside that dungeon is in logic until the Moon Pearl is found. (Unless it is a multi-entrance
|
||||
dungeon that you can access from some LW entrance)
|
||||
* Lobby list is found in the spoiler
|
||||
* Exits for Multi-entrance dungeons after beating bosses now makes more sense. Generally you'll exit from a entrance
|
||||
from which the boss can logically be reached. If there are multiple, ones that do not lead to regions only accessible
|
||||
by connector are preferred. The exit is randomly chosen if there's no obvious preference. However, In certain poor
|
||||
cases like Skull Woods in ER, sometimes an exit is chosen not because you can reach the boss from there, but to
|
||||
prevent a potential forced S&Q.
|
||||
* Palette changes:
|
||||
* Certain doors/transition no longer have an effect on the palette choice (dead ends mostly or just bridges)
|
||||
* Sanctuary palette used on the adjacent rooms to Sanctuary (Sanctuary stays the dungeon color for now)
|
||||
* Sewer palette comes back for part of Hyrule Castle for areas "near" the sewer dropdown
|
||||
* There is a setting to keep original palettes (--standardize_palettes original)
|
||||
* Known issues:
|
||||
* Palettes are not perfect
|
||||
* Some ugly colors
|
||||
* Invisible floors can be see in many palettes
|
||||
|
||||
## Key Drop Shuffle
|
||||
|
||||
--keydropshuffle added. This add 33 new locations to the game where keys are found under pots
|
||||
and where enemies drop keys. This includes 32 small key location and the ball and chain guard who normally drop the HC
|
||||
Big Key.
|
||||
|
||||
* Overall location count updated
|
||||
* Setting mentioned in spoiler
|
||||
* Minor change: if a key is Universal or for that dungeon, then if will use the old mechanics of picking up the key without
|
||||
an entire pose and should be obtainable with the hookshot or boomerang as before
|
||||
|
||||
## --mixed_travel setting
|
||||
* Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump two sections of a supertile that are
|
||||
otherwise unconnected logically can be reach using these glitches. To prevent the player from unintentionally
|
||||
* prevent: Rails are added to the 3 spots to prevent these tricks. This setting is recommend for those learning
|
||||
crossed dungeon mode to learn what is dangerous and what is not. No logic seeds ignore this setting.
|
||||
* allow: The rooms are left alone and it is up to the discretion of the player whether to use these tricks or not.
|
||||
* force: The two disjointed sections are forced to be in the same dungeon but performing the glitches is never
|
||||
logically required to complete that game.
|
||||
|
||||
## Keysanity menu redesign
|
||||
|
||||
Redesign of Keysanity Menu complete for crossed dungeon and moved out of experimental.
|
||||
* First screen about Big Keys and Small Keys
|
||||
* 1st Column: The map is required for information about the Big Key
|
||||
* If you don't have the map, it'll be blank until you obtain the Big Key
|
||||
* If have the map:
|
||||
* 0 indicates there is no Big Key for that dungeon
|
||||
* A red symbol indicates the Ball N Chain guard has the big key for that dungeon (does not apply in
|
||||
--keydropshuffle)
|
||||
* Blank if there a big key but you haven't found it yet
|
||||
* 2nd Column displays the current number of keys for that dungeon. Suppressed in retro (always blank)
|
||||
* 3rd Column only displays if you have the map. It shows the number of keys left to collect for that dungeon. If
|
||||
--keydropshuffle is off, this does not count key drops. If on, it does.
|
||||
* (Note: the key columns can display up to 35 using the letters A-Z after 9)
|
||||
* Second screen about Maps / Compass
|
||||
* 1st Column: indicates if you have found the map or not for that dungeon
|
||||
* 2nd and 3rd Column: You must have the compass to see these columns. A two-digit display that shows you how
|
||||
many chests are left in the dungeon. If --keydropshuffle is off, this does not count key drops. If on, it does.
|
||||
|
||||
## Potshuffle by compiling
|
||||
|
||||
Same flag as before but uses python logic written by compiling instead of the enemizer logic-less version.
|
||||
|
||||
## Other features
|
||||
|
||||
### Spoiler log improvements
|
||||
|
||||
* In crossed mode, the new dungeon is listed along with the location designated by a '@' sign
|
||||
* Random gt crystals and ganon crystal are noted in the settings for better reproduction of seeds
|
||||
|
||||
### Experimental features
|
||||
|
||||
* Moved BK information and total chest keys per dungeon to keysanity menu. The info there requires compass for all info.
|
||||
* Map still required for on-hud key counter.
|
||||
* Added total counter to keysanity the compass/map screen when you have the compass for the dungeon.
|
||||
* Open "Edge" transitions can now be linked with normal doors
|
||||
* "Straight" staircases (the ones similar to normal doors) can be linked with both normal doors and edges
|
||||
* Only the item counter is currently experimental
|
||||
* Item counter is suppressed in Triforce Hunt
|
||||
|
||||
#### Couple of temporary debug features added:
|
||||
#### Temporary debug features
|
||||
|
||||
* Total item count displays where TFH's goal usually does
|
||||
* A red square appears in the upper right corner of the hud if the castle gate is closed
|
||||
* Removed the red square in the upper right corner of the hud if the castle gate is closed
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
* Fix for Animated Tiles in crossed dungeon
|
||||
* Stonewall hardlock no longer reachable from certain drops (Sewer Drop, some Skull Woods drops) that were previously possible
|
||||
* No logic uses less key door logic
|
||||
* Spoiler log encoding
|
||||
* Enemizer settings made consistent with website
|
||||
* Swamp flooded ladders in the basement now requires Flippers
|
||||
* PoD EG Glitch gets killed on transitions (Only when DR is on)
|
||||
* Problem with standard logic fixed wanting you to pass through the tapestry backwards to rescue Zelda
|
||||
* Fixed SRAM corruption issues
|
||||
* Problem with the dungeons requiring you to take Blind through her attic fixed. (Maiden no longer despawns)
|
||||
* Hyrule Castle will not be your DW access in various Entrance Shuffles: simple, restricted, dungeonssimple, dungeonsfull
|
||||
(Also prevents getting stuck in TR opening)
|
||||
* Beatable only (accessibility: none) no longer fails when there are unplaced items
|
||||
* 2.0.20u
|
||||
* Problem with Desert Wall not being pre-opened in intensity 3 fixed
|
||||
* 2.0.19u
|
||||
* Generation improvement
|
||||
* Possible fix for shop vram corruption
|
||||
* The Cane of Byrna does not count as a chest key anymore
|
||||
* 2.0.18u
|
||||
* Generation improvements
|
||||
* Bombs/Dash doors more consistent with the amount in vanilla.
|
||||
* 2.0.17u
|
||||
* Generation improvements
|
||||
* 2.0.16u
|
||||
* Prevent HUD from showing key counter when in the overworld. (Aga 2 doesn't always clear the dungeon indicator)
|
||||
* Fixed key logic regarding certain isolated "important" locations
|
||||
* Fixed a problem with keydropshuffle thinking certain progression items are keys
|
||||
* A couple of inverted rules fixed
|
||||
* A more accurate count of which locations are blocked by teh big key in Ganon's Tower
|
||||
* Updated base rom to 31.0.7 (includes potential hera basement cage fix)
|
||||
* 2.0.15u
|
||||
* Allow Aga Tower lobby door as a a paired keydoor (typo)
|
||||
* Fix portal check for multi-entrance dungeons
|
||||
* 2.0.14u
|
||||
* Removal of key doors no longer messes up certain lobbies
|
||||
* Fixed ER entrances when Desert Back is a connector
|
||||
* 2.0.13u
|
||||
* Minor portal re-work for certain logic and spoiler information
|
||||
* Repaired certain exits wrongly affected by Sanctuary placement (ER crossed + intensity 3)
|
||||
* Fix for inverted ER + intensity 3
|
||||
* Fix for current small keys missing on keysanity menu
|
||||
* Logic added for cases where you can flood Swamp Trench 1 before finding flippers and lock yourself out of getting
|
||||
something behind the trench that leads to the flippers
|
||||
* 2.0.12u
|
||||
* Another fix for animated tiles (fairy fountains)
|
||||
* GT Big Key stat fixed on credits
|
||||
* Any denomination of rupee 20 or below can be removed to make room for Crossed Dungeon's extra dungeon items. This
|
||||
helps retro generate more often.
|
||||
* Fix for TR Lobbies in intensity 3 and ER shuffles that was causing a hardlock
|
||||
* Standard ER logic revised for lobby shuffle and rain state considerations.
|
||||
* 2.0.11u
|
||||
* Fix output path setting in settings.json
|
||||
* Fix trock entrances when intensity <= 2
|
||||
* 2.0.10u
|
||||
* Fix POD, TR, GT and SKULL 3 entrances if sanc ends up in that dungeon in crossed ER+
|
||||
* TR Lobbies that need a bomb and can be entered before bombing should be pre-opened
|
||||
* Animated tiles are loaded correctly in lobbies
|
||||
* If a wallmaster grabs you and the lobby is dark, the lamp turns on now
|
||||
* Certain key rules no longer override item requirements (e.g. Somaria behind TR Hub)
|
||||
* Old Man Cave is correctly one way in the graph
|
||||
* Some key logic fixes
|
||||
* 2.0.9-u
|
||||
* /missing command in MultiClient fixed
|
||||
* 2.0.8-u
|
||||
* Player sprite disappears after picking up a key drop in keydropshuffle
|
||||
* Sewers and Hyrule Castle compass problems
|
||||
* Double count of the Hera Basement Cage item (both overall and compass)
|
||||
* Unnecessary/inconsistent rug cutoff
|
||||
* TR Crystal Maze thought you get through backwards without Somaria
|
||||
* Ensure Thieves Attic Window area can always be reached
|
||||
* Fixed where HC big key was not counted
|
||||
* Prior fixes
|
||||
* Fixed a situation where logic did not account properly for Big Key doors in standard Hyrule Castle
|
||||
* Fixed a problem ER shuffle generation that did not account for lobbies moving around
|
||||
* Fixed a problem with camera unlock (GT Mimics and Mire Minibridge)
|
||||
* Fixed a problem with bad-pseudo layer at PoD map Balcony (unable to hit switch with Bomb)
|
||||
* Fixed a problem with the Ganon hint when hints are turned off
|
||||
|
||||
# Known Issues
|
||||
|
||||
* Potenial keylocks in multi-entrance dungeons
|
||||
* Incorrect vanilla keylogic for Mire
|
||||
* ER - Potential for Skull Woods West to be completely inaccessible in non-beatable logic
|
||||
+144
-79
@@ -4,7 +4,7 @@ from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType
|
||||
|
||||
def create_regions(world, player):
|
||||
world.regions += [
|
||||
create_lw_region(player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q']),
|
||||
create_menu_region(player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q']),
|
||||
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'],
|
||||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam',
|
||||
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||
@@ -100,7 +100,8 @@ def create_regions(world, player):
|
||||
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']),
|
||||
create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
|
||||
|
||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']),
|
||||
create_cave_region(player, 'Old Man Cave Ledge', 'a connector', None, ['Old Man Cave Exit (West)', 'Old Man Cave Dropdown']),
|
||||
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
|
||||
@@ -198,18 +199,45 @@ def create_regions(world, player):
|
||||
create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']),
|
||||
]
|
||||
|
||||
|
||||
def create_dungeon_regions(world, player):
|
||||
std_flag = world.mode[player] == 'standard'
|
||||
inv_flag = world.mode[player] == 'inverted'
|
||||
world.regions += [
|
||||
create_dungeon_region(player, 'Hyrule Castle Lobby', 'Hyrule Castle', None, ['Hyrule Castle Lobby W', 'Hyrule Castle Lobby E',
|
||||
'Hyrule Castle Lobby WN', 'Hyrule Castle Lobby North Stairs', 'Hyrule Castle Exit (South)']),
|
||||
create_dungeon_region(player, 'Hyrule Castle West Lobby', 'Hyrule Castle', None, ['Hyrule Castle West Lobby E', 'Hyrule Castle West Lobby N',
|
||||
'Hyrule Castle West Lobby EN', 'Hyrule Castle Exit (West)']),
|
||||
create_dungeon_region(player, 'Hyrule Castle East Lobby', 'Hyrule Castle', None, ['Hyrule Castle East Lobby W', 'Hyrule Castle East Lobby N',
|
||||
'Hyrule Castle East Lobby NW', 'Hyrule Castle Exit (East)']),
|
||||
create_dungeon_region(player, 'Hyrule Castle East Hall', 'Hyrule Castle', None, ['Hyrule Castle East Hall W', 'Hyrule Castle East Hall S',
|
||||
'Hyrule Castle East Hall SW']),
|
||||
create_dungeon_region(player, 'Sanctuary Portal', 'Hyrule Castle', None, ['Sanctuary Exit', 'Enter HC (Sanc)']),
|
||||
create_dungeon_region(player, 'Hyrule Castle West Portal', 'Hyrule Castle', None, ['Hyrule Castle Exit (West)', 'Enter HC (West)']),
|
||||
create_dungeon_region(player, 'Hyrule Castle South Portal', 'Hyrule Castle', None, ['Hyrule Castle Exit (South)', 'Enter HC (South)']),
|
||||
create_dungeon_region(player, 'Hyrule Castle East Portal', 'Hyrule Castle', None, ['Hyrule Castle Exit (East)', 'Enter HC (East)']),
|
||||
create_dungeon_region(player, 'Eastern Portal', 'Eastern Palace', None, ['Eastern Palace Exit', 'Enter Eastern Palace']),
|
||||
create_dungeon_region(player, 'Desert West Portal', 'Desert Palace', None, ['Desert Palace Exit (West)', 'Enter Desert (West)']),
|
||||
create_dungeon_region(player, 'Desert South Portal', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Enter Desert (South)']),
|
||||
create_dungeon_region(player, 'Desert East Portal', 'Desert Palace', None, ['Desert Palace Exit (East)', 'Enter Desert (East)']),
|
||||
create_dungeon_region(player, 'Desert Back Portal', 'Desert Palace', None, ['Desert Palace Exit (North)', 'Enter Desert (North)']),
|
||||
create_dungeon_region(player, 'Hera Portal', 'Tower of Hera', None, ['Tower of Hera Exit', 'Enter Hera']),
|
||||
create_dungeon_region(player, 'Agahnims Tower Portal', 'Castle Tower', None, [inv_flag and 'Inverted Agahnims Tower Exit' or 'Agahnims Tower Exit', 'Enter Agahnims Tower']),
|
||||
create_dungeon_region(player, 'Palace of Darkness Portal', 'Palace of Darkness', None, ['Palace of Darkness Exit', 'Enter Palace of Darkness']),
|
||||
create_dungeon_region(player, 'Swamp Portal', 'Swamp Palace', None, ['Swamp Palace Exit', 'Enter Swamp']),
|
||||
create_dungeon_region(player, 'Skull 1 Portal', 'Skull Woods', None, ['Skull Woods First Section Exit', 'Enter Skull Woods 1']),
|
||||
create_dungeon_region(player, 'Skull 2 East Portal', 'Skull Woods', None, ['Skull Woods Second Section Exit (East)', 'Enter Skull Woods 2 (East)']),
|
||||
create_dungeon_region(player, 'Skull 2 West Portal', 'Skull Woods', None, ['Skull Woods Second Section Exit (West)', 'Enter Skull Woods 2 (West)']),
|
||||
create_dungeon_region(player, 'Skull 3 Portal', 'Skull Woods', None, ['Skull Woods Final Section Exit', 'Enter Skull Woods 3']),
|
||||
create_dungeon_region(player, 'Thieves Town Portal', "Thieves' Town", None, ['Thieves Town Exit', 'Enter Thieves Town']),
|
||||
create_dungeon_region(player, 'Ice Portal', 'Ice Palace', None, ['Ice Palace Exit', 'Enter Ice Palace']),
|
||||
create_dungeon_region(player, 'Mire Portal', 'Misery Mire', None, ['Misery Mire Exit', 'Enter Misery Mire']),
|
||||
create_dungeon_region(player, 'Turtle Rock Main Portal', 'Turtle Rock', None, ['Turtle Rock Exit (Front)', 'Enter Turtle Rock (Main)']),
|
||||
create_dungeon_region(player, 'Turtle Rock Lazy Eyes Portal', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (West)', 'Enter Turtle Rock (Lazy Eyes)']),
|
||||
create_dungeon_region(player, 'Turtle Rock Chest Portal', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (East)', 'Enter Turtle Rock (Chest)']),
|
||||
create_dungeon_region(player, 'Turtle Rock Eye Bridge Portal', 'Turtle Rock', None, ['Turtle Rock Isolated Ledge Exit', 'Enter Turtle Rock (Laser Bridge)']),
|
||||
create_dungeon_region(player, 'Ganons Tower Portal', "Ganon's Tower", None, [inv_flag and 'Inverted Ganons Tower Exit' or 'Ganons Tower Exit', 'Enter Ganons Tower']),
|
||||
|
||||
create_dungeon_region(player, 'Hyrule Castle Lobby', 'Hyrule Castle', None,
|
||||
['Hyrule Castle Lobby W', 'Hyrule Castle Lobby E', 'Hyrule Castle Lobby WN', 'Hyrule Castle Lobby North Stairs', 'Hyrule Castle Lobby S']),
|
||||
create_dungeon_region(player, 'Hyrule Castle West Lobby', 'Hyrule Castle', None,
|
||||
['Hyrule Castle West Lobby E', 'Hyrule Castle West Lobby N', 'Hyrule Castle West Lobby EN', 'Hyrule Castle West Lobby S']),
|
||||
create_dungeon_region(player, 'Hyrule Castle East Lobby', 'Hyrule Castle', None,
|
||||
['Hyrule Castle East Lobby W', 'Hyrule Castle East Lobby N', 'Hyrule Castle East Lobby NW', 'Hyrule Castle East Lobby S']),
|
||||
create_dungeon_region(player, 'Hyrule Castle East Hall', 'Hyrule Castle', None,
|
||||
['Hyrule Castle East Hall W', 'Hyrule Castle East Hall S', 'Hyrule Castle East Hall SW']),
|
||||
create_dungeon_region(player, 'Hyrule Castle West Hall', 'Hyrule Castle', None, ['Hyrule Castle West Hall E', 'Hyrule Castle West Hall S']),
|
||||
create_dungeon_region(player, 'Hyrule Castle Back Hall', 'Hyrule Castle', None, ['Hyrule Castle Back Hall E', 'Hyrule Castle Back Hall W', 'Hyrule Castle Back Hall Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Castle Throne Room', 'Hyrule Castle', None, ['Hyrule Castle Throne Room Tapestry', 'Hyrule Castle Throne Room South Stairs']),
|
||||
@@ -225,10 +253,11 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Armory Boomerang', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Boomerang Guard Key Drop'], ['Hyrule Dungeon Armory Boomerang WS']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Armory North Branch', 'Hyrule Castle', None, ['Hyrule Dungeon Armory Interior Key Door S', 'Hyrule Dungeon Armory Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Staircase', 'Hyrule Castle', None, ['Hyrule Dungeon Staircase Up Stairs', 'Hyrule Dungeon Staircase Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Cellblock', 'Hyrule Castle',
|
||||
['Hyrule Castle - Big Key Drop', 'Hyrule Castle - Zelda\'s Chest'] if not std_flag else
|
||||
['Hyrule Castle - Big Key Drop', 'Hyrule Castle - Zelda\'s Chest', 'Zelda Pickup'],
|
||||
['Hyrule Dungeon Cellblock Up Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Cellblock', 'Hyrule Castle', ['Hyrule Castle - Big Key Drop'], ['Hyrule Dungeon Cellblock Up Stairs', 'Hyrule Dungeon Cellblock Door']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Cell', 'Hyrule Castle',
|
||||
["Hyrule Castle - Zelda's Chest"] if not std_flag else
|
||||
["Hyrule Castle - Zelda's Chest", 'Zelda Pickup'],
|
||||
['Hyrule Dungeon Cell Exit']),
|
||||
|
||||
|
||||
create_dungeon_region(player, 'Sewers Behind Tapestry', 'Hyrule Castle', None, ['Sewers Behind Tapestry S', 'Sewers Behind Tapestry Down Stairs']),
|
||||
@@ -244,10 +273,10 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Sewers Pull Switch', 'Hyrule Castle', None, ['Sewers Pull Switch N', 'Sewers Pull Switch S']),
|
||||
create_dungeon_region(player, 'Sanctuary', 'Hyrule Castle',
|
||||
['Sanctuary'] if not std_flag else ['Sanctuary', 'Zelda Drop Off'],
|
||||
['Sanctuary Exit', 'Sanctuary N']),
|
||||
['Sanctuary S', 'Sanctuary N', 'Sanctuary Mirror Route']),
|
||||
|
||||
# Eastern Palace
|
||||
create_dungeon_region(player, 'Eastern Lobby', 'Eastern Palace', None, ['Eastern Lobby N', 'Eastern Palace Exit', 'Eastern Lobby NW', 'Eastern Lobby NE']),
|
||||
create_dungeon_region(player, 'Eastern Lobby', 'Eastern Palace', None, ['Eastern Lobby N', 'Eastern Lobby S', 'Eastern Lobby NW', 'Eastern Lobby NE']),
|
||||
create_dungeon_region(player, 'Eastern Lobby Bridge', 'Eastern Palace', None, ['Eastern Lobby Bridge S', 'Eastern Lobby Bridge N']),
|
||||
create_dungeon_region(player, 'Eastern Lobby Left Ledge', 'Eastern Palace', None, ['Eastern Lobby Left Ledge SW']),
|
||||
create_dungeon_region(player, 'Eastern Lobby Right Ledge', 'Eastern Palace', None, ['Eastern Lobby Right Ledge SE']),
|
||||
@@ -279,11 +308,11 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Eastern Boss', 'Eastern Palace', ['Eastern Palace - Boss', 'Eastern Palace - Prize'], ['Eastern Boss SE']),
|
||||
|
||||
# Desert Palace
|
||||
create_dungeon_region(player, 'Desert Main Lobby', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Main Lobby N Edge', 'Desert Main Lobby Left Path', 'Desert Main Lobby Right Path']),
|
||||
create_dungeon_region(player, 'Desert Main Lobby', 'Desert Palace', None, ['Desert Main Lobby S', 'Desert Main Lobby N Edge', 'Desert Main Lobby Left Path', 'Desert Main Lobby Right Path']),
|
||||
create_dungeon_region(player, 'Desert Left Alcove', 'Desert Palace', None, ['Desert Main Lobby NW Edge', 'Desert Left Alcove Path']),
|
||||
create_dungeon_region(player, 'Desert Right Alcove', 'Desert Palace', None, ['Desert Main Lobby NE Edge', 'Desert Main Lobby E Edge', 'Desert Right Alcove Path']),
|
||||
create_dungeon_region(player, 'Desert Dead End', 'Desert Palace', None, ['Desert Dead End Edge']),
|
||||
create_dungeon_region(player, 'Desert East Lobby', 'Desert Palace', None, ['Desert East Lobby WS', 'Desert Palace Exit (East)']),
|
||||
create_dungeon_region(player, 'Desert East Lobby', 'Desert Palace', None, ['Desert East Lobby WS', 'Desert East Lobby S']),
|
||||
create_dungeon_region(player, 'Desert East Wing', 'Desert Palace', None, ['Desert East Wing ES', 'Desert East Wing Key Door EN', 'Desert East Wing W Edge', 'Desert East Wing N Edge']),
|
||||
create_dungeon_region(player, 'Desert Compass Room', 'Desert Palace', ['Desert Palace - Compass Chest'], ['Desert Compass Key Door WN', 'Desert Compass NW']),
|
||||
create_dungeon_region(player, 'Desert Cannonball', 'Desert Palace', ['Desert Palace - Big Key Chest'], ['Desert Cannonball S']),
|
||||
@@ -296,9 +325,9 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Desert Circle of Pots', 'Desert Palace', None, ['Desert Circle of Pots ES', 'Desert Circle of Pots NW']),
|
||||
create_dungeon_region(player, 'Desert Big Chest Room', 'Desert Palace', ['Desert Palace - Big Chest'], ['Desert Big Chest SW']),
|
||||
create_dungeon_region(player, 'Desert West Wing', 'Desert Palace', None, ['Desert West Wing N Edge', 'Desert West Wing WS']),
|
||||
create_dungeon_region(player, 'Desert West Lobby', 'Desert Palace', None, ['Desert West Lobby ES', 'Desert Palace Exit (West)', 'Desert West Lobby NW']),
|
||||
create_dungeon_region(player, 'Desert West Lobby', 'Desert Palace', None, ['Desert West Lobby ES', 'Desert West S', 'Desert West Lobby NW']),
|
||||
create_dungeon_region(player, 'Desert Fairy Fountain', 'Desert Palace', None, ['Desert Fairy Fountain SW']),
|
||||
create_dungeon_region(player, 'Desert Back Lobby', 'Desert Palace', None, ['Desert Palace Exit (North)', 'Desert Back Lobby NW']),
|
||||
create_dungeon_region(player, 'Desert Back Lobby', 'Desert Palace', None, ['Desert Back Lobby S', 'Desert Back Lobby NW']),
|
||||
create_dungeon_region(player, 'Desert Tiles 1', 'Desert Palace', ['Desert Palace - Desert Tiles 1 Pot Key'], ['Desert Tiles 1 SW', 'Desert Tiles 1 Up Stairs']),
|
||||
create_dungeon_region(player, 'Desert Bridge', 'Desert Palace', None, ['Desert Bridge Down Stairs', 'Desert Bridge SW']),
|
||||
create_dungeon_region(player, 'Desert Four Statues', 'Desert Palace', None, ['Desert Four Statues NW', 'Desert Four Statues ES']),
|
||||
@@ -308,7 +337,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Desert Boss', 'Desert Palace', ['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Boss SW']),
|
||||
|
||||
# Hera
|
||||
create_dungeon_region(player, 'Hera Lobby', 'Tower of Hera', ['Tower of Hera - Map Chest'], ['Hera Lobby Down Stairs', 'Hera Lobby Key Stairs', 'Hera Lobby Up Stairs', 'Tower of Hera Exit']),
|
||||
create_dungeon_region(player, 'Hera Lobby', 'Tower of Hera', ['Tower of Hera - Map Chest'], ['Hera Lobby Down Stairs', 'Hera Lobby Key Stairs', 'Hera Lobby Up Stairs', 'Hera Lobby S']),
|
||||
create_dungeon_region(player, 'Hera Basement Cage', 'Tower of Hera', ['Tower of Hera - Basement Cage'], ['Hera Basement Cage Up Stairs']),
|
||||
create_dungeon_region(player, 'Hera Tile Room', 'Tower of Hera', None, ['Hera Tile Room Up Stairs', 'Hera Tile Room EN']),
|
||||
create_dungeon_region(player, 'Hera Tridorm', 'Tower of Hera', None, ['Hera Tridorm WN', 'Hera Tridorm SE']),
|
||||
@@ -323,7 +352,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Hera Boss', 'Tower of Hera', ['Tower of Hera - Boss', 'Tower of Hera - Prize'], ['Hera Boss Down Stairs', 'Hera Boss Outer Hole', 'Hera Boss Inner Hole']),
|
||||
|
||||
# AgaTower
|
||||
create_dungeon_region(player, 'Tower Lobby', 'Castle Tower', None, ['Tower Lobby NW', inv_flag and 'Inverted Agahnims Tower Exit' or 'Agahnims Tower Exit']),
|
||||
create_dungeon_region(player, 'Tower Lobby', 'Castle Tower', None, ['Tower Lobby NW', 'Tower Lobby S']),
|
||||
create_dungeon_region(player, 'Tower Gold Knights', 'Castle Tower', None, ['Tower Gold Knights SW', 'Tower Gold Knights EN']),
|
||||
create_dungeon_region(player, 'Tower Room 03', 'Castle Tower', ['Castle Tower - Room 03'], ['Tower Room 03 WN', 'Tower Room 03 Up Stairs']),
|
||||
create_dungeon_region(player, 'Tower Lone Statue', 'Castle Tower', None, ['Tower Lone Statue Down Stairs', 'Tower Lone Statue WN']),
|
||||
@@ -343,7 +372,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Tower Agahnim 1', 'Castle Tower', ['Agahnim 1'], ['Tower Agahnim 1 SW']),
|
||||
|
||||
# pod
|
||||
create_dungeon_region(player, 'PoD Lobby', 'Palace of Darkness', None, ['PoD Lobby N', 'PoD Lobby NW', 'PoD Lobby NE', 'Palace of Darkness Exit']),
|
||||
create_dungeon_region(player, 'PoD Lobby', 'Palace of Darkness', None, ['PoD Lobby N', 'PoD Lobby NW', 'PoD Lobby NE', 'PoD Lobby S']),
|
||||
create_dungeon_region(player, 'PoD Left Cage', 'Palace of Darkness', None, ['PoD Left Cage SW', 'PoD Left Cage Down Stairs']),
|
||||
create_dungeon_region(player, 'PoD Middle Cage', 'Palace of Darkness', None, ['PoD Middle Cage S', 'PoD Middle Cage SE', 'PoD Middle Cage N', 'PoD Middle Cage Down Stairs']),
|
||||
create_dungeon_region(player, 'PoD Shooter Room', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['PoD Shooter Room Up Stairs']),
|
||||
@@ -381,7 +410,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'PoD Boss', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize'], ['PoD Boss SE']),
|
||||
|
||||
# swamp
|
||||
create_dungeon_region(player, 'Swamp Lobby', 'Swamp Palace', None, ['Swamp Palace Exit', 'Swamp Lobby Moat']),
|
||||
create_dungeon_region(player, 'Swamp Lobby', 'Swamp Palace', None, ['Swamp Lobby S', 'Swamp Lobby Moat']),
|
||||
create_dungeon_region(player, 'Swamp Entrance', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Entrance Down Stairs', 'Swamp Entrance Moat']),
|
||||
create_dungeon_region(player, 'Swamp Pot Row', 'Swamp Palace', ['Swamp Palace - Pot Row Pot Key'], ['Swamp Pot Row Up Stairs', 'Swamp Pot Row WN', 'Swamp Pot Row WS']),
|
||||
create_dungeon_region(player, 'Swamp Map Ledge', 'Swamp Palace', ['Swamp Palace - Map Chest'], ['Swamp Map Ledge EN']),
|
||||
@@ -416,9 +445,9 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Swamp Right Elbow', 'Swamp Palace', None, ['Swamp Right Elbow SE', 'Swamp Right Elbow Down Stairs']),
|
||||
create_dungeon_region(player, 'Swamp Drain Left', 'Swamp Palace', None, ['Swamp Drain Left Up Stairs', 'Swamp Drain WN']),
|
||||
create_dungeon_region(player, 'Swamp Drain Right', 'Swamp Palace', ['Swamp Drain'], ['Swamp Drain Right Switch', 'Swamp Drain Right Up Stairs']),
|
||||
# This is intentionally odd so I don't have to treat the WS door in the Flooded Room oddly (because of how it works when going backward)
|
||||
create_dungeon_region(player, 'Swamp Flooded Room', 'Swamp Palace', None, ['Swamp Flooded Room Up Stairs', 'Swamp Flooded Room Ladder', 'Swamp Flooded Room WS']),
|
||||
create_dungeon_region(player, 'Swamp Flooded Spot', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right'], ['Swamp Flooded Spot Ladder']),
|
||||
create_dungeon_region(player, 'Swamp Flooded Room', 'Swamp Palace', None, ['Swamp Flooded Room Up Stairs', 'Swamp Flooded Room Ladder']),
|
||||
# this is more normal and allows getting the chests from doing this room backward in logic
|
||||
create_dungeon_region(player, 'Swamp Flooded Spot', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right'], ['Swamp Flooded Room WS', 'Swamp Flooded Spot Ladder']),
|
||||
create_dungeon_region(player, 'Swamp Basement Shallows', 'Swamp Palace', None, ['Swamp Basement Shallows NW', 'Swamp Basement Shallows EN', 'Swamp Basement Shallows ES']),
|
||||
create_dungeon_region(player, 'Swamp Waterfall Room', 'Swamp Palace', ['Swamp Palace - Waterfall Room'], ['Swamp Waterfall Room SW', 'Swamp Waterfall Room NW', 'Swamp Waterfall Room NE']),
|
||||
create_dungeon_region(player, 'Swamp Refill', 'Swamp Palace', None, ['Swamp Refill SW']),
|
||||
@@ -430,7 +459,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Swamp Boss', 'Swamp Palace', ['Swamp Palace - Boss', 'Swamp Palace - Prize'], ['Swamp Boss SW']),
|
||||
|
||||
# sw
|
||||
create_dungeon_region(player, 'Skull 1 Lobby', 'Skull Woods', None, ['Skull Woods First Section Exit', 'Skull 1 Lobby WS', 'Skull 1 Lobby ES']),
|
||||
create_dungeon_region(player, 'Skull 1 Lobby', 'Skull Woods', None, ['Skull 1 Lobby S', 'Skull 1 Lobby WS', 'Skull 1 Lobby ES']),
|
||||
create_dungeon_region(player, 'Skull Map Room', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Map Room WS', 'Skull Map Room SE']),
|
||||
create_dungeon_region(player, 'Skull Pot Circle', 'Skull Woods', None, ['Skull Pot Circle WN', 'Skull Pot Circle Star Path']),
|
||||
create_dungeon_region(player, 'Skull Pull Switch', 'Skull Woods', None, ['Skull Pull Switch EN', 'Skull Pull Switch S']),
|
||||
@@ -439,14 +468,14 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Skull Pot Prison', 'Skull Woods', ['Skull Woods - Pot Prison'], ['Skull Pot Prison ES', 'Skull Pot Prison SE']),
|
||||
create_dungeon_region(player, 'Skull Compass Room', 'Skull Woods', ['Skull Woods - Compass Chest'], ['Skull Compass Room NE', 'Skull Compass Room ES', 'Skull Compass Room WS']),
|
||||
create_dungeon_region(player, 'Skull Left Drop', 'Skull Woods', None, ['Skull Left Drop ES']),
|
||||
create_dungeon_region(player, 'Skull 2 East Lobby', 'Skull Woods', None, ['Skull 2 East Lobby NW', 'Skull 2 East Lobby WS', 'Skull Woods Second Section Exit (East)']),
|
||||
create_dungeon_region(player, 'Skull 2 East Lobby', 'Skull Woods', None, ['Skull 2 East Lobby NW', 'Skull 2 East Lobby WS', 'Skull 2 East Lobby SW']),
|
||||
create_dungeon_region(player, 'Skull Big Key', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Big Key SW', 'Skull Big Key WN']),
|
||||
create_dungeon_region(player, 'Skull Lone Pot', 'Skull Woods', None, ['Skull Lone Pot EN']),
|
||||
create_dungeon_region(player, 'Skull Small Hall', 'Skull Woods', None, ['Skull Small Hall ES', 'Skull Small Hall WS']),
|
||||
create_dungeon_region(player, 'Skull Back Drop', 'Skull Woods', None, ['Skull Back Drop Star Path', ]),
|
||||
create_dungeon_region(player, 'Skull 2 West Lobby', 'Skull Woods', ['Skull Woods - West Lobby Pot Key'], ['Skull 2 West Lobby ES', 'Skull 2 West Lobby NW', 'Skull Woods Second Section Exit (West)']),
|
||||
create_dungeon_region(player, 'Skull 2 West Lobby', 'Skull Woods', ['Skull Woods - West Lobby Pot Key'], ['Skull 2 West Lobby ES', 'Skull 2 West Lobby NW', 'Skull 2 West Lobby S']),
|
||||
create_dungeon_region(player, 'Skull X Room', 'Skull Woods', None, ['Skull X Room SW']),
|
||||
create_dungeon_region(player, 'Skull 3 Lobby', 'Skull Woods', None, ['Skull 3 Lobby NW', 'Skull 3 Lobby EN', 'Skull Woods Final Section Exit']),
|
||||
create_dungeon_region(player, 'Skull 3 Lobby', 'Skull Woods', None, ['Skull 3 Lobby NW', 'Skull 3 Lobby EN', 'Skull 3 Lobby SW']),
|
||||
create_dungeon_region(player, 'Skull East Bridge', 'Skull Woods', None, ['Skull East Bridge WN', 'Skull East Bridge WS']),
|
||||
create_dungeon_region(player, 'Skull West Bridge Nook', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull West Bridge Nook ES']),
|
||||
create_dungeon_region(player, 'Skull Star Pits', 'Skull Woods', None, ['Skull Star Pits SW', 'Skull Star Pits ES']),
|
||||
@@ -457,7 +486,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Skull Boss', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
|
||||
|
||||
# tt
|
||||
create_dungeon_region(player, 'Thieves Lobby', 'Thieves\' Town', ['Thieves\' Town - Map Chest'], ['Thieves Town Exit', 'Thieves Lobby N Edge', 'Thieves Lobby NE Edge', 'Thieves Lobby E']),
|
||||
create_dungeon_region(player, 'Thieves Lobby', 'Thieves\' Town', ['Thieves\' Town - Map Chest'], ['Thieves Lobby S', 'Thieves Lobby N Edge', 'Thieves Lobby NE Edge', 'Thieves Lobby E']),
|
||||
create_dungeon_region(player, 'Thieves Ambush', 'Thieves\' Town', ['Thieves\' Town - Ambush Chest'], ['Thieves Ambush S Edge', 'Thieves Ambush SE Edge', 'Thieves Ambush ES Edge', 'Thieves Ambush EN Edge', 'Thieves Ambush E']),
|
||||
create_dungeon_region(player, 'Thieves Rail Ledge', 'Thieves\' Town', None, ['Thieves Rail Ledge NW', 'Thieves Rail Ledge W', 'Thieves Rail Ledge Drop Down']),
|
||||
create_dungeon_region(player, 'Thieves BK Corner', 'Thieves\' Town', None, ['Thieves BK Corner WN Edge', 'Thieves BK Corner WS Edge', 'Thieves BK Corner S Edge', 'Thieves BK Corner SW Edge', 'Thieves BK Corner NE']),
|
||||
@@ -483,14 +512,15 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Thieves Basement Block', 'Thieves\' Town', None, ['Thieves Basement Block Up Stairs', 'Thieves Basement Block WN', 'Thieves Basement Block Path']),
|
||||
create_dungeon_region(player, 'Thieves Blocked Entry', 'Thieves\' Town', None, ['Thieves Blocked Entry Path', 'Thieves Blocked Entry SW']),
|
||||
create_dungeon_region(player, 'Thieves Lonely Zazak', 'Thieves\' Town', None, ['Thieves Lonely Zazak WS', 'Thieves Lonely Zazak ES', 'Thieves Lonely Zazak NW']),
|
||||
create_dungeon_region(player, 'Thieves Blind\'s Cell', 'Thieves\' Town', ['Thieves\' Town - Blind\'s Cell', 'Suspicious Maiden'], ['Thieves Blind\'s Cell WS']),
|
||||
create_dungeon_region(player, "Thieves Blind's Cell", 'Thieves\' Town', None, ["Thieves Blind's Cell WS", "Thieves Blind's Cell Door"]),
|
||||
create_dungeon_region(player, "Thieves Blind's Cell Interior", 'Thieves\' Town', ['Thieves\' Town - Blind\'s Cell', 'Suspicious Maiden'], ["Thieves Blind's Cell Exit"]),
|
||||
create_dungeon_region(player, 'Thieves Conveyor Bridge', 'Thieves\' Town', None, ['Thieves Conveyor Bridge EN', 'Thieves Conveyor Bridge ES', 'Thieves Conveyor Bridge WS', 'Thieves Conveyor Bridge Block Path']),
|
||||
create_dungeon_region(player, 'Thieves Conveyor Block', 'Thieves\' Town', None, ['Thieves Conveyor Block Path', 'Thieves Conveyor Block WN']),
|
||||
create_dungeon_region(player, 'Thieves Big Chest Room', 'Thieves\' Town', ['Thieves\' Town - Big Chest'], ['Thieves Big Chest Room ES']),
|
||||
create_dungeon_region(player, 'Thieves Trap', 'Thieves\' Town', None, ['Thieves Trap EN']),
|
||||
|
||||
# ice
|
||||
create_dungeon_region(player, 'Ice Lobby', 'Ice Palace', None, ['Ice Palace Exit', 'Ice Lobby WS']),
|
||||
create_dungeon_region(player, 'Ice Lobby', 'Ice Palace', None, ['Ice Lobby SE', 'Ice Lobby WS']),
|
||||
create_dungeon_region(player, 'Ice Jelly Key', 'Ice Palace', ['Ice Palace - Jelly Key Drop'], ['Ice Jelly Key ES', 'Ice Jelly Key Down Stairs']),
|
||||
create_dungeon_region(player, 'Ice Floor Switch', 'Ice Palace', None, ['Ice Floor Switch Up Stairs', 'Ice Floor Switch ES']),
|
||||
create_dungeon_region(player, 'Ice Cross Left', 'Ice Palace', None, ['Ice Cross Left WS', 'Ice Cross Left Push Block']),
|
||||
@@ -538,7 +568,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Ice Boss', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
|
||||
|
||||
# mire
|
||||
create_dungeon_region(player, 'Mire Lobby', 'Misery Mire', None, ['Misery Mire Exit', 'Mire Lobby Gap']),
|
||||
create_dungeon_region(player, 'Mire Lobby', 'Misery Mire', None, ['Mire Lobby S', 'Mire Lobby Gap']),
|
||||
create_dungeon_region(player, 'Mire Post-Gap', 'Misery Mire', None, ['Mire Post-Gap Gap', 'Mire Post-Gap Down Stairs']),
|
||||
create_dungeon_region(player, 'Mire 2', 'Misery Mire', None, ['Mire 2 Up Stairs', 'Mire 2 NE']),
|
||||
create_dungeon_region(player, 'Mire Hub', 'Misery Mire', None, ['Mire Hub SE', 'Mire Hub ES', 'Mire Hub E', 'Mire Hub NE', 'Mire Hub WN', 'Mire Hub WS', 'Mire Hub Upper Blue Barrier', 'Mire Hub Lower Blue Barrier']),
|
||||
@@ -595,7 +625,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Mire Boss', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize'], ['Mire Boss SW']),
|
||||
|
||||
# tr
|
||||
create_dungeon_region(player, 'TR Main Lobby', 'Turtle Rock', None, ['TR Main Lobby Gap', 'Turtle Rock Exit (Front)']),
|
||||
create_dungeon_region(player, 'TR Main Lobby', 'Turtle Rock', None, ['TR Main Lobby Gap', 'TR Main Lobby SE']),
|
||||
create_dungeon_region(player, 'TR Lobby Ledge', 'Turtle Rock', None, ['TR Lobby Ledge NE', 'TR Lobby Ledge Gap']),
|
||||
create_dungeon_region(player, 'TR Compass Room', 'Turtle Rock', ['Turtle Rock - Compass Chest'], ['TR Compass Room NW']),
|
||||
create_dungeon_region(player, 'TR Hub', 'Turtle Rock', None, ['TR Hub SW', 'TR Hub SE', 'TR Hub ES', 'TR Hub EN', 'TR Hub NW', 'TR Hub NE']),
|
||||
@@ -617,8 +647,8 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'TR Dodgers', 'Turtle Rock', None, ['TR Dodgers WN', 'TR Dodgers SE', 'TR Dodgers NE']),
|
||||
create_dungeon_region(player, 'TR Big View', 'Turtle Rock', None, ['TR Big View WS']),
|
||||
create_dungeon_region(player, 'TR Big Chest', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['TR Big Chest Gap', 'TR Big Chest NE']),
|
||||
create_dungeon_region(player, 'TR Big Chest Entrance', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (East)', 'TR Big Chest Entrance Gap']),
|
||||
create_dungeon_region(player, 'TR Lazy Eyes', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (West)', 'TR Lazy Eyes ES']),
|
||||
create_dungeon_region(player, 'TR Big Chest Entrance', 'Turtle Rock', None, ['TR Big Chest Entrance SE', 'TR Big Chest Entrance Gap']),
|
||||
create_dungeon_region(player, 'TR Lazy Eyes', 'Turtle Rock', None, ['TR Lazy Eyes SE', 'TR Lazy Eyes ES']),
|
||||
create_dungeon_region(player, 'TR Dash Room', 'Turtle Rock', None, ['TR Dash Room SW', 'TR Dash Room ES', 'TR Dash Room NW']),
|
||||
create_dungeon_region(player, 'TR Tongue Pull', 'Turtle Rock', None, ['TR Tongue Pull WS', 'TR Tongue Pull NE']),
|
||||
create_dungeon_region(player, 'TR Rupees', 'Turtle Rock', None, ['TR Rupees SE']),
|
||||
@@ -627,14 +657,14 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'TR Dash Bridge', 'Turtle Rock', None, ['TR Dash Bridge NW', 'TR Dash Bridge SW', 'TR Dash Bridge WS']),
|
||||
create_dungeon_region(player, 'TR Eye Bridge', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
|
||||
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
|
||||
['Turtle Rock Isolated Ledge Exit', 'TR Eye Bridge NW']),
|
||||
['TR Eye Bridge SW', 'TR Eye Bridge NW']),
|
||||
create_dungeon_region(player, 'TR Crystal Maze', 'Turtle Rock', None, ['TR Crystal Maze ES', 'TR Crystal Maze Forwards Path']),
|
||||
create_dungeon_region(player, 'TR Crystal Maze End', 'Turtle Rock', None, ['TR Crystal Maze Blue Path', 'TR Crystal Maze Cane Path', 'TR Crystal Maze North Stairs']),
|
||||
create_dungeon_region(player, 'TR Final Abyss', 'Turtle Rock', None, ['TR Final Abyss South Stairs', 'TR Final Abyss NW']),
|
||||
create_dungeon_region(player, 'TR Boss', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize'], ['TR Boss SW']),
|
||||
|
||||
# gt
|
||||
create_dungeon_region(player, 'GT Lobby', 'Ganon\'s Tower', None, ['GT Lobby Left Down Stairs', 'GT Lobby Up Stairs', 'GT Lobby Right Down Stairs', inv_flag and 'Inverted Ganons Tower Exit' or 'Ganons Tower Exit']),
|
||||
create_dungeon_region(player, 'GT Lobby', 'Ganon\'s Tower', None, ['GT Lobby Left Down Stairs', 'GT Lobby Up Stairs', 'GT Lobby Right Down Stairs', 'GT Lobby S']),
|
||||
create_dungeon_region(player, 'GT Bob\'s Torch', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch'], ['GT Torch Up Stairs', 'GT Torch WN', 'GT Torch EN', 'GT Torch SW']),
|
||||
create_dungeon_region(player, 'GT Hope Room', 'Ganon\'s Tower', ['Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'], ['GT Hope Room Up Stairs', 'GT Hope Room WN', 'GT Hope Room EN']),
|
||||
create_dungeon_region(player, 'GT Big Chest', 'Ganon\'s Tower', ['Ganons Tower - Big Chest'], ['GT Big Chest NW', 'GT Big Chest SW']),
|
||||
@@ -653,7 +683,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'GT Hookshot East Platform', 'Ganon\'s Tower', None, ['GT Hookshot EN', 'GT Hookshot East-North Path', 'GT Hookshot East-South Path']),
|
||||
create_dungeon_region(player, 'GT Hookshot North Platform', 'Ganon\'s Tower', None, ['GT Hookshot NW', 'GT Hookshot North-East Path', 'GT Hookshot North-South Path']),
|
||||
create_dungeon_region(player, 'GT Hookshot South Platform', 'Ganon\'s Tower', None, ['GT Hookshot ES', 'GT Hookshot South-East Path', 'GT Hookshot South-North Path', 'GT Hookshot Platform Blue Barrier']),
|
||||
create_dungeon_region(player, 'GT Hookshot South Entry', 'Ganon\'s Tower', None, ['GT Hookshot SW', 'GT Hookshot Entry Blue Barrier']),
|
||||
create_dungeon_region(player, 'GT Hookshot South Entry', 'Ganon\'s Tower', None, ['GT Hookshot SW', 'GT Hookshot Entry Blue Barrier', 'GT Hookshot Entry Boomerang Path']),
|
||||
create_dungeon_region(player, 'GT Map Room', 'Ganon\'s Tower', ['Ganons Tower - Map Chest'], ['GT Map Room WS']),
|
||||
create_dungeon_region(player, 'GT Double Switch Entry', 'Ganon\'s Tower', None, ['GT Double Switch NW', 'GT Double Switch Orange Barrier', 'GT Double Switch Orange Barrier 2']),
|
||||
create_dungeon_region(player, 'GT Double Switch Switches', 'Ganon\'s Tower', None, ['GT Double Switch Blue Path', 'GT Double Switch Orange Path']),
|
||||
@@ -709,7 +739,7 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'GT Staredown', 'Ganon\'s Tower', None, ['GT Staredown WS', 'GT Staredown Up Ladder']),
|
||||
create_dungeon_region(player, 'GT Falling Torches', 'Ganon\'s Tower', None, ['GT Falling Torches Down Ladder', 'GT Falling Torches NE', 'GT Falling Torches Hole']),
|
||||
create_dungeon_region(player, 'GT Mini Helmasaur Room', 'Ganon\'s Tower', ['Ganons Tower - Mini Helmasaur Room - Left',
|
||||
'Ganons Tower - Mini Helmasaur Room - Right', 'Ganons Tower - Mini Helmasuar Key Drop'], ['GT Mini Helmasaur Room SE', 'GT Mini Helmasaur Room WN']),
|
||||
'Ganons Tower - Mini Helmasaur Room - Right', 'Ganons Tower - Mini Helmasaur Key Drop'], ['GT Mini Helmasaur Room SE', 'GT Mini Helmasaur Room WN']),
|
||||
create_dungeon_region(player, 'GT Bomb Conveyor', 'Ganon\'s Tower', None, ['GT Bomb Conveyor EN', 'GT Bomb Conveyor SW']),
|
||||
create_dungeon_region(player, 'GT Crystal Circles', 'Ganon\'s Tower', ['Ganons Tower - Pre-Moldorm Chest'], ['GT Crystal Circles NW', 'GT Crystal Circles SW']),
|
||||
create_dungeon_region(player, 'GT Left Moldorm Ledge', 'Ganon\'s Tower', None, ['GT Left Moldorm Ledge Drop Down', 'GT Left Moldorm Ledge NW']),
|
||||
@@ -758,15 +788,22 @@ def create_dungeon_regions(world, player):
|
||||
world.get_region('GT Crystal Circles', player).crystal_switch = True
|
||||
|
||||
|
||||
def create_menu_region(player, name, locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.Menu, 'Menu', locations, exits)
|
||||
|
||||
|
||||
def create_lw_region(player, name, locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.LightWorld, 'Light World', locations, exits)
|
||||
|
||||
|
||||
def create_dw_region(player, name, locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.DarkWorld, 'Dark World', locations, exits)
|
||||
|
||||
|
||||
def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.Cave, hint, locations, exits)
|
||||
|
||||
|
||||
def create_dungeon_region(player, name, hint='Hyrule', locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.Dungeon, hint, locations, exits)
|
||||
|
||||
@@ -780,9 +817,9 @@ def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
for location in locations:
|
||||
if location in key_only_locations:
|
||||
ko_hint = 'in a pot' if 'Pot' in location else 'with an enemy'
|
||||
ret.locations.append(Location(player, location, None, False, ko_hint, ret, key_only_locations[location]))
|
||||
if location in key_drop_data:
|
||||
ko_hint = key_drop_data[location][2]
|
||||
ret.locations.append(Location(player, location, None, False, ko_hint, ret, key_drop_data[location][3]))
|
||||
else:
|
||||
address, player_address, crystal, hint_text = location_table[location]
|
||||
ret.locations.append(Location(player, location, address, crystal, hint_text, ret, None, player_address))
|
||||
@@ -831,6 +868,29 @@ def create_shops(world, player):
|
||||
for index, item in enumerate(inventory):
|
||||
shop.add_inventory(index, *item)
|
||||
|
||||
|
||||
def adjust_locations(world, player):
|
||||
if world.keydropshuffle[player]:
|
||||
for location in key_drop_data.keys():
|
||||
loc = world.get_location(location, player)
|
||||
key_item = loc.item
|
||||
key_item.location = None
|
||||
|
||||
loc.forced_item = None
|
||||
loc.item = None
|
||||
loc.event = False
|
||||
loc.address = key_drop_data[location][1]
|
||||
loc.player_address = key_drop_data[location][0]
|
||||
|
||||
item_dungeon = key_item.name.split('(')[1][:-1]
|
||||
item_dungeon = 'Hyrule Castle' if item_dungeon == 'Escape' else item_dungeon
|
||||
dungeon = world.get_dungeon(item_dungeon, player)
|
||||
if key_item.smallkey and not world.retro[player]:
|
||||
dungeon.small_keys.append(key_item)
|
||||
elif key_item.bigkey:
|
||||
dungeon.big_key = key_item
|
||||
|
||||
|
||||
# (type, room_id, shopkeeper, custom, locked, [items])
|
||||
# item = (item, price, max=0, replacement=None, replacement_price=0)
|
||||
_basic_shop_defaults = [('Red Potion', 150), ('Small Heart', 10), ('Bombs (10)', 50)]
|
||||
@@ -849,40 +909,40 @@ shop_table = {
|
||||
'Capacity Upgrade': (0x0115, ShopType.UpgradeShop, 0x04, True, True, [('Bomb Upgrade (+5)', 100, 7), ('Arrow Upgrade (+5)', 100, 7)])
|
||||
}
|
||||
|
||||
key_only_locations = {
|
||||
'Hyrule Castle - Map Guard Key Drop': 'Small Key (Escape)',
|
||||
'Hyrule Castle - Boomerang Guard Key Drop': 'Small Key (Escape)',
|
||||
'Hyrule Castle - Key Rat Key Drop': 'Small Key (Escape)',
|
||||
'Hyrule Castle - Big Key Drop': 'Big Key (Escape)',
|
||||
'Eastern Palace - Dark Square Pot Key': 'Small Key (Eastern Palace)',
|
||||
'Eastern Palace - Dark Eyegore Key Drop': 'Small Key (Eastern Palace)',
|
||||
'Desert Palace - Desert Tiles 1 Pot Key': 'Small Key (Desert Palace)',
|
||||
'Desert Palace - Beamos Hall Pot Key': 'Small Key (Desert Palace)',
|
||||
'Desert Palace - Desert Tiles 2 Pot Key': 'Small Key (Desert Palace)',
|
||||
'Castle Tower - Dark Archer Key Drop': 'Small Key (Agahnims Tower)',
|
||||
'Castle Tower - Circle of Pots Key Drop': 'Small Key (Agahnims Tower)',
|
||||
'Swamp Palace - Pot Row Pot Key': 'Small Key (Swamp Palace)',
|
||||
'Swamp Palace - Trench 1 Pot Key': 'Small Key (Swamp Palace)',
|
||||
'Swamp Palace - Hookshot Pot Key': 'Small Key (Swamp Palace)',
|
||||
'Swamp Palace - Trench 2 Pot Key': 'Small Key (Swamp Palace)',
|
||||
'Swamp Palace - Waterway Pot Key': 'Small Key (Swamp Palace)',
|
||||
'Skull Woods - West Lobby Pot Key': 'Small Key (Skull Woods)',
|
||||
'Skull Woods - Spike Corner Key Drop': 'Small Key (Skull Woods)',
|
||||
'Thieves\' Town - Hallway Pot Key': 'Small Key (Thieves Town)',
|
||||
'Thieves\' Town - Spike Switch Pot Key': 'Small Key (Thieves Town)',
|
||||
'Ice Palace - Jelly Key Drop': 'Small Key (Ice Palace)',
|
||||
'Ice Palace - Conveyor Key Drop': 'Small Key (Ice Palace)',
|
||||
'Ice Palace - Hammer Block Key Drop': 'Small Key (Ice Palace)',
|
||||
'Ice Palace - Many Pots Pot Key': 'Small Key (Ice Palace)',
|
||||
'Misery Mire - Spikes Pot Key': 'Small Key (Misery Mire)',
|
||||
'Misery Mire - Fishbone Pot Key': 'Small Key (Misery Mire)',
|
||||
'Misery Mire - Conveyor Crystal Key Drop': 'Small Key (Misery Mire)',
|
||||
'Turtle Rock - Pokey 1 Key Drop': 'Small Key (Turtle Rock)',
|
||||
'Turtle Rock - Pokey 2 Key Drop': 'Small Key (Turtle Rock)',
|
||||
'Ganons Tower - Conveyor Cross Pot Key': 'Small Key (Ganons Tower)',
|
||||
'Ganons Tower - Double Switch Pot Key': 'Small Key (Ganons Tower)',
|
||||
'Ganons Tower - Conveyor Star Pits Pot Key': 'Small Key (Ganons Tower)',
|
||||
'Ganons Tower - Mini Helmasuar Key Drop': 'Small Key (Ganons Tower)'
|
||||
key_drop_data = {
|
||||
'Hyrule Castle - Map Guard Key Drop': [0x140036, 0x140037, 'in Hyrule Castle', 'Small Key (Escape)'],
|
||||
'Hyrule Castle - Boomerang Guard Key Drop': [0x140033, 0x140034, 'in Hyrule Castle', 'Small Key (Escape)'],
|
||||
'Hyrule Castle - Key Rat Key Drop': [0x14000c, 0x14000d, 'in Hyrule Castle', 'Small Key (Escape)'],
|
||||
'Hyrule Castle - Big Key Drop': [0x14003c, 0x14003d, 'in Hyrule Castle', 'Big Key (Escape)'],
|
||||
'Eastern Palace - Dark Square Pot Key': [0x14005a, 0x14005b, 'in Eastern Palace', 'Small Key (Eastern Palace)'],
|
||||
'Eastern Palace - Dark Eyegore Key Drop': [0x140048, 0x140049, 'in Eastern Palace', 'Small Key (Eastern Palace)'],
|
||||
'Desert Palace - Desert Tiles 1 Pot Key': [0x140030, 0x140031, 'in Desert Palace', 'Small Key (Desert Palace)'],
|
||||
'Desert Palace - Beamos Hall Pot Key': [0x14002a, 0x14002b, 'in Desert Palace', 'Small Key (Desert Palace)'],
|
||||
'Desert Palace - Desert Tiles 2 Pot Key': [0x140027, 0x140028, 'in Desert Palace', 'Small Key (Desert Palace)'],
|
||||
'Castle Tower - Dark Archer Key Drop': [0x140060, 0x140061, 'in Castle Tower', 'Small Key (Agahnims Tower)'],
|
||||
'Castle Tower - Circle of Pots Key Drop': [0x140051, 0x140052, 'in Castle Tower', 'Small Key (Agahnims Tower)'],
|
||||
'Swamp Palace - Pot Row Pot Key': [0x140018, 0x140019, 'in Swamp Palace', 'Small Key (Swamp Palace)'],
|
||||
'Swamp Palace - Trench 1 Pot Key': [0x140015, 0x140016, 'in Swamp Palace', 'Small Key (Swamp Palace)'],
|
||||
'Swamp Palace - Hookshot Pot Key': [0x140012, 0x140013, 'in Swamp Palace', 'Small Key (Swamp Palace)'],
|
||||
'Swamp Palace - Trench 2 Pot Key': [0x14000f, 0x140010, 'in Swamp Palace', 'Small Key (Swamp Palace)'],
|
||||
'Swamp Palace - Waterway Pot Key': [0x140009, 0x14000a, 'in Swamp Palace', 'Small Key (Swamp Palace)'],
|
||||
'Skull Woods - West Lobby Pot Key': [0x14002d, 0x14002e, 'in Skull Woods', 'Small Key (Skull Woods)'],
|
||||
'Skull Woods - Spike Corner Key Drop': [0x14001b, 0x14001c, 'near Mothula', 'Small Key (Skull Woods)'],
|
||||
"Thieves' Town - Hallway Pot Key": [0x14005d, 0x14005e, "in Thieves' Town", 'Small Key (Thieves Town)'],
|
||||
"Thieves' Town - Spike Switch Pot Key": [0x14004e, 0x14004f, "in Thieves' Town", 'Small Key (Thieves Town)'],
|
||||
'Ice Palace - Jelly Key Drop': [0x140003, 0x140004, 'in Ice Palace', 'Small Key (Ice Palace)'],
|
||||
'Ice Palace - Conveyor Key Drop': [0x140021, 0x140022, 'in Ice Palace', 'Small Key (Ice Palace)'],
|
||||
'Ice Palace - Hammer Block Key Drop': [0x140024, 0x140025, 'in Ice Palace', 'Small Key (Ice Palace)'],
|
||||
'Ice Palace - Many Pots Pot Key': [0x140045, 0x140046, 'in Ice Palace', 'Small Key (Ice Palace)'],
|
||||
'Misery Mire - Spikes Pot Key': [0x140054, 0x140055 , 'in Misery Mire', 'Small Key (Misery Mire)'],
|
||||
'Misery Mire - Fishbone Pot Key': [0x14004b, 0x14004c, 'in forgotten Mire', 'Small Key (Misery Mire)'],
|
||||
'Misery Mire - Conveyor Crystal Key Drop': [0x140063, 0x140064 , 'in Misery Mire', 'Small Key (Misery Mire)'],
|
||||
'Turtle Rock - Pokey 1 Key Drop': [0x140057, 0x140058, 'in Turtle Rock', 'Small Key (Turtle Rock)'],
|
||||
'Turtle Rock - Pokey 2 Key Drop': [0x140006, 0x140007, 'in Turtle Rock', 'Small Key (Turtle Rock)'],
|
||||
'Ganons Tower - Conveyor Cross Pot Key': [0x14003f, 0x140040, "in Ganon's Tower", 'Small Key (Ganons Tower)'],
|
||||
'Ganons Tower - Double Switch Pot Key': [0x140042, 0x140043, "in Ganon's Tower", 'Small Key (Ganons Tower)'],
|
||||
'Ganons Tower - Conveyor Star Pits Pot Key': [0x140039, 0x14003a, "in Ganon's Tower", 'Small Key (Ganons Tower)'],
|
||||
'Ganons Tower - Mini Helmasaur Key Drop': [0x14001e, 0x14001f, "atop Ganon's Tower", 'Small Key (Ganons Tower)']
|
||||
}
|
||||
|
||||
dungeon_events = [
|
||||
@@ -1143,3 +1203,8 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
|
||||
'Ice Palace - Prize': ([0x120A4, 0x53F5A, 0x53F5B, 0x180059, 0x180073, 0xC705], None, True, 'Ice Palace'),
|
||||
'Misery Mire - Prize': ([0x120A2, 0x53F48, 0x53F49, 0x180057, 0x180075, 0xC703], None, True, 'Misery Mire'),
|
||||
'Turtle Rock - Prize': ([0x120A7, 0x53F24, 0x53F25, 0x18005C, 0x180079, 0xC708], None, True, 'Turtle Rock')}
|
||||
|
||||
lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int}
|
||||
lookup_id_to_name = {**lookup_id_to_name, **{data[1]: name for name, data in key_drop_data.items()}}
|
||||
lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int}
|
||||
lookup_name_to_id = {**lookup_name_to_id, **{name: data[1] for name, data in key_drop_data.items()}}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import bisect
|
||||
import io
|
||||
import itertools
|
||||
import json
|
||||
import hashlib
|
||||
import logging
|
||||
@@ -8,11 +9,15 @@ import random
|
||||
import struct
|
||||
import sys
|
||||
import subprocess
|
||||
import bps.apply
|
||||
import bps.io
|
||||
|
||||
from BaseClasses import CollectionState, ShopType, Region, Location, DoorType
|
||||
from BaseClasses import CollectionState, ShopType, Region, Location, Door, DoorType, RegionType, PotItem
|
||||
from DoorShuffle import compass_data, DROptions, boss_indicator
|
||||
from Dungeons import dungeon_music_addresses
|
||||
from KeyDoorShuffle import count_locations_exclude_logic
|
||||
from Regions import location_table
|
||||
from RoomData import DoorKind
|
||||
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
|
||||
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
|
||||
from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
|
||||
@@ -22,7 +27,7 @@ from EntranceShuffle import door_addresses, exit_ids
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = 'b9e578ef0af231041070bd9049a55646'
|
||||
RANDOMIZERBASEHASH = '30147375153cc57197805eddf38c2a23'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
@@ -112,16 +117,16 @@ class LocalRom(object):
|
||||
if JAP10HASH != basemd5.hexdigest():
|
||||
logging.getLogger('').warning('Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway.')
|
||||
|
||||
orig_buffer = self.buffer.copy()
|
||||
|
||||
# extend to 2MB
|
||||
self.buffer.extend(bytearray([0x00] * (0x200000 - len(self.buffer))))
|
||||
|
||||
# load randomizer patches
|
||||
with open(local_path('data/base2current.json'), 'r') as stream:
|
||||
patches = json.load(stream)
|
||||
for patch in patches:
|
||||
if isinstance(patch, dict):
|
||||
for baseaddress, values in patch.items():
|
||||
self.write_bytes(int(baseaddress), values)
|
||||
with open(local_path('data/base2current.bps'), 'rb') as stream:
|
||||
bps.apply.apply_to_bytearrays(bps.io.read_bps(stream), orig_buffer, self.buffer)
|
||||
|
||||
self.create_json_patch(orig_buffer)
|
||||
|
||||
# verify md5
|
||||
patchedmd5 = hashlib.md5()
|
||||
@@ -129,6 +134,29 @@ class LocalRom(object):
|
||||
if RANDOMIZERBASEHASH != patchedmd5.hexdigest():
|
||||
raise RuntimeError('Provided Base Rom unsuitable for patching. Please provide a JAP(1.0) "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc" rom to use as a base.')
|
||||
|
||||
def create_json_patch(self, orig_buffer):
|
||||
# extend to 2MB
|
||||
orig_buffer.extend(bytearray([0x00] * (len(self.buffer) - len(orig_buffer))))
|
||||
|
||||
i = 0
|
||||
patches = []
|
||||
|
||||
while i < len(self.buffer):
|
||||
if self.buffer[i] == orig_buffer[i]:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
patch_start = i
|
||||
patch_contents = []
|
||||
while self.buffer[i] != orig_buffer[i]:
|
||||
patch_contents.append(self.buffer[i])
|
||||
i += 1
|
||||
patches.append({patch_start: patch_contents})
|
||||
|
||||
with open(local_path('data/base2current.json'), 'w') as fp:
|
||||
json.dump(patches, fp, separators=(',', ':'))
|
||||
|
||||
|
||||
def write_crc(self):
|
||||
crc = (sum(self.buffer[:0x7FDC] + self.buffer[0x7FE0:]) + 0x01FE) & 0xFFFF
|
||||
inv = crc ^ 0xFFFF
|
||||
@@ -160,7 +188,7 @@ def read_rom(stream):
|
||||
buffer = buffer[0x200:]
|
||||
return buffer
|
||||
|
||||
def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, random_sprite_on_hit):
|
||||
def patch_enemizer(world, player, rom, baserom_path, enemizercli, random_sprite_on_hit):
|
||||
baserom_path = os.path.abspath(baserom_path)
|
||||
basepatch_path = os.path.abspath(local_path(os.path.join("data","base2current.json")))
|
||||
enemizer_basepatch_path = os.path.join(os.path.dirname(enemizercli), "enemizerBasePatch.json")
|
||||
@@ -207,7 +235,7 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, r
|
||||
'GrayscaleMode': False,
|
||||
'GenerateSpoilers': False,
|
||||
'RandomizeLinkSpritePalette': False,
|
||||
'RandomizePots': shufflepots,
|
||||
'RandomizePots': False,
|
||||
'ShuffleMusic': False,
|
||||
'BootlegMagic': True,
|
||||
'CustomBosses': False,
|
||||
@@ -256,14 +284,23 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, r
|
||||
with open(options_path, 'w') as f:
|
||||
json.dump(options, f)
|
||||
|
||||
subprocess.check_call([os.path.abspath(enemizercli),
|
||||
try:
|
||||
subprocess.run([os.path.abspath(enemizercli),
|
||||
'--rom', baserom_path,
|
||||
'--seed', str(world.rom_seeds[player]),
|
||||
'--base', basepatch_path,
|
||||
'--randomizer', randopatch_path,
|
||||
'--enemizer', options_path,
|
||||
'--output', enemizer_output_path],
|
||||
cwd=os.path.dirname(enemizercli), stdout=subprocess.DEVNULL)
|
||||
cwd=os.path.dirname(enemizercli),
|
||||
check=True,
|
||||
capture_output=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
from Main import EnemizerError
|
||||
enemizerMsg = world.fish.translate("cli","cli","Enemizer returned exit code: ") + str(e.returncode) + "\n"
|
||||
enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied")
|
||||
logging.error(f'Enemizer error output: {e.stderr.decode("utf-8")}\n')
|
||||
raise EnemizerError(enemizerMsg)
|
||||
|
||||
with open(enemizer_basepatch_path, 'r') as f:
|
||||
for patch in json.load(f):
|
||||
@@ -503,7 +540,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
|
||||
if not location.crystal:
|
||||
if location.item is not None:
|
||||
# Keys in their native dungeon should use the orignal item code for keys
|
||||
# Keys in their native dungeon should use the original item code for keys
|
||||
if location.parent_region.dungeon:
|
||||
if location.parent_region.dungeon.is_dungeon_item(location.item):
|
||||
if location.item.bigkey:
|
||||
@@ -541,6 +578,9 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
if world.mapshuffle[player]:
|
||||
rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle
|
||||
|
||||
if world.pot_contents[player]:
|
||||
write_pots_to_rom(rom, world.pot_contents[player])
|
||||
|
||||
# patch entrance/exits/holes
|
||||
for region in world.regions:
|
||||
for exit in region.exits:
|
||||
@@ -596,43 +636,62 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
|
||||
# setup dr option flags based on experimental, etc.
|
||||
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
|
||||
if world.experimental[player]:
|
||||
if world.doorShuffle[player] == 'crossed':
|
||||
dr_flags |= DROptions.Map_Info
|
||||
if world.experimental[player] and world.goal[player] != 'triforcehunt':
|
||||
dr_flags |= DROptions.Debug
|
||||
if world.doorShuffle[player] == 'crossed' and world.logic[player] != 'nologic'\
|
||||
and world.mixed_travel[player] == 'prevent':
|
||||
dr_flags |= DROptions.Rails
|
||||
if world.standardize_palettes[player] == 'original':
|
||||
dr_flags |= DROptions.OriginalPalettes
|
||||
|
||||
|
||||
# fix hc big key problems (map and compass too)
|
||||
if world.doorShuffle[player] == 'crossed' or world.keydropshuffle[player]:
|
||||
rom.write_byte(0x151f1, 2)
|
||||
rom.write_byte(0x15270, 2)
|
||||
sanctuary = world.get_region('Sanctuary', player)
|
||||
rom.write_byte(0x1597b, sanctuary.dungeon.dungeon_id*2)
|
||||
update_compasses(rom, world, player)
|
||||
|
||||
# patch doors
|
||||
if world.doorShuffle[player] == 'crossed':
|
||||
rom.write_byte(0x138002, 2)
|
||||
for name, layout in world.key_layout[player].items():
|
||||
offset = compass_data[name][4]//2
|
||||
rom.write_byte(0x13f01c+offset, layout.max_chests + layout.max_drops)
|
||||
rom.write_byte(0x13f02a+offset, layout.max_chests)
|
||||
if world.retro[player]:
|
||||
rom.write_byte(0x13f030+offset, layout.max_chests + layout.max_drops)
|
||||
else:
|
||||
rom.write_byte(0x13f020+offset, layout.max_chests + layout.max_drops) # not currently used
|
||||
rom.write_byte(0x13f030+offset, layout.max_chests)
|
||||
builder = world.dungeon_layouts[player][name]
|
||||
rom.write_byte(0x13f070+offset, builder.location_cnt % 10)
|
||||
rom.write_byte(0x13f07e+offset, builder.location_cnt // 10)
|
||||
rom.write_byte(0x13f080+offset, builder.location_cnt % 10)
|
||||
rom.write_byte(0x13f090+offset, builder.location_cnt // 10)
|
||||
rom.write_byte(0x13f0a0+offset, builder.location_cnt)
|
||||
bk_status = 1 if builder.bk_required else 0
|
||||
bk_status = 2 if builder.bk_provided else bk_status
|
||||
rom.write_byte(0x13f038+offset*2, bk_status)
|
||||
rom.write_byte(0x151f1, 2)
|
||||
rom.write_byte(0x15270, 2)
|
||||
rom.write_byte(0x1597b, 2)
|
||||
if compass_code_good(rom):
|
||||
update_compasses(rom, world, player)
|
||||
else:
|
||||
logging.getLogger('').warning('Randomizer rom update! Compasses in crossed are borken')
|
||||
rom.write_byte(0x13f040+offset*2, bk_status)
|
||||
if player in world.sanc_portal.keys():
|
||||
rom.write_byte(0x159a6, world.sanc_portal[player].ent_offset)
|
||||
sanc_region = world.sanc_portal[player].door.entrance.parent_region
|
||||
if sanc_region.is_dark_world and not sanc_region.is_light_world:
|
||||
rom.write_byte(0x13ff00, 1)
|
||||
for room in world.rooms:
|
||||
if room.player == player and room.palette is not None:
|
||||
rom.write_byte(0x13f200+room.index, room.palette)
|
||||
if world.doorShuffle[player] == 'basic':
|
||||
rom.write_byte(0x138002, 1)
|
||||
for door in world.doors:
|
||||
if door.dest is not None and door.player == player and door.type in [DoorType.Normal, DoorType.SpiralStairs,
|
||||
if door.dest is not None and isinstance(door.dest, Door) and\
|
||||
door.player == player and door.type in [DoorType.Normal, DoorType.SpiralStairs,
|
||||
DoorType.Open, DoorType.StraightStairs]:
|
||||
rom.write_bytes(door.getAddress(), door.dest.getTarget(door))
|
||||
for room in world.rooms:
|
||||
if room.player == player and room.modified:
|
||||
rom.write_bytes(room.address(), room.rom_data())
|
||||
for paired_door in world.paired_doors[player]:
|
||||
rom.write_bytes(paired_door.address_a(world, player), paired_door.rom_data_a(world, player))
|
||||
rom.write_bytes(paired_door.address_b(world, player), paired_door.rom_data_b(world, player))
|
||||
if world.doorShuffle[player] != 'vanilla':
|
||||
|
||||
for builder in world.dungeon_layouts[player].values():
|
||||
if builder.pre_open_stonewall:
|
||||
if builder.pre_open_stonewall.name == 'Desert Wall Slide NW':
|
||||
@@ -640,7 +699,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
for name, pair in boss_indicator.items():
|
||||
dungeon_id, boss_door = pair
|
||||
opposite_door = world.get_door(boss_door, player).dest
|
||||
if opposite_door.roomIndex > -1:
|
||||
if opposite_door and opposite_door.roomIndex > -1:
|
||||
dungeon_name = opposite_door.entrance.parent_region.dungeon.name
|
||||
dungeon_id = boss_indicator[dungeon_name][0]
|
||||
rom.write_byte(0x13f000+dungeon_id, opposite_door.roomIndex)
|
||||
@@ -650,12 +709,72 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
if dr_flags & DROptions.Town_Portal and world.mode[player] == 'inverted':
|
||||
rom.write_byte(0x138006, 1)
|
||||
|
||||
# fix skull woods exit, if not fixed during exit patching
|
||||
for portal in world.dungeon_portals[player]:
|
||||
if not portal.default:
|
||||
offset = portal.ent_offset
|
||||
rom.write_byte(0x14577 + offset*2, portal.current_room())
|
||||
rom.write_bytes(0x14681 + offset*8, portal.relative_coords())
|
||||
rom.write_bytes(0x14aa9 + offset*2, portal.scroll_x())
|
||||
rom.write_bytes(0x14bb3 + offset*2, portal.scroll_y())
|
||||
rom.write_bytes(0x14cbd + offset*2, portal.link_y())
|
||||
rom.write_bytes(0x14dc7 + offset*2, portal.link_x())
|
||||
rom.write_bytes(0x14fdb + offset*2, portal.camera_x())
|
||||
rom.write_byte(0x152f9 + offset, portal.bg_setting())
|
||||
rom.write_byte(0x1537e + offset, portal.hv_scroll())
|
||||
rom.write_byte(0x15403 + offset, portal.scroll_quad())
|
||||
rom.write_byte(0x15aee + portal.exit_offset, portal.current_room())
|
||||
if portal.boss_exit_idx > -1:
|
||||
rom.write_byte(0x7939 + portal.boss_exit_idx, portal.current_room())
|
||||
|
||||
# fix exits, if not fixed during exit patching
|
||||
if world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla':
|
||||
write_int16(rom, 0x15DB5 + 2 * exit_ids['Skull Woods Final Section Exit'][1], 0x00F8)
|
||||
elif world.force_fix[player]['sw']:
|
||||
write_int16(rom, 0x15DB5 + world.force_fix[player]['sw'].exit_offset, 0x00F8)
|
||||
if world.force_fix[player]['pod']:
|
||||
write_int16(rom, 0x15DB5 + world.force_fix[player]['pod'].exit_offset, 0x0640)
|
||||
if world.force_fix[player]['tr']:
|
||||
write_int16(rom, 0x15DB5 + world.force_fix[player]['tr'].exit_offset, 0x0134)
|
||||
if world.force_fix[player]['gt']:
|
||||
write_int16(rom, 0x15DB5 + world.force_fix[player]['gt'].exit_offset, 0x00A4)
|
||||
|
||||
write_custom_shops(rom, world, player)
|
||||
|
||||
def credits_digit(num):
|
||||
# top: $54 is 1, 55 2, etc , so 57=4, 5C=9
|
||||
# bot: $7A is 1, 7B is 2, etc so 7D=4, 82=9 (zero unknown...)
|
||||
return 0x53+num, 0x79+num
|
||||
|
||||
if world.keydropshuffle[player]:
|
||||
rom.write_byte(0x140000, 1)
|
||||
rom.write_byte(0x187010, 249) # dynamic credits
|
||||
# collection rate address: 238C37
|
||||
mid_top, mid_bot = credits_digit(4)
|
||||
last_top, last_bot = credits_digit(9)
|
||||
# top half
|
||||
rom.write_byte(0x118C53, mid_top)
|
||||
rom.write_byte(0x118C54, last_top)
|
||||
# bottom half
|
||||
rom.write_byte(0x118C71, mid_bot)
|
||||
rom.write_byte(0x118C72, last_bot)
|
||||
|
||||
if world.keydropshuffle[player] or world.doorShuffle[player] != 'vanilla':
|
||||
gt = world.dungeon_layouts[player]['Ganons Tower']
|
||||
gt_logic = world.key_logic[player]['Ganons Tower']
|
||||
total = 0
|
||||
for region in gt.master_sector.regions:
|
||||
total += count_locations_exclude_logic(region.locations, gt_logic)
|
||||
rom.write_byte(0x187012, total) # dynamic credits
|
||||
# gt big key address: 238B59
|
||||
mid_top, mid_bot = credits_digit(total // 10)
|
||||
last_top, last_bot = credits_digit(total % 10)
|
||||
# top half
|
||||
rom.write_byte(0x118B75, mid_top)
|
||||
rom.write_byte(0x118B76, last_top)
|
||||
# bottom half
|
||||
rom.write_byte(0x118B93, mid_bot)
|
||||
rom.write_byte(0x118B94, last_bot)
|
||||
|
||||
# patch medallion requirements
|
||||
if world.required_medallions[player][0] == 'Bombos':
|
||||
rom.write_byte(0x180022, 0x00) # requirement
|
||||
@@ -1178,7 +1297,20 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
rom.write_byte(0x18005F, world.crystals_needed_for_ganon[player])
|
||||
|
||||
# block HC upstairs doors in rain state in standard mode
|
||||
rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' else 0x00)
|
||||
prevent_rain = world.mode[player] == "standard" and world.shuffle[player] != 'vanilla'
|
||||
rom.write_byte(0x18008A, 0x01 if prevent_rain else 0x00)
|
||||
# block sanc door in rain state and the dungeon is not vanilla
|
||||
rom.write_byte(0x13f0fa, 0x01 if world.mode[player] == "standard" and world.doorShuffle[player] != 'vanilla' else 0x00)
|
||||
|
||||
if prevent_rain:
|
||||
portals = [world.get_portal('Hyrule Castle East', player), world.get_portal('Hyrule Castle West', player)]
|
||||
for idx, portal in enumerate(portals):
|
||||
x = idx*2
|
||||
room_idx = portal.door.roomIndex
|
||||
room = world.get_room(room_idx, player)
|
||||
write_int16(rom, 0x13f0f0+x, room_idx)
|
||||
rom.write_byte(0x13f0f6+x, room.position(portal.door).value)
|
||||
rom.write_byte(0x13f0f7+x, room.kind(portal.door).value)
|
||||
|
||||
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] else 0x00)
|
||||
| (0x02 if world.compassshuffle[player] else 0x00)
|
||||
@@ -1258,6 +1390,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
rom.write_bytes(0x180188, [0, 0, 0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0, 0, 0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
bow_max, bomb_max, magic_max = 0, 0, 0
|
||||
bow_small, magic_small = 0, 0
|
||||
if world.mode[player] == 'standard':
|
||||
if uncle_location.item is not None and uncle_location.item.name in ['Bow', 'Progressive Bow']:
|
||||
rom.write_byte(0x18004E, 1) # Escape Fill (arrows)
|
||||
@@ -1265,7 +1398,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
rom.write_bytes(0x180185, [0, 0, 70]) # Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180188, [0, 0, 10]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0, 0, 10]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
bow_max = 70
|
||||
bow_max, bow_small = 70, 10
|
||||
elif uncle_location.item is not None and uncle_location.item.name in ['Bombs (10)']:
|
||||
rom.write_byte(0x18004E, 2) # Escape Fill (bombs)
|
||||
rom.write_bytes(0x180185, [0, 50, 0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||
@@ -1277,13 +1410,16 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
rom.write_bytes(0x180185, [0x80, 0, 0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180188, [0x20, 0, 0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0x20, 0, 0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
magic_max = 0x80
|
||||
magic_max, magic_small = 0x80, 0x20
|
||||
if world.doorShuffle[player] == 'crossed':
|
||||
# Uncle respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x180185, [max(0x20, magic_max), max(3, bomb_max), max(10, bow_max)])
|
||||
rom.write_bytes(0x180188, [0x20, 3, 10]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [0x20, 3, 10]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
|
||||
elif world.doorShuffle[player] == 'basic': # just in case a bomb is needed to get to a chest
|
||||
rom.write_bytes(0x180185, [max(0x00, magic_max), max(3, bomb_max), max(0, bow_max)])
|
||||
rom.write_bytes(0x180188, [magic_small, 3, bow_small]) # Zelda respawn refills (magic, bombs, arrows)
|
||||
rom.write_bytes(0x18018B, [magic_small, 3, bow_small]) # Mantle respawn refills (magic, bombs, arrows)
|
||||
|
||||
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
|
||||
rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required[player] else 0x00)
|
||||
@@ -1298,20 +1434,31 @@ def patch_rom(world, rom, player, team, enemized):
|
||||
|
||||
# set correct flag for hera basement item
|
||||
hera_basement = world.get_location('Tower of Hera - Basement Cage', player)
|
||||
if hera_basement.item is not None and hera_basement.item.name == 'Small Key (Tower of Hera)' and hera_basement.item.player == player:
|
||||
is_small_key_this_dungeon = False
|
||||
if hera_basement.item is not None and hera_basement.item.smallkey:
|
||||
item_dungeon = hera_basement.item.name.split('(')[1][:-1]
|
||||
if item_dungeon == 'Escape':
|
||||
item_dungeon = 'Hyrule Castle'
|
||||
is_small_key_this_dungeon = hera_basement.parent_region.dungeon.name == item_dungeon
|
||||
if is_small_key_this_dungeon:
|
||||
rom.write_byte(0x4E3BB, 0xE4)
|
||||
else:
|
||||
rom.write_byte(0x4E3BB, 0xEB)
|
||||
|
||||
# fix trock doors for reverse entrances
|
||||
if world.fix_trock_doors[player]:
|
||||
if world.get_door('TR Lazy Eyes SE', player).entranceFlag:
|
||||
world.get_room(0x23, player).change(0, DoorKind.CaveEntrance)
|
||||
if world.get_door('TR Eye Bridge SW', player).entranceFlag:
|
||||
world.get_room(0xd5, player).change(0, DoorKind.CaveEntrance)
|
||||
# do this unconditionally - gets overwritten by RoomData in doorShufflemodes
|
||||
rom.write_byte(0xFED31, 0x0E) # preopen bombable exit
|
||||
rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit
|
||||
# included unconditionally in base2current
|
||||
#rom.write_byte(0xFE465, 0x1E) # remove small key door on backside of big key door
|
||||
else:
|
||||
rom.write_byte(0xFED31, 0x2A) # preopen bombable exit
|
||||
rom.write_byte(0xFEE41, 0x2A) # preopen bombable exit
|
||||
|
||||
if world.doorShuffle[player] != 'vanilla' or world.keydropshuffle[player]:
|
||||
for room in world.rooms:
|
||||
if room.player == player and room.modified:
|
||||
rom.write_bytes(room.address(), room.rom_data())
|
||||
|
||||
write_strings(rom, world, player, team)
|
||||
|
||||
@@ -1630,6 +1777,9 @@ def write_strings(rom, world, player, team):
|
||||
return "nothing"
|
||||
if ped_hint:
|
||||
hint = dest.pedestal_hint_text if dest.pedestal_hint_text else "unknown item"
|
||||
else:
|
||||
if isinstance(dest, Region) and dest.type == RegionType.Dungeon and dest.dungeon:
|
||||
hint = dest.dungeon.name
|
||||
else:
|
||||
hint = dest.hint_text if dest.hint_text else "something"
|
||||
if dest.player != player:
|
||||
@@ -2139,66 +2289,20 @@ def patch_shuffled_dark_sanc(world, rom, player):
|
||||
rom.write_bytes(0x180262, [unknown_1, unknown_2, 0x00])
|
||||
|
||||
|
||||
# 24B118 and 20BB32
|
||||
compass_r_addr = 0x123118 # a9 90 24 8f 9a c7 7e
|
||||
compass_w_addr = 0x103b32 # e2 20 ad 0c 04 c9 00 d0
|
||||
|
||||
|
||||
def compass_code_good(rom):
|
||||
if isinstance(rom, LocalRom):
|
||||
# a990248f9ac77e
|
||||
if rom.buffer[compass_r_addr] != 0xa9 or rom.buffer[compass_r_addr+1] != 0x90 or rom.buffer[compass_r_addr+2] != 0x24:
|
||||
return False
|
||||
if rom.buffer[compass_r_addr+3] != 0x8f or rom.buffer[compass_r_addr+4] != 0x9a or rom.buffer[compass_r_addr+5] != 0xc7:
|
||||
return False
|
||||
if rom.buffer[compass_w_addr] != 0xe2 or rom.buffer[compass_w_addr+1] != 0x20 or rom.buffer[compass_w_addr+2] != 0xad:
|
||||
return False
|
||||
if rom.buffer[compass_w_addr+3] != 0x0c or rom.buffer[compass_w_addr+4] != 0x04 or rom.buffer[compass_w_addr+5] != 0xc9:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def update_compasses(rom, world, player):
|
||||
layouts = world.dungeon_layouts[player]
|
||||
# cmp XX : bne escape
|
||||
# cpy 32 : bne escape
|
||||
# brl .itemCounts
|
||||
# c9 02 d0 eastern
|
||||
# nop #2
|
||||
new_code = [0x07, 0xC0, 0x32, 0xD0, 0x03, 0x82, 0xB9, 0x01, 0xC9, 0x02, 0xD0, 0x10, 0xEA, 0xEA]
|
||||
rom.write_bytes(compass_w_addr + 8, new_code)
|
||||
# 05 06 07 08
|
||||
# C9 00 D0 02 80 04 C9 02 D0 15 C0 32 D0 03 82 B3 01
|
||||
# C9 XX D0 07 C0 32 D0 03 82 B9 01 C9 02 D0 10 EA EA
|
||||
|
||||
provided_dungeon = False
|
||||
for name, builder in layouts.items():
|
||||
digit_offset, sram_byte, write_offset, jmp_nop_flag, dungeon_id = compass_data[name]
|
||||
digit1 = builder.location_cnt // 10
|
||||
digit2 = builder.location_cnt % 10
|
||||
rom.write_byte(compass_r_addr+digit_offset, 0x90+digit1)
|
||||
rom.write_byte(compass_r_addr+digit_offset+7, 0x90+digit2)
|
||||
|
||||
# read compass count code
|
||||
start_address = compass_r_addr+digit_offset+15
|
||||
|
||||
# -0x59 relative to compass_r_addr, +8000 because rom -120000 to get rid of high byte
|
||||
jmp_address = compass_r_addr-0x118059
|
||||
# lda $7ef4(sb); jmp $jmp_address
|
||||
rom.write_bytes(start_address, [0xaf, sram_byte, 0xf4, 0x7e, 0x4c, jmp_address % 0x100, jmp_address // 0x100])
|
||||
|
||||
# write compass count code
|
||||
write_address = compass_w_addr+write_offset
|
||||
# 0x186 relative to compass_r_addr, +8000 because rom -100000 to get rid of high byte
|
||||
jmp_address = compass_w_addr-0xf7e7a
|
||||
# lda $7ef4(sb); inc; sta $7ef4(sb)
|
||||
rom.write_bytes(write_address, [0xaf, sram_byte, 0xf4, 0x7e, 0x1a, 0x8f, sram_byte, 0xf4, 0x7e])
|
||||
if jmp_nop_flag == 0:
|
||||
rom.write_bytes(write_address+9, [0x4c, jmp_address % 0x100, jmp_address // 0x100]) # jmp $jmp_address
|
||||
else:
|
||||
for i in range(0, jmp_nop_flag):
|
||||
rom.write_byte(write_address+9+i, 0xea) # nop
|
||||
dungeon_id = compass_data[name][4]
|
||||
rom.write_byte(0x187000 + dungeon_id//2, builder.location_cnt)
|
||||
if builder.bk_provided:
|
||||
rom.write_byte(compass_w_addr+6, dungeon_id)
|
||||
if provided_dungeon:
|
||||
logging.getLogger('').warning('Multiple dungeons have forced BKs! Compass code might need updating?')
|
||||
rom.write_byte(0x186FFF, dungeon_id)
|
||||
provided_dungeon = True
|
||||
if not provided_dungeon:
|
||||
rom.write_byte(0x186FFF, 0xff)
|
||||
|
||||
|
||||
|
||||
InconvenientDungeonEntrances = {'Turtle Rock': 'Turtle Rock Main',
|
||||
@@ -2459,3 +2563,29 @@ hash_alphabet = [
|
||||
"Lamp", "Hammer", "Shovel", "Ocarina", "Bug Net", "Book", "Bottle", "Potion", "Cane", "Cape", "Mirror", "Boots",
|
||||
"Gloves", "Flippers", "Pearl", "Shield", "Tunic", "Heart", "Map", "Compass", "Key"
|
||||
]
|
||||
|
||||
pot_item_room_table_lookup = 0xDB67
|
||||
|
||||
###
|
||||
# Pointer to pot location and contents for each non-empty pot in a supertile
|
||||
# Format: [(x, y, item)] FF FF (Note: x,y are bit packed to include layer)
|
||||
pot_item_table = 0xDDE7
|
||||
pot_item_table_end = 0xE6B0
|
||||
|
||||
def write_pots_to_rom(rom, pot_contents):
|
||||
n = pot_item_table
|
||||
rom.write_bytes(n, [0xFF,0xFF])
|
||||
n += 2
|
||||
for i in range(0x140):
|
||||
if i in pot_contents:
|
||||
pots = [pot for pot in pot_contents[i] if pot.item != PotItem.Nothing]
|
||||
if len(pots) > 0:
|
||||
write_int16(rom, pot_item_room_table_lookup + 2*i, n)
|
||||
rom.write_bytes(n, itertools.chain(*((pot.x,pot.y,pot.item) for pot in pots)))
|
||||
n += 3*len(pots) + 2
|
||||
rom.write_bytes(n - 2, [0xFF,0xFF])
|
||||
else:
|
||||
write_int16(rom, pot_item_room_table_lookup + 2*i, n-2)
|
||||
else:
|
||||
write_int16(rom, pot_item_room_table_lookup + 2*i, n-2)
|
||||
assert n <= pot_item_table_end
|
||||
|
||||
+15
@@ -9,7 +9,9 @@ def create_rooms(world, player):
|
||||
# Room(player, 0x03, 0x509cf).door(Position.SouthW, DoorKind.CaveEntrance),
|
||||
Room(player, 0x04, 0xfe25c).door(Position.NorthW, DoorKind.StairKey2).door(Position.InteriorW, DoorKind.Dashable).door(Position.InteriorS, DoorKind.Dashable).door(Position.InteriorE, DoorKind.TrapTriggerable).door(Position.SouthW, DoorKind.Normal),
|
||||
Room(player, 0x06, 0xfa192).door(Position.SouthW, DoorKind.Trap),
|
||||
Room(player, 0x07, None),
|
||||
# Room(player, 0x08, 0x5064f).door(Position.InteriorS2, DoorKind.CaveEntranceLow08).door(Position.SouthE, DoorKind.CaveEntrance).door(Position.SouthW2, DoorKind.NormalLow2).door(Position.SouthW2, DoorKind.ToggleFlag),
|
||||
Room(player, 0x09, None),
|
||||
Room(player, 0x0a, 0xfa734).door(Position.North, DoorKind.StairKey),
|
||||
Room(player, 0x0b, 0xfabf0).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.InteriorS, DoorKind.Trap2).door(Position.InteriorN, DoorKind.SmallKey),
|
||||
Room(player, 0x0c, 0xfef53).door(Position.South, DoorKind.DungeonEntrance),
|
||||
@@ -23,6 +25,7 @@ def create_rooms(world, player):
|
||||
Room(player, 0x14, 0xfe464).door(Position.SouthE, DoorKind.SmallKey).door(Position.WestS, DoorKind.SmallKey).door(Position.NorthW, DoorKind.Normal).door(Position.WestN, DoorKind.Normal).door(Position.SouthW, DoorKind.Normal).door(Position.EastN, DoorKind.Normal).door(Position.EastS, DoorKind.Normal),
|
||||
Room(player, 0x15, 0xfe63e).door(Position.WestS, DoorKind.Trap).door(Position.WestN, DoorKind.Normal),
|
||||
Room(player, 0x16, 0xfa150).door(Position.InteriorV, DoorKind.Bombable).door(Position.InteriorW, DoorKind.SmallKey).door(Position.InteriorE, DoorKind.Normal).door(Position.NorthW, DoorKind.Normal),
|
||||
Room(player, 0x17, None),
|
||||
# Room(player, 0x18, 0x506e5).door(Position.NorthW2, DoorKind.NormalLow).door(Position.NorthW2, DoorKind.ToggleFlag),
|
||||
Room(player, 0x19, 0xfacc6).door(Position.East, DoorKind.Bombable).door(Position.EastN, DoorKind.SmallKey),
|
||||
Room(player, 0x1a, 0xfa670).door(Position.InteriorE, DoorKind.SmallKey).door(Position.WestN, DoorKind.SmallKey).door(Position.West, DoorKind.Bombable).door(Position.SouthW, DoorKind.SmallKey).door(Position.InteriorN, DoorKind.Normal).door(Position.SouthE, DoorKind.Normal),
|
||||
@@ -38,6 +41,7 @@ def create_rooms(world, player):
|
||||
Room(player, 0x23, 0xfed30).door(Position.SouthE, DoorKind.BombableEntrance).door(Position.EastS, DoorKind.Normal),
|
||||
Room(player, 0x24, 0xfe6ee).door(Position.NorthE, DoorKind.BigKey).door(Position.InteriorN, DoorKind.Trap2).door(Position.InteriorW, DoorKind.Trap2).door(Position.InteriorE, DoorKind.Trap2).door(Position.SouthE, DoorKind.DungeonEntrance).door(Position.NorthW, DoorKind.Normal).door(Position.WestS, DoorKind.Normal).door(Position.InteriorS, DoorKind.Normal),
|
||||
Room(player, 0x26, 0xf9cbb).door(Position.South, DoorKind.SmallKey).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.InteriorE, DoorKind.TrapTriggerable).door(Position.InteriorN, DoorKind.Normal),
|
||||
Room(player, 0x27, None),
|
||||
Room(player, 0x28, 0xf92a8).door(Position.NorthW, DoorKind.StairKey2).door(Position.South, DoorKind.DungeonEntrance),
|
||||
Room(player, 0x2a, 0xfa594).door(Position.NorthE, DoorKind.Trap).door(Position.NorthW, DoorKind.SmallKey).door(Position.EastS, DoorKind.Bombable).door(Position.East2, DoorKind.NormalLow).door(Position.SouthW, DoorKind.Normal).door(Position.SouthE, DoorKind.Normal),
|
||||
Room(player, 0x2b, 0xfaaa7).door(Position.InteriorS, DoorKind.Bombable).door(Position.WestS, DoorKind.Bombable).door(Position.NorthW, DoorKind.Trap).door(Position.West2, DoorKind.NormalLow),
|
||||
@@ -66,6 +70,7 @@ def create_rooms(world, player):
|
||||
|
||||
Room(player, 0x40, 0xf8eea).door(Position.InteriorS2, DoorKind.NormalLow2),
|
||||
Room(player, 0x41, 0x50f15).door(Position.South, DoorKind.Trap),
|
||||
Room(player, 0x42, None),
|
||||
Room(player, 0x43, 0xf87f8).door(Position.NorthW, DoorKind.BigKey).door(Position.InteriorE, DoorKind.SmallKey).door(Position.SouthE, DoorKind.SmallKey),
|
||||
Room(player, 0x44, 0xfdbcd).door(Position.InteriorN, DoorKind.Trap2).door(Position.InteriorS, DoorKind.SmallKey).door(Position.EastN, DoorKind.Normal).door(Position.EastS, DoorKind.Normal),
|
||||
Room(player, 0x45, 0xfdcae).door(Position.WestN, DoorKind.Trap).door(Position.InteriorW, DoorKind.Normal).door(Position.WestS, DoorKind.Normal).door(Position.InteriorS, DoorKind.Normal),
|
||||
@@ -82,6 +87,7 @@ def create_rooms(world, player):
|
||||
Room(player, 0x51, 0x51029).door(Position.North, DoorKind.Normal).door(Position.North, DoorKind.DungeonChanger),
|
||||
Room(player, 0x52, 0x51230).door(Position.WestN2, DoorKind.Warp).door(Position.SouthW2, DoorKind.NormalLow2).door(Position.South, DoorKind.Normal),
|
||||
Room(player, 0x53, 0xf88ad).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.InteriorS, DoorKind.Trap2).door(Position.NorthE, DoorKind.SmallKey),
|
||||
Room(player, 0x54, None),
|
||||
# Room(player, 0x55, 0x50166).door(Position.InteriorW2, DoorKind.NormalLow).door(Position.SouthW, DoorKind.Normal).door(Position.SouthW, DoorKind.IncognitoEntrance),
|
||||
Room(player, 0x56, 0xfbb4e).door(Position.InteriorW, DoorKind.SmallKey).door(Position.SouthW, DoorKind.DungeonEntrance).door(Position.InteriorS, DoorKind.Normal).door(Position.EastS, DoorKind.Normal),
|
||||
Room(player, 0x57, 0xfbbd2).door(Position.InteriorN, DoorKind.Bombable).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.EastS, DoorKind.SmallKey).door(Position.SouthW, DoorKind.DungeonEntrance).door(Position.WestS, DoorKind.Normal).door(Position.SouthE, DoorKind.Normal),
|
||||
@@ -109,6 +115,7 @@ def create_rooms(world, player):
|
||||
Room(player, 0x6d, 0xffa4e).door(Position.NorthW, DoorKind.Trap).door(Position.InteriorW, DoorKind.Trap).door(Position.WestS, DoorKind.Trap),
|
||||
Room(player, 0x6e, 0xfc74b).door(Position.NorthE, DoorKind.Trap),
|
||||
|
||||
Room(player, 0x70, None),
|
||||
Room(player, 0x71, 0x52341).door(Position.InteriorW, DoorKind.SmallKey).door(Position.SouthW2, DoorKind.TrapTriggerableLow).door(Position.InteriorS2, DoorKind.TrapTriggerableLow),
|
||||
Room(player, 0x72, 0x51fda).door(Position.InteriorV, DoorKind.SmallKey),
|
||||
Room(player, 0x73, 0xf8972).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.InteriorS, DoorKind.Trap2).door(Position.InteriorE, DoorKind.Normal),
|
||||
@@ -122,11 +129,14 @@ def create_rooms(world, player):
|
||||
Room(player, 0x7e, 0xfc7c6).door(Position.SouthE, DoorKind.SmallKey).door(Position.InteriorS, DoorKind.TrapTriggerable).door(Position.EastN, DoorKind.Normal),
|
||||
Room(player, 0x7f, 0xfc827).door(Position.WestN, DoorKind.Trap).door(Position.InteriorW, DoorKind.Normal),
|
||||
|
||||
Room(player, 0x80, None),
|
||||
Room(player, 0x81, 0x5224b).door(Position.NorthW2, DoorKind.NormalLow2),
|
||||
Room(player, 0x82, None),
|
||||
Room(player, 0x83, 0xf8bba).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.SouthW, DoorKind.DungeonEntrance).door(Position.InteriorS, DoorKind.Normal),
|
||||
Room(player, 0x84, 0xf8cb7).door(Position.South, DoorKind.DungeonEntrance),
|
||||
Room(player, 0x85, 0xf8d7d).door(Position.NorthE, DoorKind.Trap).door(Position.InteriorN, DoorKind.SmallKey).door(Position.SouthE, DoorKind.DungeonEntrance).door(Position.InteriorS, DoorKind.Normal),
|
||||
Room(player, 0x87, 0xfd1b7).door(Position.InteriorN, DoorKind.Trap2).door(Position.InteriorE, DoorKind.Normal),
|
||||
Room(player, 0x89, None),
|
||||
Room(player, 0x8b, 0xff33f).door(Position.InteriorN, DoorKind.TrapTriggerable).door(Position.InteriorS, DoorKind.SmallKey).door(Position.EastN, DoorKind.Normal).door(Position.SouthW, DoorKind.Normal).door(Position.NorthW, DoorKind.Normal),
|
||||
Room(player, 0x8c, 0xff3ef).door(Position.EastN, DoorKind.Trap).door(Position.InteriorW, DoorKind.Trap2).door(Position.InteriorN, DoorKind.SmallKey).door(Position.WestN, DoorKind.Normal).door(Position.SouthW, DoorKind.Normal).door(Position.SouthE, DoorKind.Normal),
|
||||
Room(player, 0x8d, 0xff4e0).door(Position.SouthE, DoorKind.Trap).door(Position.InteriorN, DoorKind.SmallKey).door(Position.WestN, DoorKind.Normal).door(Position.NorthE, DoorKind.Normal).door(Position.InteriorS, DoorKind.Normal),
|
||||
@@ -153,6 +163,7 @@ def create_rooms(world, player):
|
||||
Room(player, 0xa3, 0xfb667).door(Position.West, DoorKind.Normal).door(Position.SouthW, DoorKind.Normal),
|
||||
Room(player, 0xa4, 0xfe741).door(Position.SouthW, DoorKind.Trap),
|
||||
Room(player, 0xa5, 0xffb7f).door(Position.NorthE, DoorKind.Trap).door(Position.InteriorE, DoorKind.Trap2).door(Position.InteriorW, DoorKind.Trap2),
|
||||
Room(player, 0xa6, None),
|
||||
Room(player, 0xa8, 0x51887).door(Position.InteriorS, DoorKind.Trap2).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.SouthE, DoorKind.SmallKey).door(Position.InteriorN2, DoorKind.NormalLow2).door(Position.EastN2, DoorKind.NormalLow2).door(Position.East, DoorKind.Normal),
|
||||
Room(player, 0xa9, 0x519c9).door(Position.West, DoorKind.Trap).door(Position.East, DoorKind.Trap).door(Position.North, DoorKind.BigKey).door(Position.WestN2, DoorKind.NormalLow2).door(Position.EastN2, DoorKind.NormalLow2).door(Position.South, DoorKind.Normal),
|
||||
Room(player, 0xaa, 0x51b29).door(Position.InteriorE, DoorKind.Trap2).door(Position.WestN2, DoorKind.NormalLow2).door(Position.InteriorN, DoorKind.Normal).door(Position.InteriorS, DoorKind.Normal).door(Position.West, DoorKind.Normal).door(Position.SouthW, DoorKind.Normal),
|
||||
@@ -250,6 +261,10 @@ class Room(object):
|
||||
self.doorListAddress = address
|
||||
self.doorList = []
|
||||
self.modified = False
|
||||
self.palette = None
|
||||
|
||||
def position(self, door):
|
||||
return self.doorList[door.doorListPos][0]
|
||||
|
||||
def kind(self, door):
|
||||
return self.doorList[door.doorListPos][1]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import logging
|
||||
from collections import deque
|
||||
|
||||
from BaseClasses import CollectionState, RegionType, DoorType, Entrance
|
||||
from Regions import key_only_locations
|
||||
from BaseClasses import CollectionState, RegionType, DoorType, Entrance, CrystalBarrier
|
||||
from RoomData import DoorKind
|
||||
|
||||
|
||||
@@ -75,6 +74,10 @@ def add_rule(spot, rule, combine='and'):
|
||||
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
||||
|
||||
|
||||
def or_rule(rule1, rule2):
|
||||
return lambda state: rule1(state) or rule2(state)
|
||||
|
||||
|
||||
def add_lamp_requirement(spot, player):
|
||||
add_rule(spot, lambda state: state.has('Lamp', player, state.world.lamps_needed_for_dark_rooms))
|
||||
|
||||
@@ -124,7 +127,6 @@ def global_rules(world, player):
|
||||
set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player))
|
||||
set_rule(world.get_location('Sahasrahla', player), lambda state: state.has('Green Pendant', player))
|
||||
|
||||
|
||||
set_rule(world.get_location('Spike Cave', player), lambda state:
|
||||
state.has('Hammer', player) and state.can_lift_rocks(player) and
|
||||
((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or
|
||||
@@ -184,9 +186,13 @@ def global_rules(world, player):
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize', player))
|
||||
|
||||
set_rule(world.get_entrance('Swamp Lobby Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player))
|
||||
set_rule(world.get_entrance('Swamp Entrance Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player))
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Approach Dry', player), lambda state: not state.has('Trench 1 Filled', player))
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Key Ledge Dry', player), lambda state: not state.has('Trench 1 Filled', player))
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Departure Dry', player), lambda state: not state.has('Trench 1 Filled', player))
|
||||
# these two are here so that, if they flood the area before finding flippers, nothing behind there can lock out the flippers
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Nexus Approach', player), lambda state: state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Nexus Key', player), lambda state: state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Approach Key', player), lambda state: state.has('Flippers', player) and state.has('Trench 1 Filled', player))
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Approach Swim Depart', player), lambda state: state.has('Flippers', player) and state.has('Trench 1 Filled', player))
|
||||
set_rule(world.get_entrance('Swamp Trench 1 Key Approach', player), lambda state: state.has('Flippers', player) and state.has('Trench 1 Filled', player))
|
||||
@@ -203,10 +209,9 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('Swamp Barrier Ledge Hook Path', player), lambda state: state.has('Hookshot', player))
|
||||
set_rule(world.get_entrance('Swamp Drain Right Switch', player), lambda state: state.has('Drained Swamp', player))
|
||||
set_rule(world.get_entrance('Swamp Drain WN', player), lambda state: state.has('Drained Swamp', player))
|
||||
# this might be unnecesssary for an insanity style shuffle
|
||||
set_rule(world.get_entrance('Swamp Flooded Room WS', player), lambda state: state.has('Drained Swamp', player))
|
||||
set_rule(world.get_entrance('Swamp Flooded Room Ladder', player), lambda state: state.has('Drained Swamp', player))
|
||||
set_rule(world.get_location('Swamp Palace - Flooded Room - Left', player), lambda state: state.has('Drained Swamp', player))
|
||||
set_rule(world.get_location('Swamp Palace - Flooded Room - Right', player), lambda state: state.has('Drained Swamp', player))
|
||||
set_rule(world.get_entrance('Swamp Flooded Spot Ladder', player), lambda state: state.has('Flippers', player) or state.has('Drained Swamp', player))
|
||||
set_rule(world.get_entrance('Swamp Drain Left Up Stairs', player), lambda state: state.has('Flippers', player) or state.has('Drained Swamp', player))
|
||||
set_rule(world.get_entrance('Swamp Waterway NW', player), lambda state: state.has('Flippers', player))
|
||||
@@ -248,6 +253,7 @@ def global_rules(world, player):
|
||||
set_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: state.can_melt_things(player))
|
||||
set_rule(world.get_entrance('Ice Hookshot Ledge Path', player), lambda state: state.has('Hookshot', player))
|
||||
set_rule(world.get_entrance('Ice Hookshot Balcony Path', player), lambda state: state.has('Hookshot', player))
|
||||
if not world.get_door('Ice Switch Room SE', player).entranceFlag:
|
||||
set_rule(world.get_entrance('Ice Switch Room SE', player), lambda state: state.has('Cane of Somaria', player) or state.has('Convenient Block', player))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Boss', player))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Prize', player))
|
||||
@@ -291,6 +297,7 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('GT Hope Room EN', player), lambda state: state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('GT Conveyor Cross WN', player), lambda state: state.has('Hammer', player))
|
||||
set_rule(world.get_entrance('GT Conveyor Cross EN', player), lambda state: state.has('Hookshot', player))
|
||||
if not world.get_door('GT Speed Torch SE', player).entranceFlag:
|
||||
set_rule(world.get_entrance('GT Speed Torch SE', player), lambda state: state.has('Fire Rod', player))
|
||||
set_rule(world.get_entrance('GT Hookshot East-North Path', player), lambda state: state.has('Hookshot', player))
|
||||
set_rule(world.get_entrance('GT Hookshot South-East Path', player), lambda state: state.has('Hookshot', player))
|
||||
@@ -298,6 +305,7 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('GT Hookshot East-South Path', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player))
|
||||
set_rule(world.get_entrance('GT Hookshot North-East Path', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player))
|
||||
set_rule(world.get_entrance('GT Hookshot North-South Path', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player))
|
||||
set_rule(world.get_entrance('GT Hookshot Entry Boomerang Path', player), lambda state: state.has('Blue Boomerang', player) or state.has('Red Boomerang', player))
|
||||
set_rule(world.get_entrance('GT Firesnake Room Hook Path', player), lambda state: state.has('Hookshot', player))
|
||||
# I am tempted to stick an invincibility rule for getting across falling bridge
|
||||
set_rule(world.get_entrance('GT Ice Armos NE', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state))
|
||||
@@ -312,6 +320,7 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('GT Gauntlet 2 EN', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_entrance('GT Gauntlet 2 SW', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_entrance('GT Gauntlet 3 NW', player), lambda state: state.can_kill_most_things(player))
|
||||
if not world.get_door('GT Gauntlet 3 SW', player).entranceFlag:
|
||||
set_rule(world.get_entrance('GT Gauntlet 3 SW', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_entrance('GT Gauntlet 4 NW', player), lambda state: state.can_kill_most_things(player))
|
||||
set_rule(world.get_entrance('GT Gauntlet 4 SW', player), lambda state: state.can_kill_most_things(player))
|
||||
@@ -328,10 +337,14 @@ def global_rules(world, player):
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
|
||||
|
||||
# crystal switch rules
|
||||
if world.get_door('Thieves Attic ES', player).crystal == CrystalBarrier.Blue:
|
||||
set_rule(world.get_entrance('Thieves Attic ES', player), lambda state: state.can_reach_blue(world.get_region('Thieves Attic', player), player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Thieves Attic ES', player), lambda state: state.can_reach_orange(world.get_region('Thieves Attic', player), player))
|
||||
|
||||
set_rule(world.get_entrance('PoD Arena Crystal Path', player), lambda state: state.can_reach_blue(world.get_region('PoD Arena Crystal', player), player))
|
||||
set_rule(world.get_entrance('Swamp Trench 2 Pots Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Trench 2 Pots', player), player))
|
||||
set_rule(world.get_entrance('Swamp Shortcut Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Shortcut', player), player))
|
||||
set_rule(world.get_entrance('Thieves Attic ES', player), lambda state: state.can_reach_blue(world.get_region('Thieves Attic', player), player))
|
||||
set_rule(world.get_entrance('Thieves Hellway Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Thieves Hellway', player), player))
|
||||
set_rule(world.get_entrance('Thieves Hellway Crystal Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Thieves Hellway N Crystal', player), player))
|
||||
set_rule(world.get_entrance('Thieves Triple Bypass SE', player), lambda state: state.can_reach_blue(world.get_region('Thieves Triple Bypass', player), player))
|
||||
@@ -457,7 +470,7 @@ def default_rules(world, player):
|
||||
set_rule(world.get_entrance('East Dark World Bridge', player), lambda state: state.has_Pearl(player) and state.has('Hammer', player))
|
||||
set_rule(world.get_entrance('Lake Hylia Island Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player) and state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # ToDo any fake flipper set up?
|
||||
set_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has_Pearl(player))
|
||||
set_rule(world.get_entrance('Graveyard Ledge Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.has_Pearl(player) and state.can_lift_rocks(player))
|
||||
set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
@@ -503,6 +516,7 @@ def inverted_rules(world, player):
|
||||
set_rule(world.get_entrance('Castle Ledge S&Q', player), lambda state: state.has_Mirror(player) and state.has('Beat Agahnim 1', player))
|
||||
|
||||
# overworld requirements
|
||||
set_rule(world.get_location('Ice Rod Cave', player), lambda state: state.has_Pearl(player))
|
||||
set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player))
|
||||
set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has_Pearl(player))
|
||||
set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: state.has_Pearl(player))
|
||||
@@ -654,6 +668,7 @@ def no_glitches_rules(world, player):
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has('Flippers', player))
|
||||
else:
|
||||
add_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player) and (state.has('Flippers', player) or state.can_lift_rocks(player)))
|
||||
add_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to
|
||||
@@ -663,6 +678,7 @@ def no_glitches_rules(world, player):
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
add_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('East Dark World Pier', player), lambda state: state.has('Flippers', player))
|
||||
add_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has('Flippers', player))
|
||||
|
||||
# todo: move some dungeon rules to no glictes logic - see these for examples
|
||||
# add_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player))
|
||||
@@ -755,8 +771,8 @@ def no_glitches_rules(world, player):
|
||||
|
||||
def open_rules(world, player):
|
||||
# softlock protection as you can reach the sewers small key door with a guard drop key
|
||||
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), lambda state: state.has_key('Small Key (Escape)', player))
|
||||
set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player), lambda state: state.has_key('Small Key (Escape)', player))
|
||||
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), lambda state: state.has_sm_key('Small Key (Escape)', player))
|
||||
set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player), lambda state: state.has_sm_key('Small Key (Escape)', player))
|
||||
|
||||
|
||||
def swordless_rules(world, player):
|
||||
@@ -828,8 +844,13 @@ def standard_rules(world, player):
|
||||
set_rule(world.get_entrance('Sanctuary S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||
# these are because of rails
|
||||
if world.shuffle[player] != 'vanilla':
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (East)', player), lambda state: state.has('Zelda Delivered', player))
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (West)', player), lambda state: state.has('Zelda Delivered', player))
|
||||
# where ever these happen to be
|
||||
for portal_name in ['Hyrule Castle East', 'Hyrule Castle West']:
|
||||
entrance = world.get_portal(portal_name, player).door.entrance
|
||||
set_rule(entrance, lambda state: state.has('Zelda Delivered', player))
|
||||
set_rule(world.get_entrance('Sanctuary Exit', player), lambda state: state.has('Zelda Delivered', player))
|
||||
# zelda should be saved before agahnim is in play
|
||||
set_rule(world.get_location('Agahnim 1', player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
# too restrictive for crossed?
|
||||
def uncle_item_rule(item):
|
||||
@@ -870,7 +891,7 @@ def standard_rules(world, player):
|
||||
rule_list, debug_path = find_rules_for_zelda_delivery(world, player)
|
||||
set_rule(world.get_location('Zelda Drop Off', player), lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list))
|
||||
|
||||
for location in ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest']:
|
||||
for location in ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest', 'Maze Race']:
|
||||
add_rule(world.get_location(location, player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
# Bonk Fairy (Light) is a notable omission in ER shuffles/Retro
|
||||
@@ -902,7 +923,9 @@ def find_rules_for_zelda_delivery(world, player):
|
||||
region, path_rules, path = queue.popleft()
|
||||
for ext in region.exits:
|
||||
connect = ext.connected_region
|
||||
if connect and connect.type == RegionType.Dungeon and connect not in visited:
|
||||
valid_region = connect and connect not in visited and\
|
||||
(connect.type == RegionType.Dungeon or connect.name == 'Hyrule Castle Ledge')
|
||||
if valid_region:
|
||||
rule = ext.access_rule
|
||||
rule_list = list(path_rules)
|
||||
next_path = list(path)
|
||||
@@ -1324,7 +1347,7 @@ def set_bunny_rules(world, player):
|
||||
# Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing.
|
||||
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave',
|
||||
'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)']
|
||||
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
|
||||
bunny_accessible_locations = ['Link\'s House', 'Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
|
||||
'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid',
|
||||
'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge', 'Dark Blacksmith Ruins']
|
||||
|
||||
@@ -1408,7 +1431,7 @@ def set_inverted_bunny_rules(world, player):
|
||||
# Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing.
|
||||
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave',
|
||||
'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)', 'The Sky']
|
||||
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
|
||||
bunny_accessible_locations = ['Link\'s House', 'Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
|
||||
'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid',
|
||||
'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge', 'Dark Blacksmith Ruins',
|
||||
'Bombos Tablet', 'Ether Tablet', 'Purple Chest']
|
||||
@@ -1528,10 +1551,10 @@ bunny_impassible_doors = {
|
||||
'Ice Backwards Room Hole', 'Ice Switch Room SE', 'Ice Antechamber NE', 'Ice Antechamber Hole', 'Mire Lobby Gap',
|
||||
'Mire Post-Gap Gap', 'Mire 2 NE', 'Mire Hub Upper Blue Barrier', 'Mire Hub Lower Blue Barrier',
|
||||
'Mire Hub Right Blue Barrier', 'Mire Hub Top Blue Barrier', 'Mire Hub Switch Blue Barrier N',
|
||||
'Mire Hub Switch Blue Barrier S', 'Mire Falling Bridge WN',
|
||||
'Mire Map Spike Side Blue Barrier', 'Mire Map Spot Blue Barrier', 'Mire Crystal Dead End Left Barrier',
|
||||
'Mire Crystal Dead End Right Barrier', 'Mire Cross ES', 'Mire Hidden Shooters Block Path S',
|
||||
'Mire Hidden Shooters Block Path N', 'Mire Left Bridge Hook Path', 'Mire Fishbone Blue Barrier',
|
||||
'Mire Hub Switch Blue Barrier S', 'Mire Falling Bridge WN', 'Mire Map Spike Side Blue Barrier',
|
||||
'Mire Map Spot Blue Barrier', 'Mire Crystal Dead End Left Barrier', 'Mire Crystal Dead End Right Barrier',
|
||||
'Mire Cross ES', 'Mire Hidden Shooters Block Path S', 'Mire Hidden Shooters Block Path N',
|
||||
'Mire Left Bridge Hook Path', 'Mire Fishbone Blue Barrier',
|
||||
'Mire South Fish Blue Barrier', 'Mire Tile Room NW', 'Mire Compass Blue Barrier', 'Mire Attic Hint Hole',
|
||||
'Mire Dark Shooters SW', 'Mire Crystal Mid Blue Barrier', 'Mire Crystal Left Blue Barrier', 'TR Main Lobby Gap',
|
||||
'TR Lobby Ledge Gap', 'TR Hub SW', 'TR Hub SE', 'TR Hub ES', 'TR Hub EN', 'TR Hub NW', 'TR Hub NE', 'TR Torches NW',
|
||||
@@ -1543,14 +1566,15 @@ bunny_impassible_doors = {
|
||||
'GT Crystal Conveyor NE', 'GT Crystal Conveyor WN', 'GT Conveyor Cross EN', 'GT Conveyor Cross WN',
|
||||
'GT Hookshot East-North Path', 'GT Hookshot East-South Path', 'GT Hookshot North-East Path',
|
||||
'GT Hookshot North-South Path', 'GT Hookshot South-East Path', 'GT Hookshot South-North Path',
|
||||
'GT Hookshot Platform Blue Barrier', 'GT Hookshot Entry Blue Barrier', 'GT Double Switch Blue Path',
|
||||
'GT Double Switch Key Blue Path', 'GT Double Switch Blue Barrier', 'GT Double Switch Transition Blue',
|
||||
'GT Firesnake Room Hook Path', 'GT Falling Bridge WN', 'GT Falling Bridge WS', 'GT Ice Armos NE', 'GT Ice Armos WS',
|
||||
'GT Crystal Paths SW', 'GT Mimics 1 NW', 'GT Mimics 1 ES', 'GT Mimics 2 WS', 'GT Mimics 2 NE',
|
||||
'GT Hidden Spikes EN', 'GT Cannonball Bridge SE', 'GT Gauntlet 1 WN', 'GT Gauntlet 2 EN', 'GT Gauntlet 2 SW',
|
||||
'GT Gauntlet 3 NW', 'GT Gauntlet 3 SW', 'GT Gauntlet 4 NW', 'GT Gauntlet 4 SW', 'GT Gauntlet 5 NW',
|
||||
'GT Gauntlet 5 WS', 'GT Lanmolas 2 ES', 'GT Lanmolas 2 NW', 'GT Wizzrobes 1 SW', 'GT Wizzrobes 2 SE',
|
||||
'GT Wizzrobes 2 NE', 'GT Torch Cross ES', 'GT Falling Torches NE', 'GT Moldorm Gap', 'GT Validation Block Path'
|
||||
'GT Hookshot Platform Blue Barrier', 'GT Hookshot Entry Blue Barrier', 'GT Hookshot Entry Boomerang Path',
|
||||
'GT Double Switch Blue Path', 'GT Double Switch Key Blue Path', 'GT Double Switch Blue Barrier',
|
||||
'GT Double Switch Transition Blue', 'GT Firesnake Room Hook Path', 'GT Falling Bridge WN', 'GT Falling Bridge WS',
|
||||
'GT Ice Armos NE', 'GT Ice Armos WS', 'GT Crystal Paths SW', 'GT Mimics 1 NW', 'GT Mimics 1 ES', 'GT Mimics 2 WS',
|
||||
'GT Mimics 2 NE', 'GT Hidden Spikes EN', 'GT Cannonball Bridge SE', 'GT Gauntlet 1 WN', 'GT Gauntlet 2 EN',
|
||||
'GT Gauntlet 2 SW', 'GT Gauntlet 3 NW', 'GT Gauntlet 3 SW', 'GT Gauntlet 4 NW', 'GT Gauntlet 4 SW',
|
||||
'GT Gauntlet 5 NW', 'GT Gauntlet 5 WS', 'GT Lanmolas 2 ES', 'GT Lanmolas 2 NW', 'GT Wizzrobes 1 SW',
|
||||
'GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE', 'GT Torch Cross ES', 'GT Falling Torches NE', 'GT Moldorm Gap',
|
||||
'GT Validation Block Path'
|
||||
}
|
||||
|
||||
|
||||
@@ -1559,11 +1583,13 @@ def add_key_logic_rules(world, player):
|
||||
for d_name, d_logic in key_logic.items():
|
||||
for door_name, keys in d_logic.door_rules.items():
|
||||
spot = world.get_entrance(door_name, player)
|
||||
add_rule(spot, create_advanced_key_rule(d_logic, player, keys))
|
||||
if not world.retro[player] or world.mode[player] != 'standard' or not retro_in_hc(spot):
|
||||
rule = create_advanced_key_rule(d_logic, player, keys)
|
||||
if keys.opposite:
|
||||
add_rule(spot, create_advanced_key_rule(d_logic, player, keys.opposite), 'or')
|
||||
rule = or_rule(rule, create_advanced_key_rule(d_logic, player, keys.opposite))
|
||||
add_rule(spot, rule)
|
||||
for location in d_logic.bk_restricted:
|
||||
if location.name not in key_only_locations.keys():
|
||||
if not location.forced_item:
|
||||
forbid_item(location, d_logic.bk_name, player)
|
||||
for location in d_logic.sm_restricted:
|
||||
forbid_item(location, d_logic.small_key_name, player)
|
||||
@@ -1573,28 +1599,32 @@ def add_key_logic_rules(world, player):
|
||||
add_rule(world.get_location(chest.name, player), create_rule(d_logic.bk_name, player))
|
||||
|
||||
|
||||
def retro_in_hc(spot):
|
||||
return spot.parent_region.dungeon.name == 'Hyrule Castle' if spot.parent_region.dungeon else False
|
||||
|
||||
|
||||
def create_rule(item_name, player):
|
||||
return lambda state: state.has(item_name, player)
|
||||
|
||||
|
||||
def create_key_rule(small_key_name, player, keys):
|
||||
return lambda state: state.has_key(small_key_name, player, keys)
|
||||
return lambda state: state.has_sm_key(small_key_name, player, keys)
|
||||
|
||||
|
||||
def create_key_rule_allow_small(small_key_name, player, keys, location):
|
||||
loc = location.name
|
||||
return lambda state: state.has_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_key(small_key_name, player, keys-1))
|
||||
return lambda state: state.has_sm_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_key(small_key_name, player, keys-1))
|
||||
|
||||
|
||||
def create_key_rule_bk_exception(small_key_name, big_key_name, player, keys, bk_keys, bk_locs):
|
||||
chest_names = [x.name for x in bk_locs]
|
||||
return lambda state: (state.has_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_key(small_key_name, player, bk_keys))
|
||||
return lambda state: (state.has_sm_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_sm_key(small_key_name, player, bk_keys))
|
||||
|
||||
|
||||
def create_key_rule_bk_exception_or_allow(small_key_name, big_key_name, player, keys, location, bk_keys, bk_locs):
|
||||
loc = location.name
|
||||
chest_names = [x.name for x in bk_locs]
|
||||
return lambda state: (state.has_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_key(small_key_name, player, keys-1)) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_key(small_key_name, player, bk_keys))
|
||||
return lambda state: (state.has_sm_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_key(small_key_name, player, keys-1)) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_sm_key(small_key_name, player, bk_keys))
|
||||
|
||||
|
||||
def create_advanced_key_rule(key_logic, player, rule):
|
||||
|
||||
@@ -20,7 +20,10 @@ normal_offset_table = {
|
||||
0xc1: 0x81, 0xc2: 0x82, 0xc3: 0x83, 0xc4: 0x84, 0xc5: 0x85, 0xc6: 0x86, 0xc7: 0x87, 0xc8: 0x88,
|
||||
0xc9: 0x89, 0xcb: 0x8A, 0xcc: 0x8B, 0xce: 0x8C, 0xd1: 0x8D, 0xd2: 0x8E, 0xd5: 0x8F, 0xd6: 0x90,
|
||||
0xd8: 0x91, 0xd9: 0x92, 0xda: 0x93, 0xdb: 0x94, 0xdc: 0x95,
|
||||
0x40: 0x96, 0x42: 0x97 # newcomers for str stairs
|
||||
0x40: 0x96, 0x42: 0x97, # newcomers for str stairs
|
||||
# newcomers for entrances
|
||||
0x28: 0x98, 0x0e: 0x99, 0x0c: 0x9a, 0x98: 0x9b, 0x83: 0x9c, 0x84: 0x9d, 0x77: 0x9e, 0xe0: 0x9f,
|
||||
0x63: 0xa0
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +62,7 @@ door_pair_offset_table = {
|
||||
0xb8: 0x01a4, 0xb9: 0x01a6, 0xba: 0x01aa, 0xbb: 0x01ad, 0xbc: 0x01b3, 0xbe: 0x01bb, 0xbf: 0x01be, 0xc0: 0x01bf,
|
||||
0xc1: 0x01c2, 0xc2: 0x01ca, 0xc3: 0x01d2, 0xc4: 0x01d9, 0xc5: 0x01da, 0xc6: 0x01dd, 0xc7: 0x01e3, 0xc8: 0x01e6,
|
||||
0xc9: 0x01e7, 0xcb: 0x01ec, 0xcc: 0x01ed, 0xce: 0x01f0, 0xd0: 0x01f1, 0xd1: 0x01f3, 0xd2: 0x01f7, 0xd5: 0x01f8,
|
||||
0xd6: 0x01fa, 0xd8: 0x01fd, 0xd9: 0x0200, 0xda: 0x0203, 0xdb: 0x0204, 0xdc: 0x0206, 0xe0: 0x020
|
||||
0xd6: 0x01fa, 0xd8: 0x01fd, 0xd9: 0x0200, 0xda: 0x0203, 0xdb: 0x0204, 0xdc: 0x0206, 0xe0: 0x0207
|
||||
}
|
||||
|
||||
# Note: 0-7 correspond to 1,2,3,4,5,6,a,14 respectively, see doortables.asm : MultDivInfo
|
||||
@@ -84,3 +87,40 @@ divisor_lookup = {
|
||||
0xa0: {0x8: 7, 0x10: 6, 0x18: 7, 0x20: 4, 0x30: 6, 0x50: 1, 0xa0: 0},
|
||||
}
|
||||
|
||||
# I don't need this right now, but I don't want to throw it away either
|
||||
# Note: this value + 0x028000 is where the header is for the room (PC not SNES addressing)
|
||||
|
||||
# room_header_lookup = {
|
||||
# 0x0: 0x462, 0x1: 0x46c, 0x2: 0x47a, 0x3: 0x5dd, 0x4: 0x485, 0x5: 0x490, 0x6: 0x490, 0x7: 0x497,
|
||||
# 0x8: 0x4a2, 0x9: 0x4a9, 0xa: 0x4b5, 0xb: 0x4c0, 0xc: 0x4cb, 0xd: 0x4d8, 0xe: 0x4df, 0xf: 0x4ea,
|
||||
# 0x10: 0x4ea, 0x11: 0x4f1, 0x12: 0x4fc, 0x13: 0x503, 0x14: 0x511, 0x15: 0x518, 0x16: 0x523, 0x17: 0x52e,
|
||||
# 0x18: 0xc73, 0x19: 0x53a, 0x1a: 0x541, 0x1b: 0x54d, 0x1c: 0x558, 0x1d: 0x563, 0x1e: 0x56e, 0x1f: 0x579,
|
||||
# 0x20: 0x584, 0x21: 0x58b, 0x22: 0x58b, 0x23: 0x503, 0x24: 0x592, 0x25: 0x599, 0x26: 0x599, 0x27: 0x5a6,
|
||||
# 0x28: 0x5b2, 0x29: 0x5bd, 0x2a: 0x5c4, 0x2b: 0x5cb, 0x2c: 0xc73, 0x2d: 0x5d6, 0x2e: 0x5d6, 0x2f: 0x5dd,
|
||||
# 0x30: 0x5e4, 0x31: 0x5ef, 0x32: 0x5fb, 0x33: 0x606, 0x34: 0x60d, 0x35: 0x618, 0x36: 0x61f, 0x37: 0x618,
|
||||
# 0x38: 0x626, 0x39: 0x631, 0x3a: 0x63b, 0x3b: 0x646, 0x3c: 0x651, 0x3d: 0x658, 0x3e: 0x663, 0x3f: 0x66e,
|
||||
# 0x40: 0x67a, 0x41: 0x686, 0x42: 0x691, 0x43: 0x69d, 0x44: 0x6a4, 0x45: 0x6ab, 0x46: 0x6b6, 0x47: 0x6bd,
|
||||
# 0x48: 0x6bd, 0x49: 0x6bd, 0x4a: 0x6c4, 0x4b: 0x6d0, 0x4c: 0x6da, 0x4d: 0x6e5, 0x4e: 0x6f0, 0x4f: 0x6fb,
|
||||
# 0x50: 0x705, 0x51: 0x713, 0x52: 0x71e, 0x53: 0x72c, 0x54: 0x737, 0x55: 0x742, 0x56: 0x749, 0x57: 0x750,
|
||||
# 0x58: 0x757, 0x59: 0x75e, 0x5a: 0x765, 0x5b: 0x76c, 0x5c: 0x773, 0x5d: 0x77e, 0x5e: 0x789, 0x5f: 0x794,
|
||||
# 0x60: 0x7a0, 0x61: 0x7a7, 0x62: 0x7a0, 0x63: 0x7b2, 0x64: 0x7bd, 0x65: 0x7c8, 0x66: 0x7d2, 0x67: 0x7dd,
|
||||
# 0x68: 0x7e4, 0x69: 0x7eb, 0x6a: 0x7eb, 0x6b: 0x7f7, 0x6c: 0x802, 0x6d: 0x80d, 0x6e: 0x814, 0x6f: 0x81f,
|
||||
# 0x70: 0x81f, 0x71: 0x82b, 0x72: 0x836, 0x73: 0x841, 0x74: 0x848, 0x75: 0x84f, 0x76: 0x856, 0x77: 0x863,
|
||||
# 0x78: 0x870, 0x79: 0x870, 0x7a: 0x870, 0x7b: 0x870, 0x7c: 0x87a, 0x7d: 0x881, 0x7e: 0x88b, 0x7f: 0x896,
|
||||
# 0x80: 0x8a1, 0x81: 0x8ac, 0x82: 0x8ac, 0x83: 0x8b3, 0x84: 0x8ba, 0x85: 0x8c1, 0x86: 0x8c8, 0x87: 0x8c8,
|
||||
# 0x88: 0x8d4, 0x89: 0x8d4, 0x8a: 0x8de, 0x8b: 0x8de, 0x8c: 0x8e5, 0x8d: 0x8f2, 0x8e: 0x8f9, 0x8f: 0x904,
|
||||
# 0x90: 0x904, 0x91: 0x90b, 0x92: 0x916, 0x93: 0x91d, 0x94: 0x928, 0x95: 0x928, 0x96: 0x92f, 0x97: 0x93a,
|
||||
# 0x98: 0x945, 0x99: 0x950, 0x9a: 0x95b, 0x9b: 0x95b, 0x9c: 0x965, 0x9d: 0x96c, 0x9e: 0x976, 0x9f: 0x981,
|
||||
# 0xa0: 0x988, 0xa1: 0x993, 0xa2: 0x99a, 0xa3: 0x993, 0xa4: 0x9a5, 0xa5: 0x9ac, 0xa6: 0x9b7, 0xa7: 0x9c2,
|
||||
# 0xa8: 0x9cc, 0xa9: 0x9d3, 0xaa: 0x9dd, 0xab: 0x9e4, 0xac: 0x9ef, 0xad: 0x9f6, 0xae: 0x9f6, 0xaf: 0xa01,
|
||||
# 0xb0: 0xa08, 0xb1: 0xa14, 0xb2: 0xa1e, 0xb3: 0xa25, 0xb4: 0xa2c, 0xb5: 0xa37, 0xb6: 0xa42, 0xb7: 0x50a,
|
||||
# 0xb8: 0xa4d, 0xb9: 0xa54, 0xba: 0xa5b, 0xbb: 0xa62, 0xbc: 0xa69, 0xbd: 0xa74, 0xbe: 0xa74, 0xbf: 0xa7f,
|
||||
# 0xc0: 0xa86, 0xc1: 0xa92, 0xc2: 0xa99, 0xc3: 0xaa0, 0xc4: 0xaa7, 0xc5: 0xab2, 0xc6: 0x50a, 0xc7: 0xab9,
|
||||
# 0xc8: 0xac0, 0xc9: 0xac7, 0xca: 0xace, 0xcb: 0xace, 0xcc: 0xace, 0xcd: 0xad5, 0xce: 0xad5, 0xcf: 0xadf,
|
||||
# 0xd0: 0xadf, 0xd1: 0xaeb, 0xd2: 0xaf6, 0xd3: 0xb01, 0xd4: 0xb01, 0xd5: 0xab2, 0xd6: 0x50a, 0xd7: 0xb01,
|
||||
# 0xd8: 0xb01, 0xd9: 0xb08, 0xda: 0xb0f, 0xdb: 0xace, 0xdc: 0xace, 0xdd: 0xb1a, 0xde: 0xb1a, 0xdf: 0xb21,
|
||||
# 0xe0: 0xb2c, 0xe1: 0xb37, 0xe2: 0xb3e, 0xe3: 0xb45, 0xe4: 0xb4c, 0xe5: 0xb4c, 0xe6: 0xb53, 0xe7: 0xb53,
|
||||
# 0xe8: 0xb5a, 0xe9: 0xb68, 0xea: 0xb68, 0xeb: 0xb73, 0xec: 0xb7e, 0xed: 0xb7e, 0xee: 0xb8a, 0xef: 0xb94,
|
||||
# 0xf0: 0xb53, 0xf1: 0xb53, 0xf2: 0xba0, 0xf3: 0xba0, 0xf4: 0xba5, 0xf5: 0xba5, 0xf6: 0xbac, 0xf7: 0xbac,
|
||||
# 0xf8: 0xbac, 0xf9: 0xbba, 0xfa: 0xbc1, 0xfb: 0xbcc, 0xfc: 0xbd7, 0xfd: 0xbd7, 0xfe: 0xbba, 0xff: 0xbe3
|
||||
# }
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import multiprocessing
|
||||
import concurrent.futures
|
||||
import argparse
|
||||
from collections import OrderedDict
|
||||
|
||||
cpu_threads = multiprocessing.cpu_count()
|
||||
py_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
||||
|
||||
|
||||
def main(args=None):
|
||||
successes = []
|
||||
errors = []
|
||||
task_mapping = []
|
||||
tests = OrderedDict()
|
||||
|
||||
successes.append(f"Testing {args.dr} DR with {args.count} Tests" + (f" (intensity={args.tense})" if args.dr in ['basic', 'crossed'] else ""))
|
||||
print(successes[0])
|
||||
|
||||
max_attempts = args.count
|
||||
pool = concurrent.futures.ThreadPoolExecutor(max_workers=cpu_threads)
|
||||
dead_or_alive = 0
|
||||
alive = 0
|
||||
|
||||
def test(testname: str, command: str):
|
||||
tests[testname] = [command]
|
||||
for mode in [['Open', ''],
|
||||
['Std ', ' --mode standard'],
|
||||
['Inv ', ' --mode inverted']]:
|
||||
|
||||
basecommand = f"python3.8 DungeonRandomizer.py --door_shuffle {args.dr} --intensity {args.tense} --suppress_rom --suppress_spoiler"
|
||||
|
||||
def gen_seed():
|
||||
taskcommand = basecommand + " " + command + mode[1]
|
||||
return subprocess.run(taskcommand, capture_output=True, shell=True, text=True)
|
||||
|
||||
for x in range(1, max_attempts + 1):
|
||||
task = pool.submit(gen_seed)
|
||||
task.success = False
|
||||
task.name = testname
|
||||
task.mode = mode[0]
|
||||
task.cmd = basecommand + " " + command + mode[1]
|
||||
task_mapping.append(task)
|
||||
|
||||
test("Vanilla ", "--shuffle vanilla")
|
||||
test("Retro ", "--retro --shuffle vanilla")
|
||||
test("Keysanity ", "--shuffle vanilla --keydropshuffle --keysanity")
|
||||
test("Simple ", "--shuffle simple")
|
||||
test("Full ", "--shuffle full")
|
||||
test("Crossed ", "--shuffle crossed")
|
||||
test("Insanity ", "--shuffle insanity")
|
||||
|
||||
from tqdm import tqdm
|
||||
with tqdm(concurrent.futures.as_completed(task_mapping),
|
||||
total=len(task_mapping), unit="seed(s)",
|
||||
desc=f"Success rate: 0.00%") as progressbar:
|
||||
for task in progressbar:
|
||||
dead_or_alive += 1
|
||||
try:
|
||||
result = task.result()
|
||||
if result.returncode:
|
||||
errors.append([task.name + task.mode, task.cmd, result.stderr])
|
||||
else:
|
||||
alive += 1
|
||||
task.success = True
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
progressbar.set_description(f"Success rate: {(alive/dead_or_alive)*100:.2f}% - {task.name}{task.mode}")
|
||||
|
||||
def get_results(testname: str):
|
||||
result = ""
|
||||
for mode in ['Open', 'Std ', 'Inv ']:
|
||||
dead_or_alive = [task.success for task in task_mapping if task.name == testname and task.mode == mode]
|
||||
alive = [x for x in dead_or_alive if x]
|
||||
success = f"{testname}{mode} Rate: {(len(alive) / len(dead_or_alive)) * 100:.2f}%"
|
||||
successes.append(success)
|
||||
print(success)
|
||||
result += f"{(len(alive)/len(dead_or_alive))*100:.2f}%\t"
|
||||
return result.strip()
|
||||
|
||||
results = []
|
||||
for t in tests.keys():
|
||||
results.append(get_results(t))
|
||||
|
||||
for result in results:
|
||||
print(result)
|
||||
successes.append(result)
|
||||
|
||||
return successes, errors
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
successes = []
|
||||
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument('--count', default=0, type=lambda value: max(int(value), 0))
|
||||
parser.add_argument('--cpu_threads', default=cpu_threads, type=lambda value: max(int(value), 1))
|
||||
parser.add_argument('--help', default=False, action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.help:
|
||||
parser.print_help()
|
||||
exit(0)
|
||||
|
||||
cpu_threads = args.cpu_threads
|
||||
|
||||
for dr in [['vanilla', args.count if args.count else 2, 1],
|
||||
['basic', args.count if args.count else 5, 3],
|
||||
['crossed', args.count if args.count else 10, 3]]:
|
||||
|
||||
for tense in range(1, dr[2] + 1):
|
||||
args = argparse.Namespace()
|
||||
args.dr = dr[0]
|
||||
args.tense = tense
|
||||
args.count = dr[1]
|
||||
s, errors = main(args=args)
|
||||
if successes:
|
||||
successes += [""] * 2
|
||||
successes += s
|
||||
print()
|
||||
|
||||
if errors:
|
||||
with open(f"{dr[0]}{(f'-{tense}' if dr[0] in ['basic', 'crossed'] else '')}-errors.txt", 'w') as stream:
|
||||
for error in errors:
|
||||
stream.write(error[0] + "\n")
|
||||
stream.write(error[1] + "\n")
|
||||
stream.write(error[2] + "\n\n")
|
||||
|
||||
with open("success.txt", "w") as stream:
|
||||
stream.write(str.join("\n", successes))
|
||||
|
||||
input("Press enter to continue")
|
||||
@@ -4,6 +4,7 @@ import re
|
||||
import subprocess
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import defaultdict
|
||||
|
||||
def int16_as_bytes(value):
|
||||
value = value & 0xFFFF
|
||||
@@ -225,6 +226,47 @@ def read_entrance_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan)
|
||||
print(string)
|
||||
|
||||
|
||||
def room_palette_data(old_rom):
|
||||
with open(old_rom, 'rb') as stream:
|
||||
old_rom_data = bytearray(stream.read())
|
||||
|
||||
offset = defaultdict(list)
|
||||
for i in range(0, 256):
|
||||
pointer_offset = 0x0271e2+i*2
|
||||
header_offset = old_rom_data[pointer_offset + 1] << 8
|
||||
header_offset += old_rom_data[pointer_offset]
|
||||
header_offset -= 0x8000
|
||||
header_offset += 0x020000
|
||||
offset[header_offset].append(i)
|
||||
# print(f'{hex(i)}: {hex(old_rom_data[header_offset+1])}')
|
||||
for header_offset, rooms in offset.items():
|
||||
print(f'{hex(header_offset)}: {[hex(x) for x in rooms]}')
|
||||
|
||||
|
||||
|
||||
# Palette notes:
|
||||
# HC: 0
|
||||
# Sewer/Dungeon: 1
|
||||
# AT: 0xc near boss, 0x0 (other) 26 (f4, f1)
|
||||
# Sanc: 0x1d
|
||||
# Hera: 0x6
|
||||
# Desert: 0x4 (boss and near boss), 0x9 (desert tiles 1 + desert main)
|
||||
# Eastern: 0xb
|
||||
# Pod: 0xf, x10 (boss)
|
||||
# Swamp: 0x8 (boss), 0xa (other)
|
||||
# Skull: 0xe (boss), 0xd (other)
|
||||
# TT: 0x17, 0x23 (attic)
|
||||
# Ice: 0x13, 0x14 (boss)
|
||||
# Mire: 0x11 (other) , 0x12 (boss/preroom)
|
||||
# TR: 0x18, 0x19 (boss+pre)
|
||||
# GT: 0x28 (entrance + B1), 0x1a (other) 0x24 (Gauntlet - Lanmo) 0x25 (conveyor-torch-wizzrode moldorm pit f5?)
|
||||
# Aga2: 0x1b, 0x1b (Pre aga2)
|
||||
# Caves: 0x7, 0x20
|
||||
# Uncle: 0x1
|
||||
# Ganon: 0x21
|
||||
# Houses: 0x2
|
||||
|
||||
|
||||
def print_wiki_doors_by_region(d_regions, world, player):
|
||||
for d, region_list in d_regions.items():
|
||||
tile_map = {}
|
||||
@@ -415,7 +457,202 @@ def print_graph(world):
|
||||
ET.dump(root)
|
||||
|
||||
|
||||
def extract_data_from_us_rom(rom):
|
||||
with open(rom, 'rb') as stream:
|
||||
rom_data = bytearray(stream.read())
|
||||
|
||||
rooms = [0x9a, 0x69, 0x78, 0x79, 0x7a, 0x88, 0x8a, 0xad]
|
||||
for room in rooms:
|
||||
b2idx = room*2
|
||||
b3idx = room*3
|
||||
headerptr = 0x110000 + b2idx # zscream specific
|
||||
headerloc = rom_data[headerptr] + rom_data[headerptr+1]*0x100 + 0x108000 # zscream specific
|
||||
header, objectdata, spritedata, secretdata = [], [], [], []
|
||||
for i in range(0, 14):
|
||||
header.append(rom_data[headerloc+i])
|
||||
objectptr = 0xF8000 + b3idx
|
||||
objectloc = rom_data[objectptr] + rom_data[objectptr+1]*0x100 + rom_data[objectptr+2]*0x10000
|
||||
objectloc -= 0x100000
|
||||
stop, idx = False, 0
|
||||
ffcnt = 0
|
||||
mode = 0
|
||||
while ffcnt < 2:
|
||||
b1 = rom_data[objectloc+idx]
|
||||
b2 = rom_data[objectloc+idx+1]
|
||||
b3 = rom_data[objectloc+idx+2]
|
||||
objectdata.append(b1)
|
||||
objectdata.append(b2)
|
||||
if b1 == 0xff and b2 == 0xff:
|
||||
ffcnt += 1
|
||||
mode = 0
|
||||
if b1 == 0xf0 and b2 == 0xff:
|
||||
mode = 1
|
||||
if not mode and ffcnt < 2:
|
||||
objectdata.append(b3)
|
||||
idx += 3
|
||||
else:
|
||||
idx += 2
|
||||
spriteptr = 0x4d62e + b2idx
|
||||
spriteloc = rom_data[spriteptr] + rom_data[spriteptr+1]*0x100 + 0x40000
|
||||
done, idx = False, 0
|
||||
while not done:
|
||||
b1 = rom_data[spriteloc+idx]
|
||||
spritedata.append(b1)
|
||||
if b1 == 0xff:
|
||||
done = True
|
||||
idx += 1
|
||||
secretptr = 0xdb69 + b2idx
|
||||
secretloc = rom_data[secretptr] + rom_data[secretptr+1]*0x100
|
||||
done, idx = False, 0
|
||||
while not done:
|
||||
b1 = rom_data[secretloc+idx]
|
||||
b2 = rom_data[secretloc+idx+1]
|
||||
b3 = rom_data[secretloc+idx+2]
|
||||
secretdata.append(b1)
|
||||
secretdata.append(b2)
|
||||
if b1 == 0xff and b2 == 0xff:
|
||||
done = True
|
||||
else:
|
||||
secretdata.append(b3)
|
||||
idx += 3
|
||||
|
||||
print(f'Room {room:02x}')
|
||||
print(f'db {",".join([f"${x:02x}" for x in header])}')
|
||||
print(f'Obj Length: {len(objectdata)}')
|
||||
print_data_block(objectdata)
|
||||
print('Sprites')
|
||||
print_data_block(spritedata)
|
||||
print('Secrets')
|
||||
print_data_block(secretdata)
|
||||
|
||||
blockdata, torchdata = [], []
|
||||
blockloc = 0x271de
|
||||
for i in range(0, 128):
|
||||
idx = i*4
|
||||
b1 = rom_data[blockloc+idx]
|
||||
b2 = rom_data[blockloc+idx+1]
|
||||
room_idx = b1 + b2*0x100
|
||||
if room_idx in rooms:
|
||||
blockdata.append(b1)
|
||||
blockdata.append(b2)
|
||||
blockdata.append(rom_data[blockloc+idx+2])
|
||||
blockdata.append(rom_data[blockloc+idx+3])
|
||||
torchloc = 0x2736A
|
||||
nomatch = False
|
||||
append = False
|
||||
for i in range(0, 192):
|
||||
idx = i*2
|
||||
b1 = rom_data[torchloc+idx]
|
||||
b2 = rom_data[torchloc+idx+1]
|
||||
if nomatch:
|
||||
if b1 == 0xff and b2 == 0xff:
|
||||
nomatch = False
|
||||
elif not append:
|
||||
room_idx = b1 + b2*0x100
|
||||
if room_idx in rooms:
|
||||
append = True
|
||||
else:
|
||||
nomatch = True
|
||||
if append:
|
||||
torchdata.append(b1)
|
||||
torchdata.append(b2)
|
||||
if b1 == 0xff and b2 == 0xff:
|
||||
append = False
|
||||
print('Blocks')
|
||||
print_data_block(blockdata)
|
||||
print('Torches')
|
||||
print_data_block(torchdata)
|
||||
print()
|
||||
|
||||
|
||||
def print_data_block(block):
|
||||
for i in range(0, len(block)//16 + 1):
|
||||
slice = block[i*16:i*16+16]
|
||||
print(f'db {",".join([f"${x:02x}" for x in slice])}')
|
||||
|
||||
|
||||
def extract_data_from_jp_rom(rom):
|
||||
with open(rom, 'rb') as stream:
|
||||
rom_data = bytearray(stream.read())
|
||||
|
||||
# rooms = [0x7b, 0x7c, 0x7d, 0x8b, 0x8c, 0x8d, 0x9b, 0x9c, 0x9d]
|
||||
rooms = [0x1a, 0x2a, 0xd1]
|
||||
for room in rooms:
|
||||
b2idx = room*2
|
||||
b3idx = room*3
|
||||
headerptr = 0x271e2 + b2idx
|
||||
headerloc = rom_data[headerptr] + rom_data[headerptr+1]*0x100 + 0x18000
|
||||
header, objectdata, spritedata, secretdata = [], [], [], []
|
||||
for i in range(0, 14):
|
||||
header.append(rom_data[headerloc+i])
|
||||
objectptr = 0xF8000 + b3idx
|
||||
objectloc = rom_data[objectptr] + rom_data[objectptr+1]*0x100 + rom_data[objectptr+2]*0x10000
|
||||
objectloc -= 0x100000
|
||||
stop, idx = False, 0
|
||||
ffcnt = 0
|
||||
mode = 0
|
||||
while ffcnt < 2:
|
||||
b1 = rom_data[objectloc+idx]
|
||||
b2 = rom_data[objectloc+idx+1]
|
||||
b3 = rom_data[objectloc+idx+2]
|
||||
objectdata.append(b1)
|
||||
objectdata.append(b2)
|
||||
if b1 == 0xff and b2 == 0xff:
|
||||
ffcnt += 1
|
||||
mode = 0
|
||||
if b1 == 0xf0 and b2 == 0xff:
|
||||
mode = 1
|
||||
if not mode and ffcnt < 2:
|
||||
objectdata.append(b3)
|
||||
idx += 3
|
||||
else:
|
||||
idx += 2
|
||||
spriteptr = 0x4d62e + b2idx
|
||||
spriteloc = rom_data[spriteptr] + rom_data[spriteptr+1]*0x100 + 0x40000
|
||||
secretptr = 0xdb67 + b2idx
|
||||
secretloc = rom_data[secretptr] + rom_data[secretptr+1]*0x100
|
||||
done, idx = False, 0
|
||||
while not done:
|
||||
b1 = rom_data[spriteloc+idx]
|
||||
spritedata.append(b1)
|
||||
if b1 == 0xff:
|
||||
done = True
|
||||
idx += 1
|
||||
done, idx = False, 0
|
||||
while not done:
|
||||
b1 = rom_data[secretloc+idx]
|
||||
b2 = rom_data[secretloc+idx+1]
|
||||
b3 = rom_data[secretloc+idx+2]
|
||||
secretdata.append(b1)
|
||||
secretdata.append(b2)
|
||||
if b1 == 0xff and b2 == 0xff:
|
||||
done = True
|
||||
else:
|
||||
secretdata.append(b3)
|
||||
idx += 3
|
||||
print(f'Room {room:02x}')
|
||||
print(f'HeaderPtr {headerptr:06x}')
|
||||
print(f'HeaderLoc {headerloc:06x}')
|
||||
print(f'db {",".join([f"${x:02x}" for x in header])}')
|
||||
print(f'Obj Length: {len(objectdata)}')
|
||||
print(f'ObjectPtr {objectptr:06x}')
|
||||
print(f'ObjectLoc {objectloc:06x}')
|
||||
for i in range(0, len(objectdata)//16 + 1):
|
||||
slice = objectdata[i*16:i*16+16]
|
||||
print(f'db {",".join([f"${x:02x}" for x in slice])}')
|
||||
print(f'SpritePtr {spriteptr:06x}')
|
||||
print(f'SpriteLoc {spriteloc:06x}')
|
||||
print_data_block(spritedata)
|
||||
print(f'SecretPtr {secretptr:06x}')
|
||||
print(f'SecretLoc {secretloc:06x}')
|
||||
print_data_block(secretdata)
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# make_new_base2current()
|
||||
# read_entrance_data(old_rom=sys.argv[1])
|
||||
read_layout_data(old_rom=sys.argv[1])
|
||||
# room_palette_data(old_rom=sys.argv[1])
|
||||
# extract_data_from_us_rom(sys.argv[1])
|
||||
extract_data_from_jp_rom(sys.argv[1])
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
!add = "clc : adc"
|
||||
!addl = "clc : adc.l"
|
||||
!sub = "sec : sbc"
|
||||
!bge = "bcs"
|
||||
!blt = "bcc"
|
||||
@@ -33,6 +34,11 @@ incsrc overrides.asm
|
||||
incsrc edges.asm
|
||||
incsrc math.asm
|
||||
incsrc hudadditions.asm
|
||||
incsrc dr_lobby.asm
|
||||
warnpc $279700
|
||||
|
||||
incsrc doortables.asm
|
||||
warnpc $288000
|
||||
|
||||
; deals with own hooks
|
||||
incsrc keydropshuffle.asm
|
||||
|
||||
+72
-25
@@ -39,21 +39,22 @@ db $70
|
||||
|
||||
org $279F00
|
||||
DoorOffset:
|
||||
db $00,$01,$02,$00,$03,$00,$04,$00,$00,$00,$00,$00,$00,$05,$00,$00
|
||||
db $00,$01,$02,$00,$03,$00,$04,$00,$00,$00,$00,$00,$9A,$05,$99,$00
|
||||
db $00,$06,$07,$08,$09,$0A,$0B,$00,$00,$0C,$0D,$0E,$00,$0F,$10,$11
|
||||
db $12,$13,$14,$15,$16,$00,$17,$00,$00,$00,$18,$19,$00,$00,$1A,$00
|
||||
db $12,$13,$14,$15,$16,$00,$17,$00,$98,$00,$18,$19,$00,$00,$1A,$00
|
||||
db $1B,$00,$1C,$1D,$1E,$1F,$20,$21,$22,$23,$24,$25,$00,$26,$27,$00
|
||||
db $96,$28,$97,$29,$2A,$2B,$2C,$00,$00,$2D,$2E,$2F,$30,$31,$32,$00
|
||||
db $33,$34,$35,$36,$00,$00,$37,$38,$39,$3A,$3B,$3C,$3D,$3E,$3F,$40
|
||||
db $41,$42,$43,$00,$00,$00,$44,$45,$46,$00,$47,$48,$49,$4A,$4B,$00
|
||||
db $00,$4C,$00,$00,$00,$4D,$4E,$00,$00,$00,$00,$4F,$50,$51,$52,$53
|
||||
db $00,$54,$00,$00,$00,$55,$00,$00,$00,$00,$00,$56,$57,$58,$59,$00
|
||||
db $5A,$5B,$5C,$5D,$00,$5E,$5F,$00,$00,$60,$00,$61,$62,$63,$64,$65
|
||||
db $41,$42,$43,$A0,$00,$00,$44,$45,$46,$00,$47,$48,$49,$4A,$4B,$00
|
||||
; 0 1 2 3 4 5 6 7 8 9 a b c d e f --Offset Ruler
|
||||
db $00,$4C,$00,$00,$00,$4D,$4E,$9E,$00,$00,$00,$4F,$50,$51,$52,$53
|
||||
db $00,$54,$00,$9C,$9D,$55,$00,$00,$00,$00,$00,$56,$57,$58,$59,$00
|
||||
db $5A,$5B,$5C,$5D,$00,$5E,$5F,$00,$9B,$60,$00,$61,$62,$63,$64,$65
|
||||
db $66,$67,$68,$69,$6A,$6B,$00,$00,$6C,$6D,$6E,$6F,$70,$00,$71,$72
|
||||
db $00,$73,$74,$75,$76,$77,$78,$79,$7A,$7B,$7C,$7D,$7E,$00,$7F,$80
|
||||
db $00,$81,$82,$83,$84,$85,$86,$87,$88,$89,$00,$8A,$8B,$00,$8C,$00
|
||||
db $00,$8D,$8E,$00,$00,$8F,$90,$00,$91,$92,$93,$94,$95,$00,$00,$00
|
||||
db $00
|
||||
db $9f
|
||||
|
||||
org $27A000
|
||||
DoorTable:
|
||||
@@ -210,7 +211,16 @@ dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003,
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; TT SE Quad
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Aga 6F
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Sewers Rope
|
||||
; this should end at 27AE40 about (152 * 24 bytes = 3648 or E40)
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Swamp Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Ice Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; GT Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Mire Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Desert West Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Desert Main Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Hera Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Tower Lobby
|
||||
dw $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003, $0003 ; Desert Back Lobby
|
||||
; this should end at 27AF18 about (160 * 24 bytes = 3840 or F18)
|
||||
; some values you can hardcode for spirals
|
||||
;dw $0070, $36a0 ; ->HC Stairwell
|
||||
;dw $0072, $4ff8 ; ->HC Map Room
|
||||
@@ -552,23 +562,26 @@ db $01, $02, $03, $04, $05, $06, $0a, $14
|
||||
; HC HC EP DP AT SP PD MM SW IP TH TT TR GT
|
||||
org $27f000
|
||||
CompassBossIndicator:
|
||||
dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
|
||||
TotalKeys: ;27f01c
|
||||
db $04, $04, $02, $04, $04, $06, $06, $06, $05, $06, $01, $03, $06, $08
|
||||
ChestKeys: ;27f02a
|
||||
db $01, $01, $00, $01, $02, $01, $06, $03, $03, $02, $01, $01, $04, $04
|
||||
BigKeyStatus: ;27f038 (status 2 indicate BnC guard)
|
||||
dw $0002, $0002, $0001, $0001, $0000, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001
|
||||
DungeonReminderTable: ;27f054
|
||||
dw $2D50, $2D50, $2D51, $2D52, $2D54, $2D56, $2D55, $2D5A, $2D57, $2D59, $2D53, $2D58, $2D5B, $2D5C
|
||||
TotalLocationsLow: ;27f070
|
||||
db $08, $08, $06, $06, $02, $00, $04, $08, $08, $08, $06, $08, $02, $07
|
||||
TotalLocationsHigh: ;27f07e
|
||||
db $00, $00, $00, $00, $00, $01, $01, $00, $00, $00, $00, $00, $01, $02
|
||||
;27F08C
|
||||
dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
|
||||
TotalKeys: ;27f020
|
||||
db $04, $04, $02, $04, $04, $06, $06, $06, $05, $06, $01, $03, $06, $08, $00, $00
|
||||
ChestKeys: ;27f030
|
||||
db $01, $01, $00, $01, $02, $01, $06, $03, $03, $02, $01, $01, $04, $04, $00, $00
|
||||
BigKeyStatus: ;27f040 (status 2 indicate BnC guard)
|
||||
dw $0002, $0002, $0001, $0001, $0000, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0000, $0000
|
||||
DungeonReminderTable: ;27f060
|
||||
dw $2D50, $2D50, $2D51, $2D52, $2D54, $2D56, $2D55, $2D5A, $2D57, $2D59, $2D53, $2D58, $2D5B, $2D5C, $0000, $0000
|
||||
TotalLocationsLow: ;27f080
|
||||
db $08, $08, $06, $06, $02, $00, $04, $08, $08, $08, $06, $08, $02, $07, $00, $00
|
||||
TotalLocationsHigh: ;27f090
|
||||
db $00, $00, $00, $00, $00, $01, $01, $00, $00, $00, $00, $00, $01, $02, $00, $00
|
||||
org $27f0a0
|
||||
TotalLocations:
|
||||
db $08, $08, $06, $06, $02, $0a, $0e, $08, $08, $08, $06, $08, $0c, $1b, $00, $00
|
||||
; no more room here
|
||||
|
||||
; Vert 0,6,0 Horz 2,0,8
|
||||
org $27f090
|
||||
org $27f0b0
|
||||
CoordIndex: ; Horizontal 1st
|
||||
db 2, 0 ; Coordinate Index $20-$23
|
||||
OppCoordIndex:
|
||||
@@ -588,7 +601,16 @@ dw $007f, $0077 ; Left/Top camera bounds when at edge or layout frozen
|
||||
dw $0007, $000b ; Left/Top camera bounds when not frozen + appropriate low byte $22/$20 (preadj. by #$78/#$6c)
|
||||
dw $00ff, $010b ; Right/Bot camera bounds when not frozen + appropriate low byte $20/$22
|
||||
dw $017f, $0187 ; Right/Bot camera bound when at edge or layout frozen
|
||||
;27f0ae next free byte
|
||||
;27f0ce next free byte
|
||||
|
||||
org $27f0f0
|
||||
RemoveRainDoorsRoom:
|
||||
dw $0060, $0062, $ffff ; ffff indicates end of list
|
||||
RainDoorMatch: ; org $27f0f6 and f8 for now
|
||||
dw $0081, $0061 ; not xba'd
|
||||
BlockSanctuaryDoorInRain: ;27f0fa
|
||||
dw $0000
|
||||
|
||||
|
||||
org $27f100
|
||||
TilesetTable:
|
||||
@@ -609,5 +631,30 @@ db $04,$0c,$0c,$0c,$0d,$0d,$0d,$0d,$05,$05,$ff,$0a,$0a,$ff,$0b,$ff
|
||||
db $04,$0c,$0c,$ff,$ff,$0d,$0d,$ff,$05,$05,$05,$0a,$0a,$ff,$0b,$06
|
||||
db $04,$06,$06,$06,$06,$06,$06,$06,$06,$ff,$06,$06,$ff,$06,$06,$06
|
||||
db $06,$06,$03,$03,$03,$03,$ff,$ff,$06,$06,$06,$06,$ff,$06,$06,$06
|
||||
;27f200
|
||||
|
||||
;27f200
|
||||
PaletteTable:
|
||||
db $21,$00,$00,$07,$00,$08,$00,$00,$07,$00,$00,$00,$00,$00,$00,$21
|
||||
db $21,$00,$00,$00,$00,$00,$00,$00,$07,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$0e,$00,$00,$07,$00,$00,$07
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$07,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$13
|
||||
db $00,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
; 0 1 2 3 4 5 6 7 8 9 a b c d e f --Offset Ruler
|
||||
db $00,$00,$00,$00,$00,$00,$00,$06,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
|
||||
db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$14,$20
|
||||
db $00,$07,$20,$20,$07,$07,$07,$07,$07,$20,$20,$07,$20,$20,$20,$20
|
||||
db $07,$07,$02,$02,$02,$02,$07,$07,$07,$20,$20,$07,$20,$20,$20,$07
|
||||
|
||||
;27f300
|
||||
|
||||
;
|
||||
org $27ff00
|
||||
SancDarkWorldFlag:
|
||||
db 0
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
CheckDarkWorldSanc:
|
||||
STA $A0 : STA $048E ; what we wrote over
|
||||
LDA.l SancDarkWorldFlag : BEQ +
|
||||
SEP #$30
|
||||
LDA $A0 : CMP #$12 : BNE ++
|
||||
LDA.l $7EF357 : BNE ++ ; moon pearl?
|
||||
LDA #$17 : STA $5D : INC $02E0 : LDA.b #$40 : STA !DARK_WORLD
|
||||
++ REP #$30
|
||||
+ RTL
|
||||
+34
-12
@@ -59,6 +59,15 @@ rts
|
||||
nop #5
|
||||
.done
|
||||
|
||||
org $01b618 ; Bank01.asm : 7963 Dungeon_LoadHeader (REP #$20 : INY : LDA [$0D], Y)
|
||||
nop : jsl OverridePaletteHeader
|
||||
|
||||
org $02817e ; Bank02.asm : 414 (LDA $02811E, X)
|
||||
jsl FixAnimatedTiles
|
||||
|
||||
org $028a06 ; Bank02.asm : 1941 Dungeon_ResetTorchBackgroundAndPlayer
|
||||
JSL FixWallmasterLamp
|
||||
|
||||
org $00d377 ;Bank 00 line 3185
|
||||
DecompDungAnimatedTiles:
|
||||
org $00fda4 ;Bank 00 line 8882
|
||||
@@ -101,10 +110,6 @@ nop : stz $0dd0, X : rts
|
||||
.not_in_ganons_tower
|
||||
|
||||
|
||||
org $208206
|
||||
jsl MirrorCheckOverride2
|
||||
org $208270
|
||||
jsl MirrorCheckOverride2
|
||||
org $07a955 ; <- Bank07.asm : around 6564 (JP is a bit different) (STZ $05FC : STZ $05FD)
|
||||
jsl BlockEraseFix
|
||||
nop #2
|
||||
@@ -123,19 +128,36 @@ nop #3
|
||||
org $028fc9
|
||||
nop #2 : jsl BlindAtticFix
|
||||
|
||||
; also rando's hooks.asm line 1360
|
||||
; 106e4e -> goes to a0ee4e
|
||||
org $a0ee8a ; <- 6FC4C - headsup_display.asm : 836 (LDA $7EF36E : AND.w #$00FF : ADD.w #$0007 : AND.w #$FFF8 : TAX)
|
||||
jsl DrHudOverride
|
||||
org $028409
|
||||
jsl SuctionOverworldFix
|
||||
|
||||
org $0ded04 ; <- rando's hooks.asm line 2192 - 6ED04 - equipment.asm : 1963 (REP #$30)
|
||||
jsl DrHudDungeonItemsAdditions
|
||||
org $098638 ; rando's hooks.asm line 2192
|
||||
jsl CountChestKeys
|
||||
;org $098638 ; rando's hooks.asm line 2192
|
||||
;jsl CountChestKeys
|
||||
org $06D192 ; rando's hooks.asm line 457
|
||||
jsl CountAbsorbedKeys
|
||||
; rando's hooks.asm line 1020
|
||||
org $05FC7E ; <- 2FC7E - sprite_dash_item.asm : 118 (LDA $7EF36F : INC A : STA $7EF36F)
|
||||
jsl CountBonkItem
|
||||
;org $05FC7E ; <- 2FC7E - sprite_dash_item.asm : 118 (LDA $7EF36F : INC A : STA $7EF36F)
|
||||
;jsl CountBonkItem
|
||||
|
||||
org $019dbd ; <- Bank01.asm : 4465 of Object_Draw8xN (LDA $9B52, Y : STA $7E2000, X)
|
||||
jsl CutoffEntranceRug : bra .nextTile : nop
|
||||
.nextTile
|
||||
|
||||
;maybe set 02e2 to 0
|
||||
|
||||
org $0799de ; <- Bank07.asm : 4088 (LDA.b #$15 : STA $5D)
|
||||
JSL StoreTempBunnyState
|
||||
;
|
||||
org $08c450 ; <- ancilla_receive_item.asm : 146-148 (STY $5D : STZ $02D8)
|
||||
JSL RetrieveBunnyState : NOP
|
||||
|
||||
org $02d9ce ; <- Bank02.asm : Dungeon_LoadEntrance 10829 (STA $A0 : STA $048E)
|
||||
JSL CheckDarkWorldSanc : NOP
|
||||
|
||||
org $01891e ; <- Bank 01.asm : 991 Dungeon_LoadType2Object (LDA $00 : XBA : AND.w #$00FF)
|
||||
JSL RainPrevention : NOP #2
|
||||
|
||||
; These two, if enabled together, have implications for vanilla BK doors in IP/Hera/Mire
|
||||
; IPBJ is common enough to consider not doing this. Mire is not a concern for vanilla - maybe glitched modes
|
||||
|
||||
+28
@@ -33,6 +33,23 @@ GfxFixer:
|
||||
rtl
|
||||
}
|
||||
|
||||
FixAnimatedTiles:
|
||||
LDA.L DRMode : CMP #$02 : BNE +
|
||||
LDA $040C : CMP.b #$FF : BEQ +
|
||||
PHX
|
||||
LDX $A0 : LDA.l TilesetTable, x
|
||||
CMP $0AA1 : beq ++
|
||||
TAX : PLA : BRA +
|
||||
++
|
||||
PLX
|
||||
+ LDA $02802E, X ; what we wrote over
|
||||
RTL
|
||||
|
||||
FixWallmasterLamp:
|
||||
ORA $0458
|
||||
STY $1C : STA $1D : RTL ; what we wrote over
|
||||
|
||||
|
||||
CgramAuxToMain: ; ripped this from bank02 because it ended with rts
|
||||
{
|
||||
rep #$20
|
||||
@@ -55,3 +72,14 @@ CgramAuxToMain: ; ripped this from bank02 because it ended with rts
|
||||
inc $15
|
||||
rts
|
||||
}
|
||||
|
||||
OverridePaletteHeader:
|
||||
lda.l DRMode : cmp #$02 : bne +
|
||||
lda.l DRFlags : and #$20 : bne +
|
||||
cpx #$01c2 : !bge +
|
||||
rep #$20
|
||||
txa : lsr : tax
|
||||
lda.l PaletteTable, x
|
||||
iny : rtl
|
||||
+ rep #$20 : iny : lda [$0D], Y ; what we wrote over
|
||||
rtl
|
||||
+117
-81
@@ -13,15 +13,11 @@ HudAdditions:
|
||||
LDX.b $05 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+10 ; draw 100's digit
|
||||
LDX.b $06 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+12 ; draw 10's digit
|
||||
LDX.b $07 : TXA : ORA.w #$2400 : STA !GOAL_DRAW_ADDRESS+14 ; draw 1's digit
|
||||
|
||||
lda $7ef29b : and #$0020 : beq +
|
||||
lda #$207f : bra .drawthing
|
||||
+ lda #$345e
|
||||
.drawthing STA !GOAL_DRAW_ADDRESS+16 ; castle gate indicator
|
||||
++
|
||||
|
||||
ldx $040c : cpx #$ff : bne + : rts : +
|
||||
lda.l DRMode : bne + : rts : +
|
||||
LDX $1B : BNE + : RTS : + ; Skip if outdoors
|
||||
ldx $040c : cpx #$ff : bne + : rts : + ; Skip if not in dungeon
|
||||
lda.l DRMode : bne + : rts : + ; Skip if not door rando
|
||||
phb : phk : plb
|
||||
lda $7ef364 : and.l $0098c0, x : beq +
|
||||
lda.w CompassBossIndicator, x : and #$00ff : cmp $a0 : bne +
|
||||
@@ -35,35 +31,32 @@ HudAdditions:
|
||||
.reminder sta $7ec702
|
||||
+ lda.w DRFlags : and #$0004 : beq .restore
|
||||
lda $7ef368 : and.l $0098c0, x : beq .restore
|
||||
|
||||
; lda #$2811 : sta $7ec740
|
||||
; lda $7ef366 : and.l $0098c0, x : bne .check
|
||||
; lda.w BigKeyStatus, x : bne + ; change this, if bk status changes to one byte
|
||||
; lda #$2574 : bra ++
|
||||
; + cmp #$0002 : bne +
|
||||
; lda #$2420 : bra ++
|
||||
; + lda #$207f : bra ++
|
||||
; .check lda #$2826
|
||||
; ++ sta $7ec742
|
||||
txa : lsr : tax
|
||||
|
||||
lda.l GenericKeys : and #$00ff : bne +
|
||||
lda $7ef4e0, x : jsr ConvertToDisplay : sta $7ec7a2
|
||||
lda #$2830 : sta $7ec7a4
|
||||
+
|
||||
lda.w ChestKeys, x : jsr ConvertToDisplay : sta $7ec7a6
|
||||
|
||||
; lda #$2871 : sta $7ec780
|
||||
; lda.w TotalKeys, x
|
||||
; sep #$20 : !sub $7ef4b0, x : rep #$20 ; todo 4b0 no longer in use
|
||||
; jsr ConvertToDisplay : sta $7ec782
|
||||
; todo 4b0 no longer in use
|
||||
|
||||
.restore
|
||||
plb : rts
|
||||
}
|
||||
|
||||
;column distance for BK/Smalls
|
||||
HudOffsets:
|
||||
; none hc east desert aga swamp pod mire skull ice hera tt tr gt
|
||||
dw $fffe, $0000, $0006, $0008, $0002, $0010, $000e, $0018, $0012, $0016, $000a, $0014, $001a, $001e
|
||||
|
||||
; offset from 1644
|
||||
RowOffsets:
|
||||
dw $0000, $0000, $0040, $0080, $0000, $0080, $0040, $0080, $00c0, $0040, $00c0, $0000, $00c0, $0000
|
||||
|
||||
ColumnOffsets:
|
||||
dw $0000, $0000, $0000, $0000, $000a, $000a, $000a, $0014, $000a, $0014, $0000, $0014, $0014, $001e
|
||||
|
||||
|
||||
DrHudDungeonItemsAdditions:
|
||||
{
|
||||
jsl DrawHUDDungeonItems
|
||||
@@ -73,34 +66,90 @@ DrHudDungeonItemsAdditions:
|
||||
phx : phy : php
|
||||
rep #$30
|
||||
|
||||
lda !HUD_FLAG : and.w #$0020 : beq + : bra ++ : +
|
||||
lda HUDDungeonItems : and.w #$0003 : bne + : bra ++ : +
|
||||
lda.w #$2810 : sta $1684 ; small keys icon
|
||||
lda.w #$2811 : sta $16c4 ; big key icon
|
||||
lda.w #$2810 : sta $1704 ; small keys icon
|
||||
lda.w #$24f5 : sta $1606 : sta $1610 : sta $161a : sta $1624
|
||||
sta $1644 : sta $164a : sta $1652 : sta $1662 : sta $1684 : sta $16c4
|
||||
ldx #$0000
|
||||
- sta $1704, x : sta $170e, x : sta $1718, x
|
||||
inx #2 : cpx #$0008 : !blt -
|
||||
|
||||
lda !HUD_FLAG : and.w #$0020 : beq + : brl ++ : +
|
||||
lda HUDDungeonItems : and.w #$0007 : bne + : brl ++ : +
|
||||
; bk symbols
|
||||
lda.w #$2811 : sta $1606 : sta $1610 : sta $161a : sta $1624
|
||||
; sm symbols
|
||||
lda.w #$2810 : sta $160a : sta $1614 : sta $161e : sta $16e4
|
||||
; blank out stuff
|
||||
lda.w #$24f5 : sta $1724
|
||||
|
||||
ldx #$0002
|
||||
- lda $7ef364 : and.l $0098c0, x : beq + ; must have compass
|
||||
lda.l HudOffsets, x : tay
|
||||
jsr BkStatus : sta $16C6, y ; big key status
|
||||
phx
|
||||
- lda #$0000 : !addl RowOffsets,x : !addl ColumnOffsets, x : tay
|
||||
lda.l DungeonReminderTable, x : sta $1644, y : iny #2
|
||||
lda.w #$24f5 : sta $1644, y
|
||||
lda $7ef368 : and.l $0098c0, x : beq + ; must have map
|
||||
jsr BkStatus : sta $1644, y : bra .smallKey ; big key status
|
||||
+ lda $7ef366 : and.l $0098c0, x : beq .smallKey
|
||||
lda.w #$2826 : sta $1644, y
|
||||
.smallKey
|
||||
+ iny #2
|
||||
cpx #$001a : bne +
|
||||
tya : !add #$003c : tay
|
||||
+ stx $00
|
||||
txa : lsr : tax
|
||||
lda.l ChestKeys, x : jsr ConvertToDisplay2 : sta $1706, y ; small key totals
|
||||
plx
|
||||
+ inx #2 : cpx #$001b : bcc -
|
||||
lda.w #$24f5 : sta $1644, y
|
||||
lda.l GenericKeys : and #$00FF : bne +
|
||||
lda.l $7ef37c, x : and #$00FF : beq +
|
||||
jsr ConvertToDisplay2 : sta $1644, y
|
||||
+ iny #2 : lda.w #$24f5 : sta $1644, y
|
||||
phx : ldx $00
|
||||
lda $7ef368 : and.l $0098c0, x : beq + ; must have map
|
||||
plx : sep #$30 : lda.l ChestKeys, x : sta $02
|
||||
lda.l GenericKeys : bne +++
|
||||
lda $02 : !sub $7ef4e0, x : sta $02
|
||||
+++ lda $02
|
||||
rep #$30
|
||||
jsr ConvertToDisplay2 : sta $1644, y ; small key totals
|
||||
bra .skipStack
|
||||
+ plx
|
||||
.skipStack iny #2
|
||||
cpx #$000d : beq +
|
||||
lda.w #$24f5 : sta $1644, y
|
||||
+
|
||||
ldx $00
|
||||
+ inx #2 : cpx #$001b : bcs ++ : brl -
|
||||
++
|
||||
lda !HUD_FLAG : and.w #$0020 : bne + : bra ++ : +
|
||||
lda HUDDungeonItems : and.w #$000c : bne + : bra ++ : +
|
||||
lda.w #$24f5 : sta $1704 ; blank
|
||||
lda !HUD_FLAG : and.w #$0020 : bne + : brl ++ : +
|
||||
lda HUDDungeonItems : and.w #$000c : bne + : brl ++ : +
|
||||
; map symbols (do I want these) ; note compass symbol is 2c20
|
||||
lda.w #$2821 : sta $1606 : sta $1610 : sta $161a : sta $1624
|
||||
; blank out a couple thing from old hud
|
||||
lda.w #$24f5 : sta $16e4 : sta $1724
|
||||
sta $160a : sta $1614 : sta $161e ; blank out sm key indicators
|
||||
ldx #$0002
|
||||
- lda $7ef364 : and.l $0098c0, x : beq + ; must have compass
|
||||
lda.l HudOffsets, x : tay
|
||||
- lda #$0000 ; start of hud area
|
||||
!addl RowOffsets, x : !addl ColumnOffsets, x : tay
|
||||
lda.l DungeonReminderTable, x : sta $1644, y
|
||||
iny #2
|
||||
lda.w #$24f5 : sta $1644, y ; blank out map spot
|
||||
lda $7ef368 : and.l $0098c0, x : beq + ; must have map
|
||||
lda #$2826 : sta $1644, y ; check mark
|
||||
+ iny #2
|
||||
cpx #$001a : bne +
|
||||
tya : !add #$003c : tay
|
||||
+ lda $7ef364 : and.l $0098c0, x : beq + ; must have compass
|
||||
phx ; total chest counts
|
||||
txa : lsr : tax
|
||||
lda.l TotalLocationsLow, x : jsr ConvertToDisplay2 : sta $1706, y
|
||||
lda.l TotalLocationsHigh, x : jsr ConvertToDisplay2 : sta $16c6, y
|
||||
sep #$30
|
||||
lda.l TotalLocations, x : !sub $7EF4BF, x : JSR HudHexToDec2DigitCopy
|
||||
rep #$30
|
||||
lda $06 : jsr ConvertToDisplay2 : sta $1644, y : iny #2
|
||||
lda $07 : jsr ConvertToDisplay2 : sta $1644, y
|
||||
plx
|
||||
+
|
||||
+ inx #2 : cpx #$001b : bcc -
|
||||
bra .skipBlanks
|
||||
+ lda.w #$24f5 : sta $1644, y : iny #2 : sta $1644, y
|
||||
.skipBlanks iny #2
|
||||
cpx #$001a : beq +
|
||||
lda.w #$24f5 : sta $1644, y ; blank out spot
|
||||
+ inx #2 : cpx #$001b : !bge ++ : brl -
|
||||
++
|
||||
plp : ply : plx : rtl
|
||||
}
|
||||
@@ -108,7 +157,7 @@ DrHudDungeonItemsAdditions:
|
||||
BkStatus:
|
||||
lda $7ef366 : and.l $0098c0, x : bne +++ ; has the bk already
|
||||
lda.l BigKeyStatus, x : bne ++
|
||||
lda #$2482 : rts ; 0/O for no BK
|
||||
lda #$2827 : rts ; 0/O for no BK
|
||||
++ cmp #$0002 : bne +
|
||||
lda #$2420 : rts ; symbol for BnC
|
||||
+ lda #$24f5 : rts ; black otherwise
|
||||
@@ -124,29 +173,7 @@ ConvertToDisplay2:
|
||||
cmp #$000a : !blt +
|
||||
!add #$2553 : rts
|
||||
+ !add #$2816 : rts
|
||||
++ lda #$2483 : rts ; 0/O for 0 or placeholder digit
|
||||
|
||||
CountChestKeys:
|
||||
jsl ItemDowngradeFix
|
||||
jsr CountChest
|
||||
rtl
|
||||
|
||||
CountChest:
|
||||
lda !MULTIWORLD_ITEM_PLAYER_ID : bne .end
|
||||
cpy #$24 : beq +
|
||||
cpy #$a0 : !blt .end
|
||||
cpy #$ae : !bge .end
|
||||
pha : phx
|
||||
tya : and #$0f : bne ++
|
||||
inc a
|
||||
++ tax : bra .count
|
||||
+ pha : phx
|
||||
lda $040c : lsr : tax
|
||||
.count
|
||||
lda $7ef4b0, x : inc : sta $7ef4b0, x
|
||||
lda $7ef4e0, x : inc : sta $7ef4e0, x
|
||||
.restore plx : pla
|
||||
.end rts
|
||||
++ lda #$2827 : rts ; 0/O for 0 or placeholder digit ;2483
|
||||
|
||||
CountAbsorbedKeys:
|
||||
jsl IncrementSmallKeysNoPrimary : phx
|
||||
@@ -155,20 +182,6 @@ CountAbsorbedKeys:
|
||||
lda $7ef4b0, x : inc : sta $7ef4b0, x
|
||||
+ plx : rtl
|
||||
|
||||
CountBonkItem:
|
||||
jsl GiveBonkItem
|
||||
lda $a0 ; check room ID - only bonk keys in 2 rooms so we're just checking the lower byte
|
||||
cmp #115 : bne + ; Desert Bonk Key
|
||||
lda.l BonkKey_Desert
|
||||
bra ++
|
||||
+ : cmp #140 : bne + ; GTower Bonk Key
|
||||
lda.l BonkKey_GTower
|
||||
bra ++
|
||||
+ lda.b #$24 ; default to small key
|
||||
++ cmp #$24 : bne +
|
||||
phy : tay : jsr CountChest : ply
|
||||
+ rtl
|
||||
|
||||
;================================================================================
|
||||
; 16-bit A, 8-bit X
|
||||
; in: A(b) - Byte to Convert
|
||||
@@ -201,3 +214,26 @@ HudHexToDec4DigitCopy:
|
||||
+
|
||||
STY $07 ; Store 1s digit
|
||||
RTS
|
||||
|
||||
;================================================================================
|
||||
; 8-bit registers
|
||||
; in: A(b) - Byte to Convert
|
||||
; out: $06 - $07 (high - low)
|
||||
;================================================================================
|
||||
HudHexToDec2DigitCopy: ; modified
|
||||
PHY
|
||||
LDY.b #$00
|
||||
-
|
||||
CMP.b #10 : !BLT +
|
||||
INY
|
||||
SBC.b #10 : BRA -
|
||||
+
|
||||
STY $06 : LDY #$00 ; Store 10s digit and reset Y
|
||||
CMP.b #1 : !BLT +
|
||||
-
|
||||
INY
|
||||
DEC : BNE -
|
||||
+
|
||||
STY $07 ; Store 1s digit
|
||||
PLY
|
||||
RTS
|
||||
@@ -0,0 +1,181 @@
|
||||
org $06926e ; <- 3126e - sprite_prep.asm : 2664 (LDA $0B9B : STA $0CBA, X)
|
||||
jsl SpriteKeyPrep : nop #2
|
||||
|
||||
org $06d049 ; <- 35049 sprite_absorbable : 31-32 (JSL Sprite_DrawRippleIfInWater : JSR Sprite_DrawAbsorbable)
|
||||
jsl SpriteKeyDrawGFX : bra + : nop : +
|
||||
|
||||
org $06d180
|
||||
jsl BigKeyGet : bcs $07 : nop #5
|
||||
|
||||
org $06d18d ; <- 3518D - sprite_absorbable.asm : 274 (LDA $7EF36F : INC A : STA $7EF36F)
|
||||
jsl KeyGet
|
||||
|
||||
org $06f9f3 ; bank06.asm : 6732 (JSL Sprite_LoadProperties)
|
||||
jsl LoadProperties_PreserveItemMaybe
|
||||
|
||||
|
||||
|
||||
|
||||
org $06d23a
|
||||
Sprite_DrawAbsorbable:
|
||||
org $1eff81
|
||||
Sprite_DrawRippleIfInWater:
|
||||
org $0db818
|
||||
Sprite_LoadProperties:
|
||||
|
||||
org $288000 ;140000
|
||||
ShuffleKeyDrops:
|
||||
db 0
|
||||
ShuffleKeyDropsReserved:
|
||||
db 0
|
||||
|
||||
LootTable: ;PC: 140002
|
||||
db $0e, $00, $24 ;; ice jelly key
|
||||
db $13, $00, $24 ;; pokey 2
|
||||
db $16, $00, $24 ;; swamp waterway pot
|
||||
db $21, $00, $24 ;; key rat
|
||||
db $35, $00, $24 ;; swamp trench 2 pot
|
||||
db $36, $00, $24 ;; hookshot pot
|
||||
db $37, $00, $24 ;; trench 1 pot
|
||||
db $38, $00, $24 ;; pot row pot
|
||||
db $39, $00, $24 ;; skull gibdo
|
||||
db $3d, $00, $24 ;; gt minihelma
|
||||
db $3e, $00, $24 ;; ice conveyor
|
||||
db $3f, $00, $24 ;; ice hammer block ??? is this a dungeon secret?
|
||||
db $43, $00, $24 ;; tiles 2 pot
|
||||
db $53, $00, $24 ;; beamos hall pot
|
||||
db $56, $00, $24 ;; skull west lobby pot
|
||||
db $63, $00, $24 ;; desert tiles 1 pot
|
||||
db $71, $00, $24 ;; boomerang guard
|
||||
db $72, $00, $24 ;; hc map guard
|
||||
db $7b, $00, $24 ;; gt star pits pot
|
||||
db $80, $00, $32 ;; a big key (for the current dungeon)
|
||||
db $8b, $00, $24 ;; gt conv cross block
|
||||
db $9b, $00, $24 ;; gt dlb switch pot
|
||||
db $9f, $00, $24 ;; ice many pots
|
||||
db $99, $00, $24 ;; eastern eyegore
|
||||
db $a1, $00, $24 ;; mire fishbone pot
|
||||
db $ab, $00, $24 ;; tt spike switch pot
|
||||
db $b0, $00, $24 ;; tower circle of pots usain
|
||||
db $b3, $00, $24 ;; mire spikes pot
|
||||
db $b6, $00, $24 ;; pokey 1
|
||||
db $ba, $00, $24 ;; eastern dark pot
|
||||
db $bc, $00, $24 ;; tt hallway pot
|
||||
db $c0, $00, $24 ;; tower dark archer
|
||||
db $c1, $00, $24 ;; mire glitchy jelly
|
||||
db $ff, $00, $ff
|
||||
;140068
|
||||
|
||||
KeyTable:
|
||||
db $a0, $a0, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $aa, $ab, $ac, $ad
|
||||
|
||||
SpriteKeyPrep:
|
||||
{
|
||||
lda $0b9b : sta $0cba, x ; what we wrote over
|
||||
pha
|
||||
lda.l ShuffleKeyDrops : beq +
|
||||
phx
|
||||
ldx #$fd
|
||||
- inx #3 : lda.l LootTable, x : cmp #$ff : beq ++ : cmp $a0 : bne -
|
||||
inx : lda.l LootTable, x : sta !MULTIWORLD_SPRITEITEM_PLAYER_ID
|
||||
inx : lda.l LootTable, x
|
||||
plx : sta $0e80, x
|
||||
cmp #$24 : bne +++
|
||||
lda $a0 : cmp #$80 : bne + : lda #$24
|
||||
+++ jsl PrepDynamicTile : bra +
|
||||
++ plx : lda #$24 : sta $0e80, x
|
||||
+ pla
|
||||
rtl
|
||||
}
|
||||
|
||||
SpriteKeyDrawGFX:
|
||||
{
|
||||
jsl Sprite_DrawRippleIfInWater
|
||||
pha
|
||||
lda.l ShuffleKeyDrops : bne +
|
||||
- pla
|
||||
phk : pea.w .jslrtsreturn-1
|
||||
pea.w $068014 ; an rtl address - 1 in Bank06
|
||||
jml Sprite_DrawAbsorbable
|
||||
.jslrtsreturn
|
||||
rtl
|
||||
+ lda $0e80, x
|
||||
cmp #$24 : bne +
|
||||
lda $a0 : cmp #$80 : bne - : lda #$24
|
||||
+ jsl DrawDynamicTile ; see DrawHeartPieceGFX if problems
|
||||
cmp #$03 : bne +
|
||||
pha : lda $0e60, x : ora.b #$20 : sta $0E60, x : pla
|
||||
+
|
||||
jsl.l Sprite_DrawShadowLong
|
||||
|
||||
pla : rtl
|
||||
}
|
||||
|
||||
KeyGet:
|
||||
{
|
||||
lda $7ef36f ; what we wrote over
|
||||
pha
|
||||
lda.l ShuffleKeyDrops : bne +
|
||||
- pla : rtl
|
||||
+ ldy $0e80, x
|
||||
lda $a0 : cmp #$87 : bne +
|
||||
jsr ShouldKeyBeCountedForDungeon : bcc -
|
||||
jsl CountChestKeyLong : bra -
|
||||
+ sty $00
|
||||
jsr KeyGetPlayer : sta !MULTIWORLD_ITEM_PLAYER_ID
|
||||
lda !MULTIWORLD_ITEM_PLAYER_ID : bne .receive
|
||||
phx
|
||||
lda $040c : lsr : tax
|
||||
lda $00 : CMP.l KeyTable, x : bne +
|
||||
- JSL.l FullInventoryExternal : jsl CountChestKeyLong : plx : pla : rtl
|
||||
+ cmp #$af : beq - ; universal key
|
||||
cmp #$24 : beq - ; small key for this dungeon
|
||||
plx
|
||||
.receive
|
||||
jsl.l $0791b3 ; Player_HaltDashAttackLong
|
||||
jsl.l Link_ReceiveItem
|
||||
pla : dec : rtl
|
||||
}
|
||||
|
||||
; Input Y - the item type
|
||||
ShouldKeyBeCountedForDungeon:
|
||||
{
|
||||
phx
|
||||
lda $040c : lsr : tax
|
||||
tya : cmp KeyTable, x : bne +
|
||||
- plx : sec : rts
|
||||
+ cmp #$24 : beq -
|
||||
plx : clc : rts
|
||||
}
|
||||
|
||||
BigKeyGet:
|
||||
{
|
||||
lda.l ShuffleKeyDrops : bne +
|
||||
- stz $02e9 : ldy.b #$32 ; what we wrote over
|
||||
phx : jsl Link_ReceiveItem : plx ; what we wrote over
|
||||
clc : rtl
|
||||
+
|
||||
ldy $0e80, x
|
||||
cpy #$32 : beq -
|
||||
+ sec : rtl
|
||||
}
|
||||
|
||||
KeyGetPlayer:
|
||||
{
|
||||
phx
|
||||
ldx #$fd
|
||||
- inx #3 : lda.l LootTable, x : cmp #$ff : beq ++ : cmp $a0 : bne -
|
||||
++ inx : lda.l LootTable, x
|
||||
plx
|
||||
rts
|
||||
}
|
||||
|
||||
LoadProperties_PreserveItemMaybe:
|
||||
{
|
||||
lda.l ShuffleKeyDrops : bne +
|
||||
jsl Sprite_LoadProperties : rtl
|
||||
+ lda $0e80, x : pha
|
||||
jsl Sprite_LoadProperties
|
||||
pla : sta $0e80, x
|
||||
rtl
|
||||
}
|
||||
+6
-3
@@ -190,8 +190,9 @@ PrepScrollToEdge:
|
||||
+ sta $05
|
||||
lda $01 : and #$10 : beq +
|
||||
lda #01
|
||||
+ sta $ee
|
||||
rts
|
||||
+ sta $ee : bne +
|
||||
stz $0476
|
||||
+ rts
|
||||
}
|
||||
|
||||
; Normal Flags should be in $01
|
||||
@@ -199,7 +200,9 @@ PrepScrollToEdge:
|
||||
PrepScrollToNormal:
|
||||
{
|
||||
lda $01 : sta $fe : and #$04 : lsr #2 : sta $ee ; trap door and layer
|
||||
stz $05 : lda #$78 : sta $04
|
||||
bne +
|
||||
stz $0476
|
||||
+ stz $05 : lda #$78 : sta $04
|
||||
lda $01 : and #$03 : beq .end
|
||||
cmp #$02 : !bge +
|
||||
lda #$f8 : sta $04 : bra .end
|
||||
|
||||
+65
-5
@@ -11,7 +11,7 @@ LampCheckOverride:
|
||||
|
||||
LDA $7EF3CA : BNE +
|
||||
.lightWorld
|
||||
LDA $040C : CMP.b #$02 : BNE ++ ; check if we're in HC
|
||||
LDA $040C : CMP.b #$04 : !BGE ++ ; check if we're in HC
|
||||
LDA LampConeSewers : BRA .done
|
||||
++
|
||||
LDA LampConeLightWorld : BRA .done
|
||||
@@ -49,10 +49,6 @@ MirrorCheckOverride:
|
||||
rtl
|
||||
+ lda.l DRScroll : rtl
|
||||
|
||||
MirrorCheckOverride2:
|
||||
lda $7ef353 : and #$02 : rtl
|
||||
|
||||
|
||||
BlockEraseFix:
|
||||
lda $7ef353 : and #$02 : beq +
|
||||
stz $05fc : stz $05fd
|
||||
@@ -80,3 +76,67 @@ BlindAtticFix:
|
||||
lda #$01 : rtl
|
||||
+ lda $7EF3CC : cmp.b #$06
|
||||
rtl
|
||||
|
||||
SuctionOverworldFix:
|
||||
stz $50 : stz $5e
|
||||
lda.l DRMode : beq +
|
||||
stz $49
|
||||
+ rtl
|
||||
|
||||
; TT Alcove, Mire bridges, pod falling, SW torch room, TR Pipe room, Bob's Room, Ice Many Pots, Mire Hub
|
||||
; swamp waterfall, Gauntlet 3, Eastern Push block
|
||||
CutoffRooms:
|
||||
db $bc, $a2, $1a, $49, $14, $8c, $9f, $c2
|
||||
db $66, $5d, $a8
|
||||
; Don't forget CutoffRoomCount!!!
|
||||
|
||||
CutoffEntranceRug:
|
||||
pha : phx
|
||||
lda.l DRMode : beq .norm
|
||||
lda $04 : cmp #$000A : beq +
|
||||
cmp #$000C : bne .norm
|
||||
+ lda $a0 : sep #$20 : ldx #$0000
|
||||
- cmp.l CutoffRooms, x : beq .check
|
||||
inx : cpx #$000B : !blt - ; CutoffRoomCount is here!
|
||||
rep #$20
|
||||
.norm plx : pla : lda $9B52, y : sta $7E2000, x ; what we wrote over
|
||||
rtl
|
||||
.check
|
||||
rep #$20
|
||||
lda $0c : cmp #$0006 : !bge .skip
|
||||
lda $0e : cmp #$0008 : !bge .skip
|
||||
cmp #$0004 : !blt .skip
|
||||
bra .norm
|
||||
.skip plx : pla : rtl
|
||||
|
||||
|
||||
StoreTempBunnyState:
|
||||
LDA $5D : CMP #$1C : BNE +
|
||||
STA $5F
|
||||
+ LDA #$15 : STA $5D ; what we wrote over
|
||||
RTL
|
||||
|
||||
RetrieveBunnyState:
|
||||
STY $5D : STZ $02D8 ; what we wrote over
|
||||
LDA $5F : BEQ +
|
||||
STA $5D
|
||||
+ RTL
|
||||
|
||||
RainPrevention:
|
||||
LDA $00 : XBA : AND #$00FF ; what we wrote over
|
||||
PHA
|
||||
LDA $7EF3C5 : AND #$00FF : CMP #$0002 : !BGE .done ; only in rain states (0 or 1)
|
||||
LDA.l $7EF3C6 : AND #$0004 : BNE .done ; zelda's been rescued
|
||||
LDA.l BlockSanctuaryDoorInRain : BEQ .done ;flagged
|
||||
LDA $A0 : CMP #$0012 : BNE + ;we're in the sanctuary
|
||||
LDA.l $7EF3CC : AND #$00FF : CMP #$0001 : BEQ .done ; zelda is following
|
||||
LDA $00 : CMP #$02A1 : BNE .done
|
||||
PLA : LDA #$0008 : RTL
|
||||
+ LDA.l BlockCastleDoorsInRain : BEQ .done ;flagged
|
||||
LDX #$FFFE
|
||||
- INX #2 : LDA.l RemoveRainDoorsRoom, X : CMP #$FFFF : BEQ .done
|
||||
CMP $A0 : BNE -
|
||||
LDA.l RainDoorMatch, X : CMP $00 : BNE -
|
||||
PLA : LDA #$0008 : RTL
|
||||
.done PLA : RTL
|
||||
|
||||
|
||||
+4
-4
@@ -177,11 +177,11 @@ ScrollX: ;change the X offset variables
|
||||
rts
|
||||
|
||||
LimitXCamera:
|
||||
cmp #$0080 : !bge +
|
||||
cmp #$0079 : !bge +
|
||||
lda #$0000 : bra .end
|
||||
+ cmp #$0181 : !blt +
|
||||
lda #$0180
|
||||
+ !sub #$0080
|
||||
+ cmp #$0178 : !blt +
|
||||
lda #$0178
|
||||
+ !sub #$0078
|
||||
.end rts
|
||||
|
||||
CheckRoomLayoutX:
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user