Key Door Shuffle algorithm added

This commit is contained in:
aerinon
2019-10-03 16:27:44 -06:00
parent 5cc4fdfa1f
commit 92ad60bedf
5 changed files with 375 additions and 84 deletions

View File

@@ -84,8 +84,10 @@ class World(object):
self.spoiler = Spoiler(self)
self.lamps_needed_for_dark_rooms = 1
self.doors = []
self._door_cache = {}
self.paired_doors = {}
self.rooms = []
self._room_cache = {}
def intialize_regions(self):
for region in self.regions:
@@ -141,29 +143,38 @@ class World(object):
def get_door(self, doorname, player):
if isinstance(doorname, Door):
return doorname
for door in self.doors:
if door.name == doorname and door.player == player:
return door
raise RuntimeError('No such door %s' % doorname)
try:
return self._door_cache[(doorname, player)]
except KeyError:
for door in self.doors:
if door.name == doorname and door.player == player:
self._door_cache[(doorname, player)] = door
return door
raise RuntimeError('No such door %s for player %d' % (doorname, player))
def check_for_door(self, doorname, player):
if isinstance(doorname, Door):
return doorname
for door in self.doors:
if door.name == doorname and door.player == player:
return door
return None
try:
return self._door_cache[(doorname, player)]
except KeyError:
for door in self.doors:
if door.name == doorname and door.player == player:
self._door_cache[(doorname, player)] = door
return door
return None
def get_room(self, room_idx, player):
if isinstance(room_idx, Room):
return room_idx
for room in self.rooms:
if room.index == room_idx and room.player == player:
return room
raise RuntimeError('No such room %s' % room_idx)
try:
return self._room_cache[(room_idx, player)]
except KeyError:
for room in self.rooms:
if room.index == room_idx and room.player == player:
self._room_cache[(room_idx, player)] = room
return room
raise RuntimeError('No such room %s for player %d' % (room_idx, player))
def get_all_state(self, keys=False):
ret = CollectionState(self)

View File

