@@ -809,6 +809,7 @@ class DoorType(Enum):
|
||||
Open = 5
|
||||
Hole = 6
|
||||
Warp = 7
|
||||
Interior = 8
|
||||
|
||||
|
||||
@unique
|
||||
@@ -849,6 +850,7 @@ class Door(object):
|
||||
self.blocked = False # Indicates if the door is normally blocked off. (Sanc door or always closed)
|
||||
self.smallKey = False # There's a small key door on this side
|
||||
self.bigKey = False # There's a big key door on this side
|
||||
self.ugly = False # Indicates that it can't be seen from the front (e.g. back of a big key door)
|
||||
|
||||
def getAddress(self):
|
||||
if self.type == DoorType.Normal:
|
||||
@@ -897,17 +899,24 @@ class Boss(object):
|
||||
return self.defeat_rule(state, self.player)
|
||||
|
||||
class Location(object):
|
||||
def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None):
|
||||
def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None):
|
||||
self.name = name
|
||||
self.parent_region = parent
|
||||
self.item = None
|
||||
if forced_item is not None:
|
||||
from Items import ItemFactory
|
||||
self.forced_item = ItemFactory([forced_item], player)[0]
|
||||
self.item = self.forced_item
|
||||
self.event = True
|
||||
else:
|
||||
self.forced_item = None
|
||||
self.item = None
|
||||
self.event = False
|
||||
self.crystal = crystal
|
||||
self.address = address
|
||||
self.spot_type = 'Location'
|
||||
self.hint_text = hint_text if hint_text is not None else 'Hyrule'
|
||||
self.recursion_count = 0
|
||||
self.staleness_count = 0
|
||||
self.event = False
|
||||
self.locked = True
|
||||
self.always_allow = lambda item, state: False
|
||||
self.access_rule = lambda state: True
|
||||
|
||||
113
DoorShuffle.py
113
DoorShuffle.py
@@ -124,7 +124,7 @@ def connect_one_way(world, entrancename, exitname, player, skipSpoiler=False):
|
||||
|
||||
# if these were already connected somewhere, remove the backreference
|
||||
if entrance.connected_region is not None:
|
||||
entrance.connected_region.entrances.remove(entrance, player)
|
||||
entrance.connected_region.entrances.remove(entrance)
|
||||
if ext.connected_region is not None:
|
||||
ext.connected_region.entrances.remove(ext)
|
||||
|
||||
@@ -143,61 +143,110 @@ def connect_one_way(world, entrancename, exitname, player, skipSpoiler=False):
|
||||
|
||||
def within_dungeon(world, player):
|
||||
# TODO: Add dungeon names to Regions so we can just look these lists up
|
||||
dungeon_region_names_es = ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Hyrule Castle East Hall', 'Hyrule Castle West Hall', 'Hyrule Castle Back Hall', 'Hyrule Castle Throne Room', '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', 'Hyrule Dungeon Staircase', 'Hyrule Dungeon Cellblock', 'Sewers Behind Tapestry', 'Sewers Rope Room', 'Sewers Dark Cross', 'Sewers Water', 'Sewers Key Rat', 'Sewers Secret Room', 'Sewers Secret Room Blocked Path', 'Sewers Pull Switch', 'Sanctuary']
|
||||
# TODO: The "starts" regions need access logic
|
||||
dungeon_region_starts_es = ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Secret Room']
|
||||
dungeon_region_names_es = ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Hyrule Castle East Hall', 'Hyrule Castle West Hall', 'Hyrule Castle Back Hall', 'Hyrule Castle Throne Room', '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 North Branch', 'Hyrule Dungeon Staircase', 'Hyrule Dungeon Cellblock', 'Sewers Behind Tapestry', 'Sewers Rope Room', 'Sewers Dark Cross', 'Sewers Water', 'Sewers Key Rat', 'Sewers Secret Room', 'Sewers Secret Room Blocked Path', 'Sewers Pull Switch', 'Sanctuary']
|
||||
dungeon_region_starts_ep = ['Eastern Lobby']
|
||||
dungeon_region_names_ep = ['Eastern Lobby', 'Eastern Cannonball', 'Eastern Cannonball Ledge', 'Eastern Courtyard Ledge', 'Eastern Map Area', 'Eastern Compass Area', 'Eastern Courtyard', 'Eastern Fairies', 'Eastern Map Valley', 'Eastern Dark Square', 'Eastern Big Key', 'Eastern Darkness', 'Eastern Attic Start', 'Eastern Attic Switches', 'Eastern Eyegores', 'Eastern Boss']
|
||||
dungeon_region_lists = [dungeon_region_names_es, dungeon_region_names_ep]
|
||||
for region_list in dungeon_region_lists:
|
||||
shuffle_dungeon(world, player, region_list)
|
||||
dungeon_region_lists = [(dungeon_region_starts_es, dungeon_region_names_es), (dungeon_region_starts_ep, dungeon_region_names_ep)]
|
||||
for start_list, region_list in dungeon_region_lists:
|
||||
shuffle_dungeon(world, player, start_list, region_list)
|
||||
|
||||
def shuffle_dungeon(world, player, dungeon_region_names):
|
||||
def shuffle_dungeon(world, player, start_region_names, dungeon_region_names):
|
||||
logger = logging.getLogger('')
|
||||
# Part one - generate a random layout
|
||||
available_regions = []
|
||||
for name in dungeon_region_names:
|
||||
for name in [r for r in dungeon_region_names if r not in start_region_names]:
|
||||
available_regions.append(world.get_region(name, player))
|
||||
random.shuffle(available_regions)
|
||||
|
||||
# "Ugly" doors are doors that we don't want to see from the front, because of some
|
||||
# sort of unsupported key door. To handle them, make a map of "ugly regions" and
|
||||
# never link across them.
|
||||
ugly_regions = {}
|
||||
next_ugly_region = 1
|
||||
|
||||
# Pick a random region and make its doors the open set
|
||||
# TODO: It would make sense to start with the entrance but I'm not sure it's needed.
|
||||
# Add all start regions to the open set.
|
||||
available_doors = []
|
||||
region = available_regions.pop()
|
||||
print("Starting in " + region.name)
|
||||
available_doors.extend(get_doors(world, region, player))
|
||||
for name in start_region_names:
|
||||
logger.info("Starting in %s", name)
|
||||
for door in get_doors(world, world.get_region(name, player), player):
|
||||
ugly_regions[door.name] = 0
|
||||
available_doors.append(door)
|
||||
|
||||
# Loop until all available doors are used
|
||||
while len(available_doors) > 0:
|
||||
# Pick a random available door to connect
|
||||
# TODO: Is there an existing "remove random from list" in this codebase?
|
||||
# Pick a random available door to connect, prioritizing ones that aren't blocked.
|
||||
# This makes them either get picked up through another door (so they head deeper
|
||||
# into the dungeon), or puts them late in the dungeon (so they probably are part
|
||||
# of a loop). Panic if neither of these happens.
|
||||
random.shuffle(available_doors)
|
||||
available_doors.sort(key=lambda door: 1 if door.blocked else 2 if door.ugly else 0)
|
||||
door = available_doors.pop()
|
||||
logger.info('Linking %s', door.name)
|
||||
# Find an available region that has a compatible door
|
||||
connect_region, connect_door = find_compatible_door_in_regions(world, door, available_regions, player)
|
||||
if connect_region is not None:
|
||||
# Also ignore compatible doors if they're blocked; these should only be used to
|
||||
# create loops.
|
||||
if connect_region is not None and not door.blocked:
|
||||
logger.info(' Found new region %s via %s', connect_region.name, connect_door.name)
|
||||
# Apply connection and add the new region's doors to the available list
|
||||
maybe_connect_two_way(world, door, connect_door, player)
|
||||
available_doors.extend(get_doors(world, connect_region, player))
|
||||
# Figure out the new room's ugliness region
|
||||
new_room_ugly_region = ugly_regions[door.name]
|
||||
if connect_door.ugly:
|
||||
next_ugly_region += 1
|
||||
new_room_ugly_region = next_ugly_region
|
||||
# Add the doors
|
||||
for door in get_doors(world, connect_region, player):
|
||||
ugly_regions[door.name] = new_room_ugly_region
|
||||
available_doors.append(door)
|
||||
# If an ugly door is anything but the connect door, panic and die
|
||||
if door != connect_door and door.ugly:
|
||||
logger.info('Failed because of ugly door, trying again.')
|
||||
shuffle_dungeon(world, player, start_region_names, dungeon_region_names)
|
||||
return
|
||||
|
||||
# We've used this region and door, so don't use them again
|
||||
available_regions.remove(connect_region)
|
||||
available_doors.remove(connect_door)
|
||||
else:
|
||||
# If there's no available region with a door, use an internal connection
|
||||
connect_door = find_compatible_door_in_list(world, door, available_doors, player)
|
||||
connect_door = find_compatible_door_in_list(ugly_regions, world, door, available_doors, player)
|
||||
# If we don't have a door at this point, it's time to panic and retry.
|
||||
if connect_door is None:
|
||||
logger.info('Failed because of blocked door, trying again.')
|
||||
shuffle_dungeon(world, player, start_region_names, dungeon_region_names)
|
||||
return
|
||||
logger.info(' Adding loop via %s', connect_door.name)
|
||||
maybe_connect_two_way(world, door, connect_door, player)
|
||||
available_doors.remove(connect_door)
|
||||
# Check that we used everything, and retry if we failed
|
||||
if len(available_regions) > 0 or len(available_doors) > 0:
|
||||
logger.info('Failed to add all regions to dungeon, trying again.')
|
||||
shuffle_dungeon(world, player, dungeon_region_names)
|
||||
logger.info('Failed to add all regions to dungeon, trying again.')
|
||||
shuffle_dungeon(world, player, start_region_names, dungeon_region_names)
|
||||
return
|
||||
|
||||
|
||||
|
||||
# Connects a and b. Or don't if they're an unsupported connection type.
|
||||
# TODO: This is gross, don't do it this way
|
||||
def maybe_connect_two_way(world, a, b, player):
|
||||
if a.type == DoorType.Open or a.type == DoorType.StraightStairs or a.type == DoorType.Hole or a.type == DoorType.Warp:
|
||||
# Return on unsupported types.
|
||||
if a.type == DoorType.Open or a.type == DoorType.StraightStairs or a.type == DoorType.Hole or a.type == DoorType.Warp or a.type == DoorType.Interior:
|
||||
return
|
||||
connect_two_way(world, a.name, b.name, player)
|
||||
|
||||
# Connect supported types
|
||||
if a.type == DoorType.Normal or a.type == DoorType.SpiralStairs:
|
||||
if a.blocked:
|
||||
connect_one_way(world, b.name, a.name, player)
|
||||
elif b.blocked:
|
||||
connect_one_way(world, a.name, b.name, player)
|
||||
else:
|
||||
connect_two_way(world, a.name, b.name, player)
|
||||
return
|
||||
# If we failed to account for a type, panic
|
||||
raise RuntimeError('Unknown door type ' + a.type.name)
|
||||
|
||||
# Finds a compatible door in regions, returns the region and door
|
||||
def find_compatible_door_in_regions(world, door, regions, player):
|
||||
for region in regions:
|
||||
@@ -206,8 +255,10 @@ def find_compatible_door_in_regions(world, door, regions, player):
|
||||
return region, proposed_door
|
||||
return None, None
|
||||
|
||||
def find_compatible_door_in_list(world, door, doors, player):
|
||||
def find_compatible_door_in_list(ugly_regions, world, door, doors, player):
|
||||
for proposed_door in doors:
|
||||
if ugly_regions[door.name] != ugly_regions[proposed_door.name]:
|
||||
continue
|
||||
if doors_compatible(door, proposed_door):
|
||||
return proposed_door
|
||||
|
||||
@@ -230,6 +281,10 @@ def doors_compatible(a, b):
|
||||
return doors_fit_mandatory_pair(falldown_pits_as_doors, a, b)
|
||||
if a.type == DoorType.Warp:
|
||||
return doors_fit_mandatory_pair(dungeon_warps_as_doors, a, b)
|
||||
if a.type == DoorType.Interior:
|
||||
return doors_fit_mandatory_pair(interior_doors, a, b)
|
||||
if a.type == DoorType.Normal and (a.smallKey or b.smallKey or a.bigKey or b.bigKey):
|
||||
return doors_fit_mandatory_pair(key_doors, a, b)
|
||||
return a.direction == switch_dir(b.direction)
|
||||
|
||||
def doors_fit_mandatory_pair(pair_list, a, b):
|
||||
@@ -664,8 +719,6 @@ def experiment(world, player):
|
||||
# DATA GOES DOWN HERE
|
||||
|
||||
mandatory_connections = [('Hyrule Dungeon North Abyss Catwalk Dropdown', 'Hyrule Dungeon North Abyss'),
|
||||
('Hyrule Dungeon Key Door S', 'Hyrule Dungeon North Abyss'),
|
||||
('Hyrule Dungeon Key Door N', 'Hyrule Dungeon Map Room'),
|
||||
('Sewers Secret Room Push Block', 'Sewers Secret Room Blocked Path')
|
||||
]
|
||||
|
||||
@@ -714,6 +767,16 @@ falldown_pits_as_doors = [('Eastern Courtyard Potholes', 'Eastern Fairy Landing'
|
||||
dungeon_warps = [('Eastern Fairies\' Warp', 'Eastern Courtyard')]
|
||||
dungeon_warps_as_doors = [('Eastern Fairies\' Warp', 'Eastern Courtyard Warp End')]
|
||||
|
||||
interior_doors = [('Hyrule Dungeon Armory Interior Key Door S', 'Hyrule Dungeon Armory Interior Key Door N'),
|
||||
('Hyrule Dungeon Map Room Key Door S', 'Hyrule Dungeon North Abyss Key Door N')]
|
||||
|
||||
key_doors = [('Sewers Key Rat Key Door N', 'Sewers Secret Room Key Door S'),
|
||||
('Sewers Dark Cross Key Door N', 'Sewers Dark Cross Key Door S'),
|
||||
('Eastern Dark Square Key Door WN', 'Eastern Cannonball Ledge Key Door EN'),
|
||||
('Eastern Darkness Up Stairs', 'Eastern Attic Start Down Stairs'),
|
||||
('Eastern Big Key NE', 'Eastern Compass Area SW'),
|
||||
('Eastern Darkness S', 'Eastern Courtyard N')]
|
||||
|
||||
default_door_connections = [('Hyrule Castle Lobby W', 'Hyrule Castle West Lobby E'),
|
||||
('Hyrule Castle Lobby E', 'Hyrule Castle East Lobby W'),
|
||||
('Hyrule Castle Lobby WN', 'Hyrule Castle West Lobby EN'),
|
||||
|
||||
33
Doors.py
33
Doors.py
@@ -49,6 +49,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),
|
||||
create_small_key_door(player, 'Hyrule Dungeon Map Room Key Door S', DoorType.Interior, Direction.South, 0x71, Right, High),
|
||||
create_small_key_door(player, 'Hyrule Dungeon North Abyss Key Door N', DoorType.Interior, Direction.North, 0x71, Right, High),
|
||||
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_dir_door(player, 'Hyrule Dungeon South Abyss North Edge', DoorType.Open, Direction.North, 0x82, None, Low),
|
||||
@@ -58,20 +60,22 @@ def create_doors(world, player):
|
||||
create_dir_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', DoorType.Open, Direction.East, 0x81, None, High),
|
||||
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),
|
||||
create_dir_door(player, 'Hyrule Dungeon Armory S', DoorType.Normal, Direction.South, 0x71, Left, Low, 0x1),
|
||||
create_dir_door(player, 'Hyrule Dungeon Armory S', DoorType.Normal, Direction.South, 0x71, Left, Low),
|
||||
create_small_key_door(player, 'Hyrule Dungeon Armory Interior Key Door N', DoorType.Interior, Direction.North, 0x71, Left, High),
|
||||
create_small_key_door(player, 'Hyrule Dungeon Armory Interior Key Door S', DoorType.Interior, Direction.South, 0x71, Left, High),
|
||||
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),
|
||||
create_spiral_stairs(player, 'Hyrule Dungeon Cellblock Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x80, 0, HTH, A, 0x1a, 0x44),
|
||||
|
||||
# sewers
|
||||
create_blocked_door(player, 'Sewers Behind Tapestry S', DoorType.Normal, Direction.South, 0x41, Mid, High, False, 0x2),
|
||||
create_blocked_door(player, 'Sewers Behind Tapestry S', DoorType.Normal, Direction.South, 0x41, Mid, High, False, False, 0x2),
|
||||
create_spiral_stairs(player, 'Sewers Behind Tapestry Down Stairs', DoorType.SpiralStairs, Direction.Down, 0x41, 0, HTH, S, 0x12, 0xb0),
|
||||
create_spiral_stairs(player, 'Sewers Rope Room Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x42, 0, HTH, S, 0x1b, 0x9c),
|
||||
create_dir_door(player, 'Sewers Rope Room North Stairs', DoorType.StraightStairs, Direction.North, 0x42, Mid, High),
|
||||
create_dir_door(player, 'Sewers Dark Cross South Stairs', DoorType.StraightStairs, Direction.South, 0x32, Mid, High),
|
||||
create_dir_door(player, 'Sewers Dark Cross Key Door N', DoorType.Normal, Direction.North, 0x32, Mid, High),
|
||||
create_dir_door(player, 'Sewers Dark Cross Key Door S', DoorType.Normal, Direction.South, 0x22, Mid, High),
|
||||
create_small_key_door(player, 'Sewers Dark Cross Key Door N', DoorType.Normal, Direction.North, 0x32, Mid, High),
|
||||
create_small_key_door(player, 'Sewers Dark Cross Key Door S', DoorType.Normal, Direction.South, 0x22, Mid, High),
|
||||
create_dir_door(player, 'Sewers Water W', DoorType.Normal, Direction.West, 0x22, Bot, High),
|
||||
create_dir_door(player, 'Sewers Key Rat E', DoorType.Normal, Direction.East, 0x21, Bot, High),
|
||||
create_small_key_door(player, 'Sewers Key Rat Key Door N', DoorType.Normal, Direction.North, 0x21, Right, High),
|
||||
@@ -94,7 +98,7 @@ def create_doors(world, player):
|
||||
create_dir_door(player, 'Eastern Map Area W', DoorType.Normal, Direction.West, 0xaa, Mid, High),
|
||||
create_dir_door(player, 'Eastern Compass Area E', DoorType.Normal, Direction.East, 0xa8, Mid, High),
|
||||
create_dir_door(player, 'Eastern Compass Area EN', DoorType.Normal, Direction.East, 0xa8, Top, Low),
|
||||
create_blocked_door(player, 'Eastern Compass Area SW', DoorType.Normal, Direction.South, 0xa8, Right, High, False),
|
||||
create_blocked_door(player, 'Eastern Compass Area SW', DoorType.Normal, Direction.South, 0xa8, Right, High, False, True),
|
||||
create_dir_door(player, 'Eastern Courtyard WN', DoorType.Normal, Direction.West, 0xa9, Top, Low),
|
||||
create_dir_door(player, 'Eastern Courtyard EN', DoorType.Normal, Direction.East, 0xa9, Top, Low),
|
||||
create_big_key_door(player, 'Eastern Courtyard N', DoorType.Normal, Direction.North, 0xa9, Mid, High),
|
||||
@@ -104,13 +108,14 @@ def create_doors(world, player):
|
||||
create_door(player, 'Eastern Courtyard Warp End', DoorType.Warp),
|
||||
create_dir_door(player, 'Eastern Map Valley WN', DoorType.Normal, Direction.West, 0xaa, Top, Low),
|
||||
create_dir_door(player, 'Eastern Map Valley SW', DoorType.Normal, Direction.South, 0xaa, Left, High),
|
||||
create_small_key_door(player, 'Eastern Dark Square NW', DoorType.Normal, Direction.North, 0xba, Left, High),
|
||||
create_dir_door(player, 'Eastern Dark Square NW', DoorType.Normal, Direction.North, 0xba, Left, High),
|
||||
create_small_key_door(player, 'Eastern Dark Square Key Door WN', DoorType.Normal, Direction.West, 0xba, Top, High),
|
||||
create_small_key_door(player, 'Eastern Big Key EN', DoorType.Normal, Direction.East, 0xb8, Top, High),
|
||||
create_dir_door(player, 'Eastern Big Key NE', DoorType.Normal, Direction.North, 0xb8, Right, High),
|
||||
create_small_key_door(player, 'Eastern Darkness S', DoorType.Normal, Direction.South, 0x99, Mid, High),
|
||||
create_dir_door(player, 'Eastern Big Key EN', DoorType.Normal, Direction.East, 0xb8, Top, High),
|
||||
create_big_key_door(player, 'Eastern Big Key NE', DoorType.Normal, Direction.North, 0xb8, Right, High),
|
||||
ugly_door(create_small_key_door(player, 'Eastern Darkness S', DoorType.Normal, Direction.South, 0x99, Mid, High)),
|
||||
# TODO: Up is a keydoor and down is not. Are they both spiralkeys or what?
|
||||
create_spiral_stairs(player, 'Eastern Darkness Up Stairs', DoorType.SpiralStairs, Direction.Up, 0x99, 0, HTH, Z, 0x1a, 0x6c, False, True),
|
||||
create_spiral_stairs(player, 'Eastern Attic Start Down Stairs', DoorType.SpiralStairs, Direction.Down, 0xda, 0, HTH, Z, 0x11, 0x80, False, True),
|
||||
ugly_door(create_spiral_stairs(player, 'Eastern Attic Start Down Stairs', DoorType.SpiralStairs, Direction.Down, 0xda, 0, HTH, Z, 0x11, 0x80, False, True)),
|
||||
create_dir_door(player, 'Eastern Attic Start WS', DoorType.Normal, Direction.West, 0xda, Bot, High),
|
||||
create_dir_door(player, 'Eastern Attic Switches ES', DoorType.Normal, Direction.East, 0xd9, Bot, High),
|
||||
create_dir_door(player, 'Eastern Attic Switches WS', DoorType.Normal, Direction.West, 0xd9, Bot, High),
|
||||
@@ -136,13 +141,13 @@ def create_big_key_door(player, name, type, direction, room, doorIndex, layer):
|
||||
return d
|
||||
|
||||
|
||||
def create_blocked_door(player, name, type, direction, room, doorIndex, layer, toggle=False, trap=0x0):
|
||||
def create_blocked_door(player, name, type, direction, room, doorIndex, layer, toggle=False, key=False, trap=0x0):
|
||||
d = Door(player, name, type, direction, room, doorIndex, layer, toggle)
|
||||
d.blocked = True
|
||||
d.smallKey = key
|
||||
d.trap = trap
|
||||
return d
|
||||
|
||||
|
||||
def create_dir_door(player, name, type, direction, room, doorIndex, layer, trap=0x0):
|
||||
d = Door(player, name, type, direction, room, doorIndex, layer)
|
||||
d.trap = trap
|
||||
@@ -164,3 +169,7 @@ def create_spiral_stairs(player, name, type, direction, room,
|
||||
d.zeroHzCam = zero_hz_cam
|
||||
d.zeroVtCam = zero_vt_cam
|
||||
return d
|
||||
|
||||
def ugly_door(door):
|
||||
door.ugly = True
|
||||
return door
|
||||
|
||||
27
Regions.py
27
Regions.py
@@ -296,13 +296,14 @@ def create_regions(world, player):
|
||||
create_dungeon_region(player, 'Hyrule Castle Back Hall', 'A dungeon', 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', 'A dungeon', None, ['Hyrule Castle Throne Room N', 'Hyrule Castle Throne Room South Stairs']),
|
||||
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Map Room', 'A dungeon', ['Hyrule Castle - Map Chest'], ['Hyrule Dungeon Key Door S', 'Hyrule Dungeon Map Room Up Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon North Abyss', 'A dungeon', None, ['Hyrule Dungeon North Abyss South Edge', 'Hyrule Dungeon Key Door N']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Map Room', 'A dungeon', ['Hyrule Castle - Map Chest', 'Hyrule Castle - Map Guard Key Drop'], ['Hyrule Dungeon Map Room Key Door S', 'Hyrule Dungeon Map Room Up Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon North Abyss', 'A dungeon', None, ['Hyrule Dungeon North Abyss South Edge', 'Hyrule Dungeon North Abyss Key Door N']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon North Abyss Catwalk', 'A dungeon', None, ['Hyrule Dungeon North Abyss Catwalk Edge', 'Hyrule Dungeon North Abyss Catwalk Dropdown']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon South Abyss', 'A dungeon', None, ['Hyrule Dungeon South Abyss North Edge', 'Hyrule Dungeon South Abyss West Edge']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon South Abyss Catwalk', 'A dungeon', None, ['Hyrule Dungeon South Abyss Catwalk North Edge', 'Hyrule Dungeon South Abyss Catwalk West Edge']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Guardroom', 'A dungeon', None, ['Hyrule Dungeon Guardroom Catwalk Edge', 'Hyrule Dungeon Guardroom Abyss Edge', 'Hyrule Dungeon Guardroom N']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Armory', 'A dungeon', ['Hyrule Castle - Boomerang Chest'], ['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Armory Main', 'A dungeon', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Boomerang Guard Key Drop'], ['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory Interior Key Door N']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Armory North Branch', 'A dungeon', None, ['Hyrule Dungeon Armory Interior Key Door S', 'Hyrule Dungeon Armory Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Staircase', 'A dungeon', None, ['Hyrule Dungeon Staircase Up Stairs', 'Hyrule Dungeon Staircase Down Stairs']),
|
||||
create_dungeon_region(player, 'Hyrule Dungeon Cellblock', 'A dungeon', ['Hyrule Castle - Zelda\'s Chest'], ['Hyrule Dungeon Cellblock Up Stairs']),
|
||||
|
||||
@@ -328,9 +329,9 @@ def create_regions(world, player):
|
||||
create_dungeon_region(player, 'Eastern Courtyard', 'A dungeon', ['Eastern Palace - Big Chest'], ['Eastern Courtyard WN', 'Eastern Courtyard EN', 'Eastern Courtyard N', 'Eastern Courtyard Potholes', 'Eastern Courtyard Warp End']),
|
||||
create_dungeon_region(player, 'Eastern Fairies', 'A dungeon', None, ['Eastern Fairies\' Warp', 'Eastern Fairy Landing']),
|
||||
create_dungeon_region(player, 'Eastern Map Valley', 'A dungeon', None, ['Eastern Map Valley WN', 'Eastern Map Valley SW']),
|
||||
create_dungeon_region(player, 'Eastern Dark Square', 'A dungeon', None, ['Eastern Dark Square NW', 'Eastern Dark Square Key Door WN']),
|
||||
create_dungeon_region(player, 'Eastern Dark Square', 'A dungeon', ['Eastern Palace - Dark Square Pot Key'], ['Eastern Dark Square NW', 'Eastern Dark Square Key Door WN']),
|
||||
create_dungeon_region(player, 'Eastern Big Key', 'A dungeon', ['Eastern Palace - Big Key Chest'], ['Eastern Big Key EN', 'Eastern Big Key NE']),
|
||||
create_dungeon_region(player, 'Eastern Darkness', 'A dungeon', None, ['Eastern Darkness S', 'Eastern Darkness Up Stairs']),
|
||||
create_dungeon_region(player, 'Eastern Darkness', 'A dungeon', ['Eastern Palace - Dark Eyegore Key Drop'], ['Eastern Darkness S', 'Eastern Darkness Up Stairs']),
|
||||
create_dungeon_region(player, 'Eastern Attic Start', 'A dungeon', None, ['Eastern Attic Start Down Stairs', 'Eastern Attic Start WS']),
|
||||
create_dungeon_region(player, 'Eastern Attic Switches', 'A dungeon', None, ['Eastern Attic Switches ES', 'Eastern Attic Switches WS']),
|
||||
create_dungeon_region(player, 'Eastern Eyegores', 'A dungeon', None, ['Eastern Eyegores ES', 'Eastern Eyegores NE']),
|
||||
@@ -377,7 +378,7 @@ def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None):
|
||||
|
||||
def create_dungeon_region(player, name, hint='Hyrule', locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.Dungeon, hint, locations, exits)
|
||||
|
||||
|
||||
def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None):
|
||||
ret = Region(name, type, hint, player)
|
||||
if locations is None:
|
||||
@@ -388,8 +389,11 @@ 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:
|
||||
address, crystal, hint_text = location_table[location]
|
||||
ret.locations.append(Location(player, location, address, crystal, hint_text, ret))
|
||||
if location in key_only_locations:
|
||||
ret.locations.append(Location(player, location, None, False, None, ret, key_only_locations[location]))
|
||||
else:
|
||||
address, crystal, hint_text = location_table[location]
|
||||
ret.locations.append(Location(player, location, address, crystal, hint_text, ret))
|
||||
return ret
|
||||
|
||||
def mark_light_world_regions(world):
|
||||
@@ -455,6 +459,13 @@ default_shop_contents = {
|
||||
'Potion Shop': [('Red Potion', 120), ('Green Potion', 60), ('Blue Potion', 160)],
|
||||
}
|
||||
|
||||
key_only_locations = {
|
||||
'Hyrule Castle - Map Guard Key Drop': 'Small Key (Escape)',
|
||||
'Hyrule Castle - Boomerang Guard Key Drop': 'Small Key (Escape)',
|
||||
'Eastern Palace - Dark Square Pot Key': 'Small Key (Eastern Palace)',
|
||||
'Eastern Palace - Dark Eyegore Key Drop': 'Small Key (Eastern Palace)',
|
||||
}
|
||||
|
||||
location_table = {'Mushroom': (0x180013, False, 'in the woods'),
|
||||
'Bottle Merchant': (0x2eb18, False, 'with a merchant'),
|
||||
'Flute Spot': (0x18014a, False, 'underground'),
|
||||
|
||||
64
Rules.py
64
Rules.py
@@ -258,14 +258,66 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!)
|
||||
set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player))
|
||||
|
||||
# Start of door rando rules
|
||||
# TODO: Do these need to flag off when door rando is off?
|
||||
# TODO: Can these replace other rules?
|
||||
|
||||
# Hyrule Castle: Can't get keys from guards unless you can kill said guards.
|
||||
add_rule(world.get_location('Hyrule Castle - Map Guard Key Drop', player), lambda state: state.can_kill_most_things(player))
|
||||
add_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player), lambda state: state.can_kill_most_things(player))
|
||||
|
||||
# Hyrule Castle: There are three keys and we don't know how we shuffled, so we
|
||||
# need three keys to be accessible before you use any of these doors.
|
||||
# TODO: Generate key rules in the shuffler. (But make sure this way works first.)
|
||||
for door in ['Sewers Key Rat Key Door N', 'Sewers Secret Room Key Door S',
|
||||
'Sewers Dark Cross Key Door N', 'Sewers Dark Cross Key Door S', 'Hyrule Dungeon Armory Interior Key Door N', 'Hyrule Dungeon Armory Interior Key Door S', 'Hyrule Dungeon Map Room Key Door S', 'Hyrule Dungeon North Abyss Key Door N']:
|
||||
set_rule(world.get_entrance(door, player), lambda state: state.has_key('Small Key (Escape)', player, 3))
|
||||
|
||||
# Sewers: All sorts of things need lamps
|
||||
# TODO: Need to play nice with other complicated lamp rules
|
||||
set_rule(world.get_entrance('Sewers Behind Tapestry S', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Behind Tapestry Down Stairs', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Rope Room Up Stairs', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Rope Room North Stairs', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Dark Cross South Stairs', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Dark Cross Key Door N', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Dark Cross Key Door S', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_location('Sewers - Dark Cross', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Water W', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Key Rat E', player), lambda state: state.has('Lamp', player))
|
||||
set_rule(world.get_entrance('Sewers Key Rat Key Door N', player), lambda state: state.has('Lamp', player))
|
||||
|
||||
# Eastern Palace
|
||||
# The stalfos room and eyegore with a key can be killed with pots.
|
||||
# Eastern Palace has dark rooms.
|
||||
for location in ['Eastern Palace - Dark Square Pot Key', 'Eastern Palace - Dark Eyegore Key Drop']:
|
||||
add_rule(world.get_location(location, player), lambda state: state.has('Lamp', player))
|
||||
for door in ['Eastern Darkness S', 'Eastern Darkness Up Stairs', 'Eastern Dark Square NW', 'Eastern Dark Square Key Door WN']:
|
||||
add_rule(world.get_entrance(door, player), lambda state: state.has('Lamp', player))
|
||||
# Eyegore room needs a bow
|
||||
set_rule(world.get_entrance('Eastern Eyegores NE', player), lambda state: state.can_shoot_arrows(player))
|
||||
# Big key rules
|
||||
set_rule(world.get_location('Eastern Palace - Big Chest', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||
set_rule(world.get_entrance('Eastern Big Key NE', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||
set_rule(world.get_entrance('Eastern Courtyard N', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||
# There are two keys and we don't know how we shuffled, so careful with key doors.
|
||||
# TODO: Generate key rules in the shuffler. (But make sure this way works first.)
|
||||
for door in ['Eastern Dark Square Key Door WN', 'Eastern Cannonball Ledge Key Door EN', 'Eastern Darkness Up Stairs', 'Eastern Attic Start Down Stairs']:
|
||||
set_rule(world.get_entrance(door, player), lambda state: state.has_key('Small Key (Eastern Palace)', player, 2))
|
||||
|
||||
# Boss rules. Same as below but no BK or arrow requirement.
|
||||
set_rule(world.get_location('Eastern Palace - Boss', player), lambda state: world.get_location('Eastern Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
set_rule(world.get_location('Eastern Palace - Prize', player), lambda state: world.get_location('Eastern Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
|
||||
# End of door rando rules.
|
||||
|
||||
# set_rule(world.get_entrance('Sewers Door', player), lambda state: state.has_key('Small Key (Escape)', player))
|
||||
# set_rule(world.get_entrance('Sewers Back Door', player), lambda state: state.has_key('Small Key (Escape)', player))
|
||||
|
||||
set_rule(world.get_location('Eastern Palace - Big Chest', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||
set_rule(world.get_location('Eastern Palace - Boss', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
set_rule(world.get_location('Eastern Palace - Prize', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
for location in ['Eastern Palace - Boss', 'Eastern Palace - Big Chest']:
|
||||
forbid_item(world.get_location(location, player), 'Big Key (Eastern Palace)', player)
|
||||
# set_rule(world.get_location('Eastern Palace - Boss', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
# set_rule(world.get_location('Eastern Palace - Prize', player), lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)', player) and world.get_location('Eastern Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
# for location in ['Eastern Palace - Boss', 'Eastern Palace - Big Chest']:
|
||||
# forbid_item(world.get_location(location, player), 'Big Key (Eastern Palace)', player)
|
||||
|
||||
set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player))
|
||||
set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has_Boots(player))
|
||||
@@ -951,7 +1003,7 @@ def swordless_rules(world, player):
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
|
||||
|
||||
def standard_rules(world, player):
|
||||
add_rule(world.get_entrance('Sewers Door', player), lambda state: state.can_kill_most_things(player))
|
||||
# add_rule(world.get_entrance('Sewers Door', player), lambda state: state.can_kill_most_things(player))
|
||||
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (East)', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||
set_rule(world.get_entrance('Hyrule Castle Exit (West)', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||
|
||||
Reference in New Issue
Block a user