diff --git a/BaseClasses.py b/BaseClasses.py index b905dfff..70c3f0c0 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -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) diff --git a/DoorShuffle.py b/DoorShuffle.py index cfb3da88..a7702f28 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -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'], diff --git a/Doors.py b/Doors.py index df76f22e..db2ea95e 100644 --- a/Doors.py +++ b/Doors.py @@ -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), ] diff --git a/Regions.py b/Regions.py index 3ac73486..9a1abbe5 100644 --- a/Regions.py +++ b/Regions.py @@ -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']), diff --git a/Rules.py b/Rules.py index 55009687..672bfc1c 100644 --- a/Rules.py +++ b/Rules.py @@ -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')