@@ -1,7 +1,9 @@
import random
import collections
import logging
import operator as op
from functools import reduce
from BaseClasses import RegionType, DoorType, Direction, Sector, pol_idx
from Dungeons import hyrule_castle_regions, eastern_regions, desert_regions, hera_regions, tower_regions, pod_regions
from Dungeons import dungeon_regions
@@ -91,6 +93,14 @@ def create_door_spoiler(world, player):
logger.debug('Door not found in queue: %s connected to %s', door_b.name, door_a.name)
else:
logger.warning('Door not connected: %s', door_a.name)
for room in world.rooms:
if room.modified:
logger.debug('Room %s changed (p%d)', room.index, player)
for dp in world.paired_doors[player]:
if dp.pair:
logger.debug('Paired Doors: %s with %s (p%d)', dp.door_a, dp.door_b, player)
else:
logger.debug('Unpaired Doors: %s not paired with %s (p%d)', dp.door_a, dp.door_b, player)
# some useful functions
@@ -414,8 +424,28 @@ def experiment(world, player):
for idx, sector_list in enumerate(dp_split):
dungeon_sectors.append((sector_list, desert_default_entrance_sets[idx]))
dungeon_layouts = []
for sector_list, entrance_list in dungeon_sectors:
shuffle_dungeon_no_repeats(world, player, sector_list, entrance_list)
ds = shuffle_dungeon_no_repeats(world, player, sector_list, entrance_list)
dungeon_layouts.append((ds, entrance_list))
desert_combined = None
desert_entrances = []
queue = collections.deque(dungeon_layouts)
while len(queue) > 0:
sector, entrance_list = queue.pop()
if entrance_list in desert_default_entrance_sets:
dungeon_layouts.remove((sector, entrance_list))
desert_entrances.extend(entrance_list)
if desert_combined is None:
desert_combined = sector
else:
desert_combined.regions.extend(sector.regions)
dungeon_layouts.append((desert_combined, desert_entrances))
# shuffle_key_doors for dungeons
for layout in dungeon_layouts:
shuffle_key_doors(layout[0], layout[1], world, player)
def convert_regions(region_names, world, player):
@@ -642,7 +672,7 @@ def shuffle_dungeon_no_repeats(world, player, available_sectors, entrance_region
connect_door = compatibles.pop()
logger.info(' Adding loop via %s', connect_door.name)
# Check if valid
if is_loop_valid(door, connect_door, sector, len(available_sectors) == 1):
if is_loop_valid(door, connect_door, sector, available_sectors):
maybe_connect_two_way(world, door, connect_door, player)
reachable_doors.remove(door)
reachable_doors.remove(connect_door)
@@ -739,8 +769,10 @@ def is_valid(door_a, door_b, sector_a, sector_b, available_sectors):
return False # not sure how we got here, but it's a bad idea
def is_loop_valid(door_a, door_b, sector, no_more_sectors):
if no_more_sectors:
def is_loop_valid(door_a, door_b, sector, available_sectors):
if len(available_sectors) == 1:
return True
if len(available_sectors) == 2 and door_b not in sector.outstanding_doors:
return True
elif not door_a.blocked and not door_b.blocked:
return sector.outflow() - 1 > 0
@@ -775,6 +807,254 @@ def are_there_outstanding_doors_of_type(door_a, door_b, sector_a, sector_b, avai
return True
def shuffle_key_doors(dungeon_sector, entrances, world, player):
start_regions = convert_regions(entrances, world, player)
# count number of key doors
num_key_doors = 0
current_doors = []
skips = []
for region in dungeon_sector.regions:
for ext in region.exits:
d = world.check_for_door(ext.name, player)
if d is not None and d.smallKey:
current_doors.append(d)
if d not in skips:
if d.type == DoorType.Interior:
skips.append(d.dest)
if d.type == DoorType.Normal:
for dp in world.paired_doors[player]:
if d.name == dp.door_a:
skips.append(world.get_door(dp.door_b, player))
break
elif d.name == dp.door_b:
skips.append(world.get_door(dp.door_a, player))
break
num_key_doors += 1
# traverse dungeon and find candidates
candidates = []
checked_doors = set()
for region in start_regions:
possible, checked = find_key_door_candidates(region, checked_doors, world, player)
candidates.extend(possible)
checked_doors.update(checked)
flat_candidates = []
for candidate in candidates:
# not valid if: Normal and Pair in is Checked and Pair is not in Candidates
if candidate.type != DoorType.Normal or candidate.dest not in checked_doors or candidate.dest in candidates:
flat_candidates.append(candidate)
# find valid combination of candidates
paired_candidates = build_pair_list(flat_candidates)
if len(paired_candidates) < num_key_doors:
raise Exception('Not enough candidates')
random.shuffle(paired_candidates)
combinations = ncr(len(paired_candidates), num_key_doors)
itr = 0
proposal = kth_combination(itr, paired_candidates, num_key_doors)
while not validate_key_layout(start_regions, proposal, world, player):
itr += 1
proposal = kth_combination(itr, paired_candidates, num_key_doors)
if itr > combinations:
raise Exception('No valid key layouts!')
# make changes
reassign_key_doors(current_doors, proposal, world, player)
def build_pair_list(flat_list):
paired_list = []
queue = collections.deque(flat_list)
while len(queue) > 0:
d = queue.pop()
if d.dest in queue:
paired_list.append((d, d.dest))
queue.remove(d.dest)
else:
paired_list.append(d)
return paired_list
def flatten_pair_list(paired_list):
flat_list = []
for d in paired_list:
if type(d) is tuple:
flat_list.append(d[0])
flat_list.append(d[1])
else:
flat_list.append(d)
return flat_list
def find_key_door_candidates(region, checked, world, player):
candidates = []
checked_doors = list(checked)
queue = collections.deque([(region, None)])
while len(queue) > 0:
current, last_door = queue.pop()
for ext in current.exits:
d = world.check_for_door(ext.name, player)
if d is not None and not d.blocked and d.dest is not last_door and d not in checked_doors:
valid = False
if 0 <= d.doorListPos < 4 and d.type in [DoorType.Interior, DoorType.Normal, DoorType.SpiralStairs]:
room = world.get_room(d.roomIndex, player)
position, kind = room.doorList[d.doorListPos]
if d.type == DoorType.Interior:
valid = kind in [DoorKind.Normal, DoorKind.NormalLow, DoorKind.SmallKey, DoorKind.Bombable,
DoorKind.Dashable, DoorKind.NormalLow2]
elif d.type == DoorType.SpiralStairs:
valid = kind in [DoorKind.StairKey, DoorKind.StairKey2, DoorKind.StairKeyLow]
elif d.type == DoorType.Normal:
d2 = d.dest
if d2 not in candidates:
room_b = world.get_room(d2.roomIndex, player)
pos_b, kind_b = room_b.doorList[d2.doorListPos]
okay_normals = [DoorKind.Normal, DoorKind.NormalLow, DoorKind.SmallKey, DoorKind.Bombable,
DoorKind.Dashable, DoorKind.NormalLow2, DoorKind.Warp, DoorKind.DungeonChanger]
valid = kind in okay_normals and kind_b in okay_normals
else:
valid = True
if valid:
candidates.append(d)
queue.append((ext.connected_region, d))
if d is not None:
checked_doors.append(d)
return candidates, checked_doors
def kth_combination(k, l, r):
if r == 0:
return []
elif len(l) == r:
return l
else:
i = ncr(len(l)-1, r-1)
if k < i:
return l[0:1] + kth_combination(k, l[1:], r-1)
else:
return kth_combination(k-i, l[1:], r)
def ncr(n, r):
r = min(r, n-r)
numerator = reduce(op.mul, range(n, n-r, -1), 1)
denominator = reduce(op.mul, range(1, r+1), 1)
return numerator / denominator
def validate_key_layout(start_regions, key_door_proposal, world, player):
flat_proposal = flatten_pair_list(key_door_proposal)
available_doors = [] # Doors to explore
big_key_doors = []
small_key_doors = []
big_key_opened = False
visited_regions = set() # Regions we've been to and don't need to expand
ttl_locations = 0
used_locations = 0
# Everything in a start region is in key region 0.
for region in start_regions:
visited_regions.add(region)
ttl_locations += len(region.locations)
add_doors_to_lists(region, flat_proposal, available_doors, small_key_doors,
big_key_doors, big_key_opened, world, player)
while len(available_doors) > 0:
door = available_doors.pop()
connect_region = world.get_entrance(door.name, player).connected_region
if not door.blocked and connect_region not in visited_regions:
visited_regions.add(connect_region)
ttl_locations += len(connect_region.locations)
add_doors_to_lists(connect_region, flat_proposal, available_doors, small_key_doors,
big_key_doors, big_key_opened, world, player)
if len(available_doors) == 0:
num_smalls = 0
for small in small_key_doors:
if small.dest in small_key_doors:
num_smalls += 0.5 # half now, half with the dest
else:
num_smalls += 1
num_bigs = 1 if len(big_key_doors) > 0 else 0 # all or nothing
if num_smalls == 0 and num_bigs == 0:
return True # I think that's the end
available_locations = ttl_locations - used_locations
if available_locations >= num_smalls > 0: # todo: this not lenient at all - need a recursive function maybe
available_doors.extend(small_key_doors)
small_key_doors.clear()
used_locations += num_smalls
elif not big_key_opened and available_locations >= num_bigs > 0:
big_key_opened = True
used_locations += 1 # todo: this does not handle hc big key in crossed modes
available_doors.extend(big_key_doors)
big_key_doors.clear()
else:
return False
return len(small_key_doors) == 0 and len(big_key_doors) == 0
def add_doors_to_lists(region, key_door_proposal, available_doors, small_key_doors,
big_key_doors, big_key_opened, world, player):
for door in get_doors(world, region, player):
if not door.blocked:
if door in key_door_proposal and door not in small_key_doors:
small_key_doors.append(door)
elif door.bigKey and not big_key_opened and door not in big_key_doors:
big_key_doors.append(door)
elif door not in available_doors:
available_doors.append(door)
def reassign_key_doors(current_doors, proposal, world, player):
flat_proposal = flatten_pair_list(proposal)
queue = collections.deque(current_doors)
while len(queue) > 0:
d = queue.pop()
if d.type is DoorType.SpiralStairs and d not in proposal:
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
d.smallKey = False
elif d.type is DoorType.Interior and d not in flat_proposal and d.dest not in flat_proposal:
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
d.smallKey = False
d.dest.smallKey = False
queue.remove(d.dest)
elif d.type is DoorType.Normal and d not in flat_proposal:
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
d.smallKey = False
for dp in world.paired_doors[player]:
if dp.door_a == d.name or dp.door_b == d.name:
dp.pair = False
for obj in proposal:
if type(obj) is tuple:
d1 = obj[0]
d2 = obj[1]
if d1.type is DoorType.Interior:
change_door_to_small_key(d1, world, player)
else:
names = [d1.name, d2.name]
found = False
for dp in world.paired_doors[player]:
if dp.door_a in names and dp.door_b in names:
dp.pair = True
found = True
if not found:
world.paired_doors[player].append(PairedDoor(d1.name, d2.name))
change_door_to_small_key(d1, world, player)
change_door_to_small_key(d2, world, player)
else:
d = obj
if d.type is DoorType.Interior:
change_door_to_small_key(d, world, player)
elif d.type is DoorType.SpiralStairs:
pass # we don't have spiral stairs candidates yet that aren't already key doors
elif d.type is DoorType.Normal:
change_door_to_small_key(d, world, player)
def change_door_to_small_key(d, world, player):
d.smallKey = True
room = world.get_room(d.roomIndex, player)
if room.doorList[d.doorListPos][1] != DoorKind.SmallKey:
room.change(d.doorListPos, DoorKind.SmallKey)
# DATA GOES DOWN HERE
@@ -894,7 +1174,7 @@ interior_doors = [
('Tower Lone Statue WN', 'Tower Dark Maze EN'),
('Tower Dark Maze ES', 'Tower Dark Chargers WS'),
('Tower Dual Statues WS', 'Tower Dark Pits ES'),
('Tower Dark Pits EN', 'Tower Dark Archers WS'),
('Tower Dark Pits EN', 'Tower Dark Archers WN'),
('Tower Red Spears WN', 'Tower Red Guards EN'),
('Tower Red Guards SW', 'Tower Circle of Pots NW'),
('Tower Circle of Pots WS', 'Tower Pacifist Run ES'),
@@ -1027,7 +1307,7 @@ desert_default_entrance_sets = [
# 'Skull': ['Skull 1 Lobby', 'Skull 2 Mummy Lobby', 'Skull 2 Key Lobby', 'Skull 3 Lobby'],
entrance_sets = [
['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Secret Room'],
['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Secret Room', 'Sanctuary'],
['Eastern Lobby'],
['Hera Lobby'],
['Tower Lobby'],

122
Doors.py
View File

@@ -50,8 +50,8 @@ def create_doors(world, player):
# hyrule dungeon level
create_spiral_stairs(player, 'Hyrule Dungeon Map Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x72, 0, LTH, A, 0x4b, 0xec),
small_key(create_dir_door(player, 'Hyrule Dungeon Map Room Key Door S', DoorType.Interior, Direction.South, 0x72, Mid, High)),
small_key(create_dir_door(player, 'Hyrule Dungeon North Abyss Key Door N', DoorType.Interior, Direction.North, 0x72, Mid, High)),
small_key(create_dir_door(player, 'Hyrule Dungeon Map Room Key Door S', DoorType.Interior, Direction.South, 0x72, Mid, High)).pos(0),
small_key(create_dir_door(player, 'Hyrule Dungeon North Abyss Key Door N', DoorType.Interior, Direction.North, 0x72, Mid, High)).pos(0),
create_dir_door(player, 'Hyrule Dungeon North Abyss South Edge', DoorType.Open, Direction.South, 0x72, None, Low),
create_dir_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', DoorType.Open, Direction.South, 0x72, None, High),
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Dropdown', DoorType.Logical),
@@ -63,8 +63,8 @@ def create_doors(world, player):
create_dir_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', DoorType.Open, Direction.West, 0x81, None, High),
create_dir_door(player, 'Hyrule Dungeon Guardroom N', DoorType.Normal, Direction.North, 0x81, Left, Low).pos(0),
trap(create_dir_door(player, 'Hyrule Dungeon Armory S', DoorType.Normal, Direction.South, 0x71, Left, Low), 0x2).pos(1),
small_key(create_dir_door(player, 'Hyrule Dungeon Armory Interior Key Door N', DoorType.Interior, Direction.North, 0x71, Left, High)),
small_key(create_dir_door(player, 'Hyrule Dungeon Armory Interior Key Door S', DoorType.Interior, Direction.South, 0x71, Left, High)),
small_key(create_dir_door(player, 'Hyrule Dungeon Armory Interior Key Door N', DoorType.Interior, Direction.North, 0x71, Left, High)).pos(0),
small_key(create_dir_door(player, 'Hyrule Dungeon Armory Interior Key Door S', DoorType.Interior, Direction.South, 0x71, Left, High)).pos(0),
create_spiral_stairs(player, 'Hyrule Dungeon Armory Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x71, 0, HTL, A, 0x11, 0xa8, True),
create_spiral_stairs(player, 'Hyrule Dungeon Staircase Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x70, 2, LTH, A, 0x32, 0x94, True),
create_spiral_stairs(player, 'Hyrule Dungeon Staircase Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x70, 1, HTH, A, 0x11, 0x58),
@@ -134,10 +134,10 @@ def create_doors(world, player):
create_dir_door(player, 'Desert Dead End Edge', DoorType.Open, Direction.South, 0x74, None, High),
create_dir_door(player, 'Desert East Wing W Edge', DoorType.Open, Direction.West, 0x85, None, High),
create_dir_door(player, 'Desert East Wing N Edge', DoorType.Open, Direction.North, 0x85, None, High),
create_dir_door(player, 'Desert East Lobby WS', DoorType.Interior, Direction.West, 0x85, Bot, High),
create_dir_door(player, 'Desert East Wing ES', DoorType.Interior, Direction.East, 0x85, Bot, High),
small_key(create_dir_door(player, 'Desert East Wing Key Door EN', DoorType.Interior, Direction.East, 0x85, Top, High)),
small_key(create_dir_door(player, 'Desert Compass Key Door WN', DoorType.Interior, Direction.West, 0x85, Top, High)),
create_dir_door(player, 'Desert East Lobby WS', DoorType.Interior, Direction.West, 0x85, Bot, High).pos(3),
create_dir_door(player, 'Desert East Wing ES', DoorType.Interior, Direction.East, 0x85, Bot, High).pos(3),
small_key(create_dir_door(player, 'Desert East Wing Key Door EN', DoorType.Interior, Direction.East, 0x85, Top, High)).pos(1),
small_key(create_dir_door(player, 'Desert Compass Key Door WN', DoorType.Interior, Direction.West, 0x85, Top, High)).pos(1),
trap(create_dir_door(player, 'Desert Compass NW', DoorType.Normal, Direction.North, 0x85, Left, High), 0x4).pos(0),
create_dir_door(player, 'Desert Cannonball S', DoorType.Normal, Direction.South, 0x75, Left, High).pos(1),
create_dir_door(player, 'Desert Arrow Pot Corner S Edge', DoorType.Open, Direction.South, 0x75, None, High),
@@ -146,35 +146,35 @@ def create_doors(world, player):
create_dir_door(player, 'Desert North Hall SW Edge', DoorType.Open, Direction.South, 0x74, None, High),
create_dir_door(player, 'Desert North Hall W Edge', DoorType.Open, Direction.West, 0x74, None, High),
create_dir_door(player, 'Desert North Hall E Edge', DoorType.Open, Direction.East, 0x74, None, High),
create_dir_door(player, 'Desert North Hall NW', DoorType.Interior, Direction.North, 0x74, Left, High),
create_dir_door(player, 'Desert Map SW', DoorType.Interior, Direction.South, 0x74, Left, High),
create_dir_door(player, 'Desert North Hall NE', DoorType.Interior, Direction.North, 0x74, Right, High),
create_dir_door(player, 'Desert Map SE', DoorType.Interior, Direction.South, 0x74, Right, High),
create_dir_door(player, 'Desert North Hall NW', DoorType.Interior, Direction.North, 0x74, Left, High).pos(1),
create_dir_door(player, 'Desert Map SW', DoorType.Interior, Direction.South, 0x74, Left, High).pos(1),
create_dir_door(player, 'Desert North Hall NE', DoorType.Interior, Direction.North, 0x74, Right, High).pos(0),
create_dir_door(player, 'Desert Map SE', DoorType.Interior, Direction.South, 0x74, Right, High).pos(0),
create_dir_door(player, 'Desert Sandworm Corner S Edge', DoorType.Open, Direction.South, 0x73, None, High),
create_dir_door(player, 'Desert Sandworm Corner E Edge', DoorType.Open, Direction.East, 0x73, None, High),
create_dir_door(player, 'Desert Sandworm Corner NE', DoorType.Interior, Direction.North, 0x73, Right, High),
create_dir_door(player, 'Desert Bonk Torch SE', DoorType.Interior, Direction.South, 0x73, Right, High),
create_dir_door(player, 'Desert Sandworm Corner WS', DoorType.Interior, Direction.West, 0x73, Bot, High),
create_dir_door(player, 'Desert Sandworm Corner NE', DoorType.Interior, Direction.North, 0x73, Right, High).pos(2),
create_dir_door(player, 'Desert Bonk Torch SE', DoorType.Interior, Direction.South, 0x73, Right, High).pos(2),
create_dir_door(player, 'Desert Sandworm Corner WS', DoorType.Interior, Direction.West, 0x73, Bot, High).pos(1),
# I don't know if I have to mark trap on interior doors yet - haven't mucked them up much
create_dir_door(player, 'Desert Circle of Pots ES', DoorType.Interior, Direction.East, 0x73, Bot, High),
create_dir_door(player, 'Desert Circle of Pots NW', DoorType.Interior, Direction.North, 0x73, Left, High),
create_dir_door(player, 'Desert Big Chest SW', DoorType.Interior, Direction.South, 0x73, Left, High),
create_dir_door(player, 'Desert Circle of Pots ES', DoorType.Interior, Direction.East, 0x73, Bot, High).pos(1),
create_dir_door(player, 'Desert Circle of Pots NW', DoorType.Interior, Direction.North, 0x73, Left, High).pos(0),
create_dir_door(player, 'Desert Big Chest SW', DoorType.Interior, Direction.South, 0x73, Left, High).pos(0),
create_dir_door(player, 'Desert West Wing N Edge', DoorType.Open, Direction.North, 0x83, None, High),
create_dir_door(player, 'Desert West Wing WS', DoorType.Interior, Direction.West, 0x83, Bot, High),
create_dir_door(player, 'Desert West Lobby ES', DoorType.Interior, Direction.East, 0x83, Bot, High),
create_dir_door(player, 'Desert West Wing WS', DoorType.Interior, Direction.West, 0x83, Bot, High).pos(2),
create_dir_door(player, 'Desert West Lobby ES', DoorType.Interior, Direction.East, 0x83, Bot, High).pos(2),
# Desert Back
create_dir_door(player, 'Desert Back Lobby NW', DoorType.Interior, Direction.North, 0x63, Left, High),
create_dir_door(player, 'Desert Tiles 1 SW', DoorType.Interior, Direction.South, 0x63, Left, High),
small_key(create_spiral_stairs(player, 'Desert Tiles 1 Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x63, 0, HTH, A, 0x1b, 0x6c, True)),
create_dir_door(player, 'Desert Back Lobby NW', DoorType.Interior, Direction.North, 0x63, Left, High).pos(1),
create_dir_door(player, 'Desert Tiles 1 SW', DoorType.Interior, Direction.South, 0x63, Left, High).pos(1),
small_key(create_spiral_stairs(player, 'Desert Tiles 1 Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x63, 0, HTH, A, 0x1b, 0x6c, True)).pos(0),
create_spiral_stairs(player, 'Desert Bridge Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x53, 0, HTH, A, 0x0f, 0x80, True),
create_dir_door(player, 'Desert Bridge SW', DoorType.Interior, Direction.South, 0x53, Left, High),
create_dir_door(player, 'Desert Four Statues NW', DoorType.Interior, Direction.North, 0x53, Left, High),
create_dir_door(player, 'Desert Four Statues ES', DoorType.Interior, Direction.East, 0x53, Bot, High),
create_dir_door(player, 'Desert Beamos Hall WS', DoorType.Interior, Direction.West, 0x53, Bot, High),
create_dir_door(player, 'Desert Bridge SW', DoorType.Interior, Direction.South, 0x53, Left, High).pos(0),
create_dir_door(player, 'Desert Four Statues NW', DoorType.Interior, Direction.North, 0x53, Left, High).pos(0),
create_dir_door(player, 'Desert Four Statues ES', DoorType.Interior, Direction.East, 0x53, Bot, High).pos(1),
create_dir_door(player, 'Desert Beamos Hall WS', DoorType.Interior, Direction.West, 0x53, Bot, High).pos(1),
small_key(create_dir_door(player, 'Desert Beamos Hall NE', DoorType.Normal, Direction.North, 0x53, Right, High)).pos(2),
small_key(create_dir_door(player, 'Desert Tiles 2 SE', DoorType.Normal, Direction.South, 0x43, Right, High)).pos(2),
create_dir_door(player, 'Desert Tiles 2 NE', DoorType.Interior, Direction.North, 0x43, Right, High),
create_dir_door(player, 'Desert Wall Slide SE', DoorType.Interior, Direction.South, 0x43, Right, High),
create_dir_door(player, 'Desert Tiles 2 NE', DoorType.Interior, Direction.North, 0x43, Right, High).small_key().pos(1),
create_dir_door(player, 'Desert Wall Slide SE', DoorType.Interior, Direction.South, 0x43, Right, High).small_key().pos(1),
# todo: we need a new flag for a door that has a wall on it - you have to traverse it one particular way first
# the above is not a problem until we get to crossed mode
big_key(create_dir_door(player, 'Desert Wall Slide NW', DoorType.Normal, Direction.North, 0x43, Left, High)).pos(0),
@@ -182,22 +182,22 @@ def create_doors(world, player):
# Hera
create_spiral_stairs(player, 'Hera Lobby Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x77, 3, HTL, Z, 0x21, 0x90, False, True),
small_key(create_spiral_stairs(player, 'Hera Lobby Key Stairs', DoorType.SpiralStairs, Direction.Down, 0x77, 1, HTL, A, 0x12, 0x80)),
small_key(create_spiral_stairs(player, 'Hera Lobby Key Stairs', DoorType.SpiralStairs, Direction.Down, 0x77, 1, HTL, A, 0x12, 0x80)).pos(0),
create_spiral_stairs(player, 'Hera Lobby Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x77, 2, HTL, X, 0x2b, 0x5c, False, True),
create_spiral_stairs(player, 'Hera Basement Cage Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x87, 3, LTH, Z, 0x42, 0x7c, True, True),
create_spiral_stairs(player, 'Hera Tile Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x87, 1, LTH, A, 0x32, 0x6c, True, True),
create_dir_door(player, 'Hera Tile Room EN', DoorType.Interior, Direction.East, 0x87, Top, High),
create_dir_door(player, 'Hera Tridorm WN', DoorType.Interior, Direction.West, 0x87, Top, High),
create_dir_door(player, 'Hera Tridorm SE', DoorType.Interior, Direction.South, 0x87, Right, High),
create_dir_door(player, 'Hera Torches NE', DoorType.Interior, Direction.North, 0x87, Right, High),
create_dir_door(player, 'Hera Tile Room EN', DoorType.Interior, Direction.East, 0x87, Top, High).pos(0),
create_dir_door(player, 'Hera Tridorm WN', DoorType.Interior, Direction.West, 0x87, Top, High).pos(0),
create_dir_door(player, 'Hera Tridorm SE', DoorType.Interior, Direction.South, 0x87, Right, High).pos(1),
create_dir_door(player, 'Hera Torches NE', DoorType.Interior, Direction.North, 0x87, Right, High).pos(1),
create_spiral_stairs(player, 'Hera Beetles Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x31, 2, LTH, X, 0x3a, 0x70, True, True),
create_dir_door(player, 'Hera Beetles WS', DoorType.Interior, Direction.West, 0x31, Bot, High),
create_dir_door(player, 'Hera Beetles WS', DoorType.Interior, Direction.West, 0x31, Bot, High).pos(1),
create_door(player, 'Hera Beetles Holes', DoorType.Hole),
create_dir_door(player, 'Hera Startile Corner ES', DoorType.Interior, Direction.East, 0x31, Bot, High),
big_key(create_dir_door(player, 'Hera Startile Corner NW', DoorType.Interior, Direction.North, 0x31, Left, High)),
create_dir_door(player, 'Hera Startile Corner ES', DoorType.Interior, Direction.East, 0x31, Bot, High).pos(1),
big_key(create_dir_door(player, 'Hera Startile Corner NW', DoorType.Interior, Direction.North, 0x31, Left, High)).pos(0),
create_door(player, 'Hera Startile Corner Holes', DoorType.Hole),
# technically ugly but causes lots of failures in basic
create_dir_door(player, 'Hera Startile Wide SW', DoorType.Interior, Direction.South, 0x31, Left, High),
create_dir_door(player, 'Hera Startile Wide SW', DoorType.Interior, Direction.South, 0x31, Left, High).pos(0),
create_spiral_stairs(player, 'Hera Startile Wide Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x31, 0, HTH, S, 0x6b, 0xac, False, True),
create_door(player, 'Hera Startile Wide Holes', DoorType.Hole),
create_spiral_stairs(player, 'Hera 4F Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x27, 0, HTH, S, 0x62, 0xc0),
@@ -252,12 +252,12 @@ def create_doors(world, player):
create_dir_door(player, 'Tower Agahnim 1 SW', DoorType.Normal, Direction.South, 0x20, Left, High).no_exit().trap(0x4).pos(0),
#Palace of Darkness
create_door(player, 'PoD Lobby N', DoorType.Interior).dir(Direction.North, 0x4a, Mid, High),
create_door(player, 'PoD Lobby NW', DoorType.Interior).dir(Direction.North, 0x4a, Left, High),
create_door(player, 'PoD Lobby NE', DoorType.Interior).dir(Direction.North, 0x4a, Right, High),
create_door(player, 'PoD Left Cage SW', DoorType.Interior).dir(Direction.North, 0x4a, Left, High),
create_door(player, 'PoD Middle Cage S', DoorType.Interior).dir(Direction.North, 0x4a, Mid, High),
create_door(player, 'PoD Middle Cage SE', DoorType.Interior).dir(Direction.North, 0x4a, Right, High),
create_door(player, 'PoD Lobby N', DoorType.Interior).dir(Direction.North, 0x4a, Mid, High).pos(2),
create_door(player, 'PoD Lobby NW', DoorType.Interior).dir(Direction.North, 0x4a, Left, High).pos(0),
create_door(player, 'PoD Lobby NE', DoorType.Interior).dir(Direction.North, 0x4a, Right, High).pos(1),
create_door(player, 'PoD Left Cage SW', DoorType.Interior).dir(Direction.North, 0x4a, Left, High).pos(0),
create_door(player, 'PoD Middle Cage S', DoorType.Interior).dir(Direction.North, 0x4a, Mid, High).pos(2),
create_door(player, 'PoD Middle Cage SE', DoorType.Interior).dir(Direction.North, 0x4a, Right, High).pos(1),
create_door(player, 'PoD Left Cage Down Stairs', DoorType.SpiralStairs).dir(Direction.Down, 0x4a, 1, HTH).ss(A, 0x12, 0x80, False, True),
create_door(player, 'PoD Middle Cage Down Stairs', DoorType.SpiralStairs).dir(Direction.Down, 0x4a, 0, HTH).ss(S, 0x12, 0x80, False, True),
create_door(player, 'PoD Middle Cage N', DoorType.Normal).dir(Direction.North, 0x4a, Mid, High).small_key().pos(2),
@@ -271,7 +271,7 @@ def create_doors(world, player):
create_door(player, 'PoD Pit Room Bomb Hole', DoorType.Hole),
create_door(player, 'PoD Big Key Landing Hole', DoorType.Hole),
create_door(player, 'PoD Big Key Landing Down Stairs', DoorType.SpiralStairs).dir(Direction.Down, 0x3a, 0, HTH).ss(A, 0x11, 0x00),
create_door(player, 'PoD Basement Ledge Up Stairs', DoorType.SpiralStairs).dir(Direction.Up, 0x0a, 0, HTH).ss(A, 0x1a, 0xec),
create_door(player, 'PoD Basement Ledge Up Stairs', DoorType.SpiralStairs).dir(Direction.Up, 0x0a, 0, HTH).ss(A, 0x1a, 0xec).small_key().pos(0),
create_door(player, 'PoD Basement Ledge Drop Down', DoorType.Logical),
create_door(player, 'PoD Stalfos Basement Warp', DoorType.Warp),
create_door(player, 'PoD Arena Main SW', DoorType.Normal).dir(Direction.South, 0x2a, Left, High).pos(4),
@@ -291,20 +291,20 @@ def create_doors(world, player):
create_door(player, 'PoD Conveyor North Stairs', DoorType.StraightStairs).dir(Direction.North, 0x3b, Left, High),
create_door(player, 'PoD Conveyor SW', DoorType.Normal).dir(Direction.South, 0x3b, Left, High).pos(0),
create_door(player, 'PoD Mimics 1 NW', DoorType.Normal).dir(Direction.North, 0x4b, Left, High).trap(0x4).pos(0),
create_door(player, 'PoD Mimics 1 SW', DoorType.Interior).dir(Direction.South, 0x4b, Left, High),
create_door(player, 'PoD Jelly Hall NW', DoorType.Interior).dir(Direction.North, 0x4b, Left, High),
create_door(player, 'PoD Jelly Hall NE', DoorType.Interior).dir(Direction.North, 0x4b, Right, High),
create_door(player, 'PoD Warp Hint SE', DoorType.Interior).dir(Direction.South, 0x4b, Right, High),
create_door(player, 'PoD Mimics 1 SW', DoorType.Interior).dir(Direction.South, 0x4b, Left, High).pos(1),
create_door(player, 'PoD Jelly Hall NW', DoorType.Interior).dir(Direction.North, 0x4b, Left, High).pos(1),
create_door(player, 'PoD Jelly Hall NE', DoorType.Interior).dir(Direction.North, 0x4b, Right, High).pos(2),
create_door(player, 'PoD Warp Hint SE', DoorType.Interior).dir(Direction.South, 0x4b, Right, High).pos(2),
create_door(player, 'PoD Warp Hint Warp', DoorType.Warp),
create_door(player, 'PoD Falling Bridge SW', DoorType.Normal).dir(Direction.South, 0x1a, Left, High).small_key().pos(3),
create_door(player, 'PoD Falling Bridge WN', DoorType.Normal).dir(Direction.West, 0x1a, Top, High).small_key().pos(1),
create_door(player, 'PoD Falling Bridge EN', DoorType.Interior).dir(Direction.East, 0x1a, Top, High),
create_door(player, 'PoD Falling Bridge EN', DoorType.Interior).dir(Direction.East, 0x1a, Top, High).pos(4),
create_door(player, 'PoD Big Chest Balcony W', DoorType.Normal).dir(Direction.West, 0x1a, Mid, High).pos(2),
create_door(player, 'PoD Dark Maze EN', DoorType.Normal).dir(Direction.East, 0x19, Top, High).small_key().pos(1),
create_door(player, 'PoD Dark Maze E', DoorType.Normal).dir(Direction.East, 0x19, Mid, High).pos(0),
create_door(player, 'PoD Compass Room WN', DoorType.Interior).dir(Direction.West, 0x1a, Top, High),
create_door(player, 'PoD Compass Room SE', DoorType.Interior).dir(Direction.North, 0x1a, Mid, High).small_key(),
create_door(player, 'PoD Harmless Hellway NE', DoorType.Interior).dir(Direction.North, 0x1a, Right, High).small_key(),
create_door(player, 'PoD Compass Room WN', DoorType.Interior).dir(Direction.West, 0x1a, Top, High).pos(4),
create_door(player, 'PoD Compass Room SE', DoorType.Interior).dir(Direction.North, 0x1a, Mid, High).small_key().pos(0),
create_door(player, 'PoD Harmless Hellway NE', DoorType.Interior).dir(Direction.North, 0x1a, Right, High).small_key().pos(0),
create_door(player, 'PoD Harmless Hellway SE', DoorType.Normal).dir(Direction.South, 0x1a, Right, High).pos(5),
create_door(player, 'PoD Compass Room W Down Stairs', DoorType.SpiralStairs).dir(Direction.Down, 0x1a, 0, HTH).ss(S, 0x12, 0x50, True, True),
create_door(player, 'PoD Compass Room E Down Stairs', DoorType.SpiralStairs).dir(Direction.Down, 0x1a, 1, HTH).ss(S, 0x11, 0xb0, True, True),
@@ -312,16 +312,16 @@ def create_doors(world, player):
create_door(player, 'PoD Dark Basement E Up Stairs', DoorType.SpiralStairs).dir(Direction.Up, 0x6a, 1, HTH).ss(S, 0x1b, 0x9c, True),
create_door(player, 'PoD Dark Alley NE', DoorType.Normal).dir(Direction.North, 0x6a, Right, High).big_key().pos(0),
create_door(player, 'PoD Mimics 2 SW', DoorType.Normal).dir(Direction.South, 0x1b, Left, High).pos(1),
create_door(player, 'PoD Mimics 2 NW', DoorType.Interior).dir(Direction.North, 0x1b, Left, High),
create_door(player, 'PoD Bow Statue SW', DoorType.Interior).dir(Direction.South, 0x1b, Left, High),
create_door(player, 'PoD Mimics 2 NW', DoorType.Interior).dir(Direction.North, 0x1b, Left, High).pos(0),
create_door(player, 'PoD Bow Statue SW', DoorType.Interior).dir(Direction.South, 0x1b, Left, High).pos(0),
create_door(player, 'PoD Bow Statue Down Ladder', DoorType.Ladder),
create_door(player, 'PoD Dark Pegs Up Ladder', DoorType.Ladder),
create_door(player, 'PoD Dark Pegs WN', DoorType.Interior).dir(Direction.West, 0x0b, Mid, High),
create_door(player, 'PoD Lonely Turtle SW', DoorType.Interior).dir(Direction.South, 0x0b, Mid, High),
create_door(player, 'PoD Lonely Turtle EN', DoorType.Interior).dir(Direction.East, 0x0b, Mid, High),
create_door(player, 'PoD Turtle Party ES', DoorType.Interior).dir(Direction.East, 0x0b, Mid, High),
create_door(player, 'PoD Turtle Party NW', DoorType.Interior).dir(Direction.North, 0x0b, Mid, High),
create_door(player, 'PoD Callback WS', DoorType.Interior).dir(Direction.West, 0x0b, Mid, High),
create_door(player, 'PoD Dark Pegs WN', DoorType.Interior).dir(Direction.West, 0x0b, Mid, High).small_key().pos(2),
create_door(player, 'PoD Lonely Turtle SW', DoorType.Interior).dir(Direction.South, 0x0b, Mid, High).pos(0),
create_door(player, 'PoD Lonely Turtle EN', DoorType.Interior).dir(Direction.East, 0x0b, Mid, High).small_key().pos(2),
create_door(player, 'PoD Turtle Party ES', DoorType.Interior).dir(Direction.East, 0x0b, Mid, High).pos(1),
create_door(player, 'PoD Turtle Party NW', DoorType.Interior).dir(Direction.North, 0x0b, Mid, High).pos(0),
create_door(player, 'PoD Callback WS', DoorType.Interior).dir(Direction.West, 0x0b, Mid, High).pos(1),
create_door(player, 'PoD Callback Warp', DoorType.Warp),
create_door(player, 'PoD Boss SE', DoorType.Normal).dir(Direction.South, 0x5a, Right, High).no_exit().trap(0x4).pos(0),
]

View File

@@ -371,7 +371,7 @@ def create_regions(world, player):
create_dungeon_region(player, 'Tower Dark Chargers', 'Castle Tower', None, ['Tower Dark Chargers WS', 'Tower Dark Chargers Up Stairs']),
create_dungeon_region(player, 'Tower Dual Statues', 'Castle Tower', None, ['Tower Dual Statues Down Stairs', 'Tower Dual Statues WS']),
create_dungeon_region(player, 'Tower Dark Pits', 'Castle Tower', None, ['Tower Dark Pits ES', 'Tower Dark Pits EN']),
create_dungeon_region(player, 'Tower Dark Archers', 'Castle Tower', ['Castle Tower - Dark Archer Key Drop'], ['Tower Dark Archers WS', 'Tower Dark Archers Up Stairs']),
create_dungeon_region(player, 'Tower Dark Archers', 'Castle Tower', ['Castle Tower - Dark Archer Key Drop'], ['Tower Dark Archers WN', 'Tower Dark Archers Up Stairs']),
create_dungeon_region(player, 'Tower Red Spears', 'Castle Tower', None, ['Tower Red Spears Down Stairs', 'Tower Red Spears WN']),
create_dungeon_region(player, 'Tower Red Guards', 'Castle Tower', None, ['Tower Red Guards EN', 'Tower Red Guards SW']),
create_dungeon_region(player, 'Tower Circle of Pots', 'Castle Tower', ['Castle Tower - Circle of Pots Key Drop'], ['Tower Circle of Pots NW', 'Tower Circle of Pots WS']),

View File

@@ -954,7 +954,7 @@ def no_glitches_rules(world, player):
add_conditional_lamp('Tower Dual Statues WS', 'Tower Dual Statues', 'Entrance')
add_conditional_lamp('Tower Dark Pits ES', 'Tower Dark Pits', 'Entrance')
add_conditional_lamp('Tower Dark Pits EN', 'Tower Dark Pits', 'Entrance')
add_conditional_lamp('Tower Dark Archers WS', 'Tower Dark Archers', 'Entrance')
add_conditional_lamp('Tower Dark Archers WN', 'Tower Dark Archers', 'Entrance')
add_conditional_lamp('Tower Dark Archers Up Stairs', 'Tower Dark Archers', 'Entrance')
add_conditional_lamp('Castle Tower - Dark Maze', 'Tower Dark Maze', 'Location')
add_conditional_lamp('Castle Tower - Dark Archer Key Drop', 'Tower Dark Archers', 'Location')