Upgrades doors to be written out by the rom. (No testing today)

Added some various modes to play around with.
Fixed prototype's generation code to run
This commit is contained in:
randall.rupper
2019-08-23 16:52:53 -06:00
parent 2ff60fe377
commit c87c767835
8 changed files with 324 additions and 188 deletions

View File

@@ -8,9 +8,10 @@ from Utils import int16_as_bytes
class World(object):
def __init__(self, players, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, place_dungeon_items, accessibility, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints):
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, place_dungeon_items, accessibility, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints):
self.players = players
self.shuffle = shuffle
self.doorShuffle = doorShuffle
self.logic = logic
self.mode = mode
self.swords = swords
@@ -811,23 +812,41 @@ class DoorType(Enum):
@unique
class Direction(Enum):
North = 1
West = 2
South = 3
East = 4
North = 0
West = 1
South = 2
East = 3
class Door(object):
def __init__(self, player, name, type, direction):
def __init__(self, player, name, type, direction, roomIndex, doorIndex, layer, toggle=False):
self.player = player
self.name = name
self.type = type
self.direction = direction
self.connected = False
# rom properties
self.roomIndex = roomIndex
self.doorIndex = doorIndex # 0,1,2 + Direction (N:0, W:3, S:6, E:9)
self.layer = layer # 0 for normal floor, 1 for the inset layer
self.toggle = toggle
# logical properties
# self.connected = False # combine with Dest?
self.dest = None
self.parentChunk = None
# probably need exact location of the 12 base types (6 intraroom doors)
# need the z-index
# need the room index it is located in most likely
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
def getAddress(self):
if self.type == DoorType.Normal:
return 0x13A000 + self.roomIndex * 24 + (self.doorIndex + self.direction.value * 3) * 2
def getTarget(self, toggle):
layer = 4 * (self.layer ^ 1 if toggle else self.layer)
return [self.roomIndex, layer + self.doorIndex]
def __str__(self):
return str(self.__unicode__())

View File

@@ -9,8 +9,6 @@ from Items import ItemFactory
def link_doors(world, player):
logger = logging.getLogger('')
# Make drop-down connections - if applicable
for exitName, regionName in mandatory_connections:
connect_simple_door(world, exitName, regionName, player)
@@ -27,18 +25,34 @@ def link_doors(world, player):
for exitName, regionName in dungeon_warps:
connect_simple_door(world, exitName, regionName, player)
# vanilla - todo: different modes
if world.doorShuffle == 'vanilla':
for entrance, ext in default_door_connections:
connect_two_way(world, entrance, ext, player)
for ent, ext in default_one_way_connections:
connect_one_way(world, ent, ext, player)
normal_dungeon_pool(world, player)
elif world.doorShuffle == 'basic':
normal_dungeon_pool(world, player)
within_dungeon(world, player)
elif world.doorShuffle == 'crossed':
normal_dungeon_pool(world, player)
cross_dungeon(world, player)
elif world.doorShuffle == 'experimental':
normal_dungeon_pool(world, player)
experiment(world, player)
mark_regions(world, player)
def normal_dungeon_pool(world, player):
# vanilla dungeon items
ES = world.get_dungeon('Hyrule Castle', player)
ES.small_keys = [ItemFactory('Small Key (Escape)', player)]
EP = world.get_dungeon('Eastern Palace', player)
EP.big_key = ItemFactory('Big Key (Eastern Palace)', player)
def mark_regions(world, player):
# traverse dungeons and make sure dungeon property is assigned
playerDungeons = [dungeon for dungeon in world.dungeons if dungeon.player == player]
for dungeon in playerDungeons:
@@ -52,13 +66,84 @@ def link_doors(world, player):
d = world.check_for_door(ext.name, player)
connected = ext.connected_region
if d is not None and connected is not None:
if d.connected and connected.name not in dungeon.regions and connected.type == RegionType.Dungeon and connected.name not in queue:
if d.dest is not None and connected.name not in dungeon.regions and connected.type == RegionType.Dungeon and connected.name not in queue:
queue.append(connected) # needs to be added
elif connected is not None and connected.name not in dungeon.regions and connected.type == RegionType.Dungeon and connected.name not in queue:
queue.append(connected) # needs to be added
return
#code below is a prototype for cross-dungeon mode
# some useful functions
def switch_dir(direction):
oppositemap = {
Direction.South: Direction.North,
Direction.North: Direction.South,
Direction.West: Direction.East,
Direction.East: Direction.West,
}
return oppositemap[direction]
def connect_simple_door(world, exit_name, region_name, player):
region = world.get_region(region_name, player)
world.get_entrance(exit_name, player).connect(region)
d = world.check_for_door(exit_name, player)
if d is not None:
d.dest = region
def connect_two_way(world, entrancename, exitname, player):
entrance = world.get_entrance(entrancename, player)
ext = world.get_entrance(exitname, player)
# if these were already connected somewhere, remove the backreference
if entrance.connected_region is not None:
entrance.connected_region.entrances.remove(entrance, player)
if ext.connected_region is not None:
ext.connected_region.entrances.remove(ext)
# todo - access rules for the doors...
entrance.connect(ext.parent_region)
ext.connect(entrance.parent_region)
if entrance.parent_region.dungeon:
ext.parent_region.dungeon = entrance.parent_region.dungeon
x = world.check_for_door(entrancename, player)
y = world.check_for_door(exitname, player)
if x is not None:
x.dest = y
if y is not None:
y.dest = x
# world.spoiler.set_entrance(entrance.name, exit.name, 'both') # todo: spoiler stuff
def connect_one_way(world, entrancename, exitname, player):
entrance = world.get_entrance(entrancename, player)
ext = world.get_entrance(exitname, player)
# if these were already connected somewhere, remove the backreference
if entrance.connected_region is not None:
entrance.connected_region.entrances.remove(entrance, player)
if ext.connected_region is not None:
ext.connected_region.entrances.remove(ext)
entrance.connect(ext.parent_region)
if entrance.parent_region.dungeon:
ext.parent_region.dungeon = entrance.parent_region.dungeon
x = world.check_for_door(entrancename, player)
y = world.check_for_door(exitname, player)
if x is not None:
x.dest = y
if y is not None:
y.dest = x
# spoiler info goes here?
def within_dungeon(world, player):
raise NotImplementedError('Haven\'t started this yet')
# code below is an early prototype for cross-dungeon mode
def cross_dungeon(world, player):
logger = logging.getLogger('')
# figure out which dungeons have open doors and which doors still need to be connected
@@ -79,15 +164,15 @@ def link_doors(world, player):
available_doors = set(world.doors)
unfinished_dungeons = []
# modfiy avail doors and d_regions, produces a list of unlinked doors
# modify avail doors and d_regions, produces a list of unlinked doors
for dungeon in world.dungeons:
dungeon.paths = dungeon_paths[dungeon.name]
for path in dungeon.paths:
dungeon.path_completion[path] = False
for regionName in list(dungeon.regions):
region = world.get_region(regionName)
region = world.get_region(regionName, player)
dungeon.regions.remove(regionName)
chunk = create_chunk(world, region, available_dungeon_regions, available_doors)
chunk = create_chunk(world, player, region, available_dungeon_regions, available_doors)
dungeon.chunks.append(chunk)
# todo: indicate entrance chunks
dungeon.regions.extend(chunk.regions)
@@ -108,7 +193,7 @@ def link_doors(world, player):
avail_chunks = []
while len(available_dungeon_regions) > 0:
region = available_dungeon_regions.pop()
chunk = create_chunk(world, region, available_dungeon_regions)
chunk = create_chunk(world, player, region, available_dungeon_regions)
if chunk.outflow > 0:
avail_chunks.append(chunk)
@@ -126,11 +211,11 @@ def link_doors(world, player):
for dungeon in unfinished_dungeons:
logger.info('Starting %s', dungeon.name)
bailcnt = 0
while not is_dungeon_finished(world, dungeon):
while not is_dungeon_finished(world, player, dungeon):
# pick some unfinished criteria to help?
trgt_pct = len(dungeon.regions) / target_regions
for path in dungeon.paths:
find_path(world, path, dungeon.path_completion)
find_path(world, player, path, dungeon.path_completion)
# process - expand to about half size
# start closing off unlinked doors - self pick vs dead end pick
@@ -187,14 +272,13 @@ def link_doors(world, player):
else:
bailcnt += 1
if len(dungeon.unlinked_doors) == 0 and not is_dungeon_finished(world, dungeon):
if len(dungeon.unlinked_doors) == 0 and not is_dungeon_finished(world, player, dungeon):
raise RuntimeError('Made a bad dungeon - more smarts needed')
if bailcnt > 100:
raise RuntimeError('Infinite loop detected - see output')
def create_chunk(world, newregion, available_dungeon_regions, available_doors=None):
def create_chunk(world, player, newregion, available_dungeon_regions, available_doors=None):
# if newregion.name in dungeon.regions:
# return # we've been here before
chunk = RegionChunk()
@@ -206,14 +290,14 @@ def create_chunk(world, newregion, available_dungeon_regions, available_doors=No
available_dungeon_regions.remove(region)
chunk.chests += len(region.locations)
for ext in region.exits:
d = world.check_for_door(ext.name)
d = world.check_for_door(ext.name, player)
connected = ext.connected_region
# todo - check for key restrictions?
if d is not None:
if available_doors is not None:
available_doors.remove(d)
d.parentChunk = chunk
if not d.connected:
if d.dest is None:
chunk.outflow += 1
# direction of door catalog ?
chunk.unlinked_doors.add(d)
@@ -438,11 +522,11 @@ def valid_self_pick(src_door, dest_door):
return False
def is_dungeon_finished(world, dungeon):
def is_dungeon_finished(world, player, dungeon):
if len(dungeon.unlinked_doors) > 0: # no unlinked doors
return False
for path in dungeon.paths: # paths through dungeon are possible
if not find_path(world, path, dungeon.path_completion):
if not find_path(world, player, path, dungeon.path_completion):
return False
# if dungeon.chests < dungeon.count_dungeon_item() + 2: # 2 or more chests reachable in dungeon than number of dungeon items
# return False
@@ -451,11 +535,11 @@ def is_dungeon_finished(world, dungeon):
return True
def find_path(world, path, path_completion):
def find_path(world, player, path, path_completion):
if path_completion[path]: # found it earlier -- assuming no disconnects
return True
visited_regions = set([])
queue = collections.deque([world.get_region(path[0])])
queue = collections.deque([world.get_region(path[0], player)])
while len(queue) > 0:
region = queue.popleft()
if region.name == path[1]:
@@ -469,68 +553,18 @@ def find_path(world, path, path_completion):
queue.append(connected)
return False
def switch_dir(direction):
oppositemap = {
Direction.South: Direction.North,
Direction.North: Direction.South,
Direction.West: Direction.East,
Direction.East: Direction.West,
}
return oppositemap[direction]
def experiment(world, player):
for ent, ext in experimental_connections:
if world.get_door(ent, player).blocked:
connect_one_way(world, ext, ent, player)
elif world.get_door(ext, player).blocked:
connect_one_way(world, ent, ext, player)
else:
connect_two_way(world, ent, ext, player)
def connect_simple_door(world, exit_name, region_name, player):
world.get_entrance(exit_name, player).connect(world.get_region(region_name, player))
d = world.check_for_door(exit_name, player)
if d is not None:
d.connected = True
def connect_two_way(world, entrancename, exitname, player):
entrance = world.get_entrance(entrancename, player)
ext = world.get_entrance(exitname, player)
# if these were already connected somewhere, remove the backreference
if entrance.connected_region is not None:
entrance.connected_region.entrances.remove(entrance, player)
if ext.connected_region is not None:
ext.connected_region.entrances.remove(ext)
# todo - rom indications, access rules for the doors...
entrance.connect(ext.parent_region)
ext.connect(entrance.parent_region)
if entrance.parent_region.dungeon:
ext.parent_region.dungeon = entrance.parent_region.dungeon
d = world.check_for_door(entrancename, player)
if d is not None:
d.connected = True
d = world.check_for_door(exitname, player)
if d is not None:
d.connected = True
# world.spoiler.set_entrance(entrance.name, exit.name, 'both') # todo: spoiler stuff
def connect_one_way(world, entrancename, exitname, player):
entrance = world.get_entrance(entrancename, player)
ext = world.get_entrance(exitname, player)
# if these were already connected somewhere, remove the backreference
if entrance.connected_region is not None:
entrance.connected_region.entrances.remove(entrance, player)
if ext.connected_region is not None:
ext.connected_region.entrances.remove(ext)
entrance.connect(ext.parent_region)
d = world.check_for_door(entrancename, player)
if entrance.parent_region.dungeon:
ext.parent_region.dungeon = entrance.parent_region.dungeon
if d is not None:
d.connected = True
d = world.check_for_door(exitname, player)
if d is not None:
d.connected = True
# spoiler info goes here?
# 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'),
@@ -580,7 +614,7 @@ default_door_connections = [('Hyrule Castle Lobby W', 'Hyrule Castle West Lobby
('Hyrule Castle Lobby WN', 'Hyrule Castle West Lobby EN'),
('Hyrule Castle West Lobby N', 'Hyrule Castle West Hall S'),
('Hyrule Castle East Lobby N', 'Hyrule Castle East Hall S'),
('Hyrule Castle East Lobby NE', 'Hyrule Castle East Hall SE'),
('Hyrule Castle East Lobby NW', 'Hyrule Castle East Hall SW'),
('Hyrule Castle East Hall W', 'Hyrule Castle Back Hall E'),
('Hyrule Castle West Hall E', 'Hyrule Castle Back Hall W'),
('Hyrule Castle Throne Room N', 'Sewers Behind Tapestry S'),
@@ -604,3 +638,24 @@ default_door_connections = [('Hyrule Castle Lobby W', 'Hyrule Castle West Lobby
# ('', ''),
default_one_way_connections = [('Sewers Pull Switch S', 'Sanctuary N'),
('Eastern Big Key NE', 'Eastern Compass Area SW')]
experimental_connections = [('Eastern Boss SE', 'Eastern Courtyard N'),
('Eastern Courtyard EN', 'Eastern Attic Switches WS'),
('Eastern Lobby N', 'Eastern Darkness S'),
('Eastern Courtyard WN', 'Eastern Compass Area E'),
('Eastern Attic Switches ES', 'Eastern Cannonball Ledge WN'),
('Eastern Compass Area EN', 'Hyrule Castle Back Hall W'),
('Hyrule Castle Back Hall E', 'Eastern Map Area W'),
('Eastern Attic Start WS', 'Eastern Cannonball Ledge Key Door EN'),
('Eastern Compass Area SW', 'Hyrule Dungeon Guardroom N'),
('Hyrule Castle East Lobby NW', 'Hyrule Castle East Hall SW'),
('Hyrule Castle East Lobby N', 'Eastern Courtyard Ledge S'),
('Hyrule Castle Lobby E', 'Eastern Courtyard Ledge W'),
('Hyrule Castle Lobby WN', 'Eastern Courtyard Ledge E'),
('Hyrule Castle West Lobby EN', 'Hyrule Castle East Lobby W'),
('Hyrule Castle Throne Room N', 'Hyrule Castle East Hall S'),
('Hyrule Castle West Lobby E', 'Hyrule Castle East Hall W'),
('Hyrule Castle West Lobby N', 'Hyrule Dungeon Armory S'),
('Hyrule Castle Lobby W', 'Hyrule Castle West Hall E'),
('Hyrule Castle West Hall S', 'Sanctuary N')]

202
Doors.py
View File

@@ -1,99 +1,133 @@
from BaseClasses import Door, DoorType, Direction
# constants
Top = 0
Left = 0
Mid = 1
Bot = 2
Right = 2
def create_doors(world, player):
world.doors += [
# hyrule castle
create_door(player, 'Hyrule Castle Lobby W', DoorType.Normal, Direction.West),
create_door(player, 'Hyrule Castle Lobby E', DoorType.Normal, Direction.East),
create_door(player, 'Hyrule Castle Lobby WN', DoorType.Normal, Direction.West),
create_door(player, 'Hyrule Castle Lobby North Stairs', DoorType.StraightStairs, Direction.North),
create_door(player, 'Hyrule Castle West Lobby E', DoorType.Normal, Direction.East),
create_door(player, 'Hyrule Castle West Lobby N', DoorType.Normal, Direction.North),
create_door(player, 'Hyrule Castle West Lobby EN', DoorType.Normal, Direction.East),
create_door(player, 'Hyrule Castle East Lobby W', DoorType.Normal, Direction.West),
create_door(player, 'Hyrule Castle East Lobby N', DoorType.Normal, Direction.North),
create_door(player, 'Hyrule Castle East Lobby NE', DoorType.Normal, Direction.North),
create_door(player, 'Hyrule Castle East Hall W', DoorType.Normal, Direction.West),
create_door(player, 'Hyrule Castle East Hall S', DoorType.Normal, Direction.South),
create_door(player, 'Hyrule Castle East Hall SE', DoorType.Normal, Direction.South),
create_door(player, 'Hyrule Castle West Hall E', DoorType.Normal, Direction.East),
create_door(player, 'Hyrule Castle West Hall S', DoorType.Normal, Direction.South),
create_door(player, 'Hyrule Castle Back Hall W', DoorType.Normal, Direction.West),
create_door(player, 'Hyrule Castle Back Hall E', DoorType.Normal, Direction.East),
create_door(player, 'Hyrule Castle Back Hall Down Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Hyrule Castle Throne Room N', DoorType.Normal, Direction.North),
create_door(player, 'Hyrule Castle Throne Room South Stairs', DoorType.StraightStairs, Direction.South),
create_toggle_door(player, 'Hyrule Castle Lobby W', DoorType.Normal, Direction.West, 0x61, Mid, 0),
create_toggle_door(player, 'Hyrule Castle Lobby E', DoorType.Normal, Direction.East, 0x61, Mid, 0),
create_dir_door(player, 'Hyrule Castle Lobby WN', DoorType.Normal, Direction.West, 0x61, Top, 0),
create_dir_door(player, 'Hyrule Castle Lobby North Stairs', DoorType.StraightStairs, Direction.North, 0x61, Mid, 0),
create_toggle_door(player, 'Hyrule Castle West Lobby E', DoorType.Normal, Direction.East, 0x60, Mid, 1),
create_dir_door(player, 'Hyrule Castle West Lobby N', DoorType.Normal, Direction.North, 0x60, Right, 1),
create_dir_door(player, 'Hyrule Castle West Lobby EN', DoorType.Normal, Direction.East, 0x60, Top, 1),
create_toggle_door(player, 'Hyrule Castle East Lobby W', DoorType.Normal, Direction.West, 0x62, Mid, 1),
create_dir_door(player, 'Hyrule Castle East Lobby N', DoorType.Normal, Direction.North, 0x62, Mid, 0),
create_dir_door(player, 'Hyrule Castle East Lobby NW', DoorType.Normal, Direction.North, 0x62, Left, 1),
create_dir_door(player, 'Hyrule Castle East Hall W', DoorType.Normal, Direction.West, 0x52, Top, 1),
create_dir_door(player, 'Hyrule Castle East Hall S', DoorType.Normal, Direction.South, 0x52, Mid, 0),
create_dir_door(player, 'Hyrule Castle East Hall SW', DoorType.Normal, Direction.South, 0x52, Left, 1),
create_dir_door(player, 'Hyrule Castle West Hall E', DoorType.Normal, Direction.East, 0x50, Top, 1),
create_dir_door(player, 'Hyrule Castle West Hall S', DoorType.Normal, Direction.South, 0x50, Right, 1),
create_dir_door(player, 'Hyrule Castle Back Hall W', DoorType.Normal, Direction.West, 0x01, Top, 1),
create_dir_door(player, 'Hyrule Castle Back Hall E', DoorType.Normal, Direction.East, 0x01, Top, 1),
create_door(player, 'Hyrule Castle Back Hall Down Stairs', DoorType.SpiralStairs),
create_dir_door(player, 'Hyrule Castle Throne Room N', DoorType.Normal, Direction.North, 0x51, Mid, 0),
create_dir_door(player, 'Hyrule Castle Throne Room South Stairs', DoorType.StraightStairs, Direction.South, 0x51, Mid, 1),
# hyrule dungeon level
create_door(player, 'Hyrule Dungeon Map Room Up Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Hyrule Dungeon North Abyss South Edge', DoorType.Open, Direction.South),
create_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', DoorType.Open, Direction.South),
create_door(player, 'Hyrule Dungeon South Abyss North Edge', DoorType.Open, Direction.North),
create_door(player, 'Hyrule Dungeon South Abyss West Edge', DoorType.Open, Direction.West),
create_door(player, 'Hyrule Dungeon South Abyss Catwalk North Edge', DoorType.Open, Direction.North),
create_door(player, 'Hyrule Dungeon South Abyss Catwalk West Edge', DoorType.Open, Direction.West),
create_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', DoorType.Open, Direction.East),
create_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', DoorType.Open, Direction.West),
create_door(player, 'Hyrule Dungeon Guardroom N', DoorType.Normal, Direction.North),
create_door(player, 'Hyrule Dungeon Armory S', DoorType.Normal, Direction.South),
create_door(player, 'Hyrule Dungeon Armory Down Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Hyrule Dungeon Staircase Up Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Hyrule Dungeon Staircase Down Stair', DoorType.SpiralStairs, None),
create_door(player, 'Hyrule Dungeon Cellblock Up Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Hyrule Dungeon Map Room Up Stairs', DoorType.SpiralStairs),
create_dir_door(player, 'Hyrule Dungeon North Abyss South Edge', DoorType.Open, Direction.South, 0x72, None, 1),
create_dir_door(player, 'Hyrule Dungeon North Abyss Catwalk Edge', DoorType.Open, Direction.South, 0x72, None, 0),
create_dir_door(player, 'Hyrule Dungeon South Abyss North Edge', DoorType.Open, Direction.North, 0x82, None, 1),
create_dir_door(player, 'Hyrule Dungeon South Abyss West Edge', DoorType.Open, Direction.West, 0x82, None, 1),
create_dir_door(player, 'Hyrule Dungeon South Abyss Catwalk North Edge', DoorType.Open, Direction.North, 0x82, None, 0),
create_dir_door(player, 'Hyrule Dungeon South Abyss Catwalk West Edge', DoorType.Open, Direction.West, 0x82, None, 0),
create_dir_door(player, 'Hyrule Dungeon Guardroom Catwalk Edge', DoorType.Open, Direction.East, 0x81, None, 0),
create_dir_door(player, 'Hyrule Dungeon Guardroom Abyss Edge', DoorType.Open, Direction.West, 0x81, None, 0),
create_dir_door(player, 'Hyrule Dungeon Guardroom N', DoorType.Normal, Direction.North, 0x71, Left, 1),
create_dir_door(player, 'Hyrule Dungeon Armory S', DoorType.Normal, Direction.South, 0x71, Left, 1),
create_door(player, 'Hyrule Dungeon Armory Down Stairs', DoorType.SpiralStairs),
create_door(player, 'Hyrule Dungeon Staircase Up Stairs', DoorType.SpiralStairs),
create_door(player, 'Hyrule Dungeon Staircase Down Stairs', DoorType.SpiralStairs),
create_door(player, 'Hyrule Dungeon Cellblock Up Stairs', DoorType.SpiralStairs),
# sewers
create_door(player, 'Sewers Behind Tapestry S', DoorType.Normal, Direction.South), # one-way, this door is locked
create_door(player, 'Sewers Behind Tapestry Down Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Sewers Rope Room Up Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Sewers Rope Room North Stairs', DoorType.StraightStairs, Direction.North),
create_door(player, 'Sewers Dark Cross South Stairs', DoorType.StraightStairs, Direction.South),
create_door(player, 'Sewers Dark Cross Key Door N', DoorType.Normal, Direction.North),
create_door(player, 'Sewers Dark Cross Key Door S', DoorType.Normal, Direction.South),
create_door(player, 'Sewers Water W', DoorType.Normal, Direction.West),
create_door(player, 'Sewers Key Rat E', DoorType.Normal, Direction.East),
create_door(player, 'Sewers Key Rat Key Door N', DoorType.Normal, Direction.North),
create_door(player, 'Sewers Secret Room Key Door S', DoorType.Normal, Direction.South),
create_door(player, 'Sewers Secret Room Up Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Sewers Pull Switch Down Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Sewers Pull Switch S', DoorType.Normal, Direction.South),
create_door(player, 'Sanctuary N', DoorType.Normal, Direction.North), # logically one way, but should be linked
create_blocked_door(player, 'Sewers Behind Tapestry S', DoorType.Normal, Direction.South, 0x41, Mid, 0),
create_door(player, 'Sewers Behind Tapestry Down Stairs', DoorType.SpiralStairs),
create_door(player, 'Sewers Rope Room Up Stairs', DoorType.SpiralStairs),
create_dir_door(player, 'Sewers Rope Room North Stairs', DoorType.StraightStairs, Direction.North, 0x42, Mid, 0),
create_dir_door(player, 'Sewers Dark Cross South Stairs', DoorType.StraightStairs, Direction.South, 0x32, Mid, 0),
create_dir_door(player, 'Sewers Dark Cross Key Door N', DoorType.Normal, Direction.North, 0x32, Mid, 0),
create_dir_door(player, 'Sewers Dark Cross Key Door S', DoorType.Normal, Direction.South, 0x22, Mid, 0),
create_dir_door(player, 'Sewers Water W', DoorType.Normal, Direction.West, 0x22, Bot, 0),
create_dir_door(player, 'Sewers Key Rat E', DoorType.Normal, Direction.East, 0x21, Bot, 0),
create_small_key_door(player, 'Sewers Key Rat Key Door N', DoorType.Normal, Direction.North, 0x21, Right, 0),
create_small_key_door(player, 'Sewers Secret Room Key Door S', DoorType.Normal, Direction.South, 0x11, Right, 0),
create_door(player, 'Sewers Secret Room Up Stairs', DoorType.SpiralStairs),
create_door(player, 'Sewers Pull Switch Down Stairs', DoorType.SpiralStairs),
create_dir_door(player, 'Sewers Pull Switch S', DoorType.Normal, Direction.South, 0x02, Mid, 0),
create_blocked_door(player, 'Sanctuary N', DoorType.Normal, Direction.North, 0x12, Mid, 0), # logically one way, but should be linked
# Eastern Palace
create_door(player, 'Eastern Lobby N', DoorType.Normal, Direction.North),
create_door(player, 'Eastern Cannonball S', DoorType.Normal, Direction.South),
create_door(player, 'Eastern Cannonball N', DoorType.Normal, Direction.North),
create_door(player, 'Eastern Cannonball Ledge WN', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Cannonball Ledge Key Door EN', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Courtyard Ledge S', DoorType.Normal, Direction.South),
create_door(player, 'Eastern Courtyard Ledge W', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Courtyard Ledge E', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Map Area W', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Compass Area E', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Compass Area EN', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Compass Area SW', DoorType.Normal, Direction.South), # one-way
create_door(player, 'Eastern Courtyard WN', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Courtyard EN', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Courtyard N', DoorType.Normal, Direction.North), # big key
create_door(player, 'Eastern Courtyard Potholes', DoorType.Hole, None),
create_door(player, 'Eastern Fairies\' Warp', DoorType.Warp, None),
create_door(player, 'Eastern Map Valley WN', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Map Valley SW', DoorType.Normal, Direction.South),
create_door(player, 'Eastern Dark Square NW', DoorType.Normal, Direction.North),
create_door(player, 'Eastern Dark Square Key Door WN', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Big Key EN', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Big Key NE', DoorType.Normal, Direction.North),
create_door(player, 'Eastern Darkness S', DoorType.Normal, Direction.South), # small key?
create_door(player, 'Eastern Darkness Up Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Eastern Attic Start Down Stairs', DoorType.SpiralStairs, None),
create_door(player, 'Eastern Attic Start WS', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Attic Switches ES', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Attic Switches WS', DoorType.Normal, Direction.West),
create_door(player, 'Eastern Eyegores ES', DoorType.Normal, Direction.East),
create_door(player, 'Eastern Eyegores NE', DoorType.Normal, Direction.North),
create_door(player, 'Eastern Boss SE', DoorType.Normal, Direction.South),
create_dir_door(player, 'Eastern Lobby N', DoorType.Normal, Direction.North, 0xc9, Mid, 0),
create_dir_door(player, 'Eastern Cannonball S', DoorType.Normal, Direction.South, 0xb9, Mid, 0),
create_dir_door(player, 'Eastern Cannonball N', DoorType.Normal, Direction.North, 0xb9, Mid, 0),
create_dir_door(player, 'Eastern Cannonball Ledge WN', DoorType.Normal, Direction.West, 0xb9, Top, 0),
create_small_key_door(player, 'Eastern Cannonball Ledge Key Door EN', DoorType.Normal, Direction.East, 0xb9, Top, 0),
create_dir_door(player, 'Eastern Courtyard Ledge S', DoorType.Normal, Direction.South, 0xa9, Mid, 0),
create_dir_door(player, 'Eastern Courtyard Ledge W', DoorType.Normal, Direction.West, 0xa9, Mid, 0),
create_dir_door(player, 'Eastern Courtyard Ledge E', DoorType.Normal, Direction.East, 0xa9, Mid, 0),
create_dir_door(player, 'Eastern Map Area W', DoorType.Normal, Direction.West, 0xaa, Mid, 0),
create_dir_door(player, 'Eastern Compass Area E', DoorType.Normal, Direction.East, 0xa8, Mid, 0),
create_dir_door(player, 'Eastern Compass Area EN', DoorType.Normal, Direction.East, 0xa8, Top, 0),
create_blocked_door(player, 'Eastern Compass Area SW', DoorType.Normal, Direction.South, 0xa8, Right, 0),
create_dir_door(player, 'Eastern Courtyard WN', DoorType.Normal, Direction.West, 0xa9, Top, 1),
create_dir_door(player, 'Eastern Courtyard EN', DoorType.Normal, Direction.East, 0xa9, Top, 1),
create_big_key_door(player, 'Eastern Courtyard N', DoorType.Normal, Direction.North, 0xa9, Mid, 0),
create_door(player, 'Eastern Courtyard Potholes', DoorType.Hole),
create_door(player, 'Eastern Fairies\' Warp', DoorType.Warp),
create_dir_door(player, 'Eastern Map Valley WN', DoorType.Normal, Direction.West, 0xaa, Top, 1),
create_dir_door(player, 'Eastern Map Valley SW', DoorType.Normal, Direction.South, 0xaa, Left, 0),
create_small_key_door(player, 'Eastern Dark Square NW', DoorType.Normal, Direction.North, 0xba, Left, 0),
create_small_key_door(player, 'Eastern Dark Square Key Door WN', DoorType.Normal, Direction.West, 0xba, Top, 0),
create_small_key_door(player, 'Eastern Big Key EN', DoorType.Normal, Direction.East, 0xb8, Top, 0),
create_dir_door(player, 'Eastern Big Key NE', DoorType.Normal, Direction.North, 0xb8, Right, 0),
create_small_key_door(player, 'Eastern Darkness S', DoorType.Normal, Direction.South, 0x99, Mid, 0),
create_door(player, 'Eastern Darkness Up Stairs', DoorType.SpiralStairs),
create_door(player, 'Eastern Attic Start Down Stairs', DoorType.SpiralStairs),
create_dir_door(player, 'Eastern Attic Start WS', DoorType.Normal, Direction.West, 0xda, Bot, 0),
create_dir_door(player, 'Eastern Attic Switches ES', DoorType.Normal, Direction.East, 0xd9, Bot, 0),
create_dir_door(player, 'Eastern Attic Switches WS', DoorType.Normal, Direction.West, 0xd9, Bot, 0),
create_dir_door(player, 'Eastern Eyegores ES', DoorType.Normal, Direction.East, 0xd8, Bot, 0),
create_dir_door(player, 'Eastern Eyegores NE', DoorType.Normal, Direction.North, 0xd8, Right, 0),
create_dir_door(player, 'Eastern Boss SE', DoorType.Normal, Direction.South, 0xd8, Right, 0),
]
def create_door(player, name, type, direction):
return Door(player, name, type, direction)
def create_door(player, name, type):
return Door(player, name, type, None, None, None, None)
def create_small_key_door(player, name, type, direction, room, doorIndex, layer):
d = Door(player, name, type, direction, room, doorIndex, layer)
d.smallKey = True
return d
def create_big_key_door(player, name, type, direction, room, doorIndex, layer):
d = Door(player, name, type, direction, room, doorIndex, layer)
d.bigKey = True
return d
def create_blocked_door(player, name, type, direction, room, doorIndex, layer):
d = Door(player, name, type, direction, room, doorIndex, layer)
d.blocked = True
return d
def create_dir_door(player, name, type, direction, room, doorIndex, layer):
return Door(player, name, type, direction, room, doorIndex, layer)
def create_toggle_door(player, name, type, direction, room, doorIndex, layer):
return Door(player, name, type, direction, room, doorIndex, layer, True)

View File

@@ -118,7 +118,7 @@ def start():
parser.add_argument('--algorithm', default='balanced', const='balanced', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'],
help='''\
Select item filling algorithm. (default: %(default)s
balanced: vt26 derivitive that aims to strike a balance between
balanced: vt26 derivative that aims to strike a balance between
the overworld heavy vt25 and the dungeon heavy vt26
algorithm.
vt26: Shuffle items and place them in a random location
@@ -162,6 +162,17 @@ def start():
The dungeon variants only mix up dungeons and keep the rest of
the overworld vanilla.
''')
parser.add_argument('--door_shuffle', default='vanilla', const='vanilla', nargs='?', choices=['vanilla', 'basic', 'crossed', 'experimental'],
help='''\
Select Door Shuffling Algorithm. (default: %(default)s)
Basic: Doors are mixed within a single dungeon.
(Not yet implemented)
Crossed: Doors are mixed between all dungeons.
(Not yet implemented)
Vanilla: All doors are connected the same way the were in the
base game.
Experimental: Experimental mixes live here. Use at your own risk.
''')
parser.add_argument('--crystals_ganon', default='7', const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'],
help='''\
How many crystals are needed to defeat ganon. Any other

13
Gui.py
View File

@@ -18,7 +18,7 @@ from Utils import is_bundled, local_path, output_path, open_file
def guiMain(args=None):
mainWindow = Tk()
mainWindow.wm_title("Entrance Shuffle %s" % ESVersion)
mainWindow.wm_title("Door Shuffle %s" % ESVersion)
set_icon(mainWindow)
@@ -203,6 +203,14 @@ def guiMain(args=None):
shuffleLabel = Label(shuffleFrame, text='Entrance shuffle algorithm')
shuffleLabel.pack(side=LEFT)
doorShuffleFrame = Frame(drowDownFrame)
doorShuffleVar = StringVar()
doorShuffleVar.set('vanilla')
doorShuffleOptionMenu = OptionMenu(doorShuffleFrame, doorShuffleVar, 'vanilla', 'basic', 'crosssed', 'experimental')
doorShuffleOptionMenu.pack(side=RIGHT)
doorShuffleLabel = Label(shuffleFrame, text='Door shuffle algorithm')
doorShuffleLabel.pack(side=LEFT)
heartbeepFrame = Frame(drowDownFrame)
heartbeepVar = StringVar()
heartbeepVar.set('normal')
@@ -235,6 +243,7 @@ def guiMain(args=None):
progressiveFrame.pack(expand=True, anchor=E)
algorithmFrame.pack(expand=True, anchor=E)
shuffleFrame.pack(expand=True, anchor=E)
doorShuffleFrame.pack(expand=True, anchor=E)
heartbeepFrame.pack(expand=True, anchor=E)
heartcolorFrame.pack(expand=True, anchor=E)
fastMenuFrame.pack(expand=True, anchor=E)
@@ -320,6 +329,7 @@ def guiMain(args=None):
guiargs.progressive = progressiveVar.get()
guiargs.algorithm = algorithmVar.get()
guiargs.shuffle = shuffleVar.get()
guiargs.door_shuffle = doorShuffleVar.get()
guiargs.heartbeep = heartbeepVar.get()
guiargs.heartcolor = heartcolorVar.get()
guiargs.fastmenu = fastMenuVar.get()
@@ -1080,6 +1090,7 @@ def guiMain(args=None):
goalVar.set(args.goal)
algorithmVar.set(args.algorithm)
shuffleVar.set(args.shuffle)
doorShuffleVar.set(args.door_shuffle)
heartbeepVar.set(args.heartbeep)
fastMenuVar.set(args.fastmenu)
logicVar.set(args.logic)

View File

@@ -26,7 +26,7 @@ def main(args, seed=None):
start = time.clock()
# initialize the world
world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.accessibility, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints)
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.accessibility, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints)
logger = logging.getLogger('')
if seed is None:
random.seed(None)
@@ -129,7 +129,7 @@ def main(args, seed=None):
else:
sprite = None
outfilebase = 'ER_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s_%s' % (world.logic, world.difficulty, world.difficulty_adjustments, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-nohints" if not world.hints else "", world.seed)
outfilebase = 'DR_%s_%s-%s-%s-%s%s_%s-%s_%s%s%s%s%s_%s' % (world.logic, world.difficulty, world.difficulty_adjustments, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, world.doorShuffle, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-nohints" if not world.hints else "", world.seed)
use_enemizer = args.enemizercli and (args.shufflebosses != 'none' or args.shuffleenemies or args.enemy_health != 'default' or args.enemy_health != 'default' or args.enemy_damage or args.shufflepalette or args.shufflepots)
@@ -192,7 +192,7 @@ def gt_filler(world):
def copy_world(world):
# ToDo: Not good yet
ret = World(world.players, world.shuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.accessibility, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.retro, world.custom, world.customitemarray, world.boss_shuffle, world.hints)
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.accessibility, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.retro, world.custom, world.customitemarray, world.boss_shuffle, world.hints)
ret.required_medallions = world.required_medallions.copy()
ret.swamp_patch_required = world.swamp_patch_required.copy()
ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()

View File

@@ -289,9 +289,9 @@ def create_regions(world, player):
create_dungeon_region(player, 'Hyrule Castle West Lobby', 'A dungeon', None, ['Hyrule Castle West Lobby E', 'Hyrule Castle West Lobby N',
'Hyrule Castle West Lobby EN', 'Hyrule Castle Exit (West)']),
create_dungeon_region(player, 'Hyrule Castle East Lobby', 'A dungeon', None, ['Hyrule Castle East Lobby W', 'Hyrule Castle East Lobby N',
'Hyrule Castle East Lobby NE', 'Hyrule Castle Exit (East)']),
'Hyrule Castle East Lobby NW', 'Hyrule Castle Exit (East)']),
create_dungeon_region(player, 'Hyrule Castle East Hall', 'A dungeon', None, ['Hyrule Castle East Hall W', 'Hyrule Castle East Hall S',
'Hyrule Castle East Hall SE']),
'Hyrule Castle East Hall SW']),
create_dungeon_region(player, 'Hyrule Castle West Hall', 'A dungeon', None, ['Hyrule Castle West Hall E', 'Hyrule Castle West Hall S']),
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']),

8
Rom.py
View File

@@ -7,7 +7,7 @@ import random
import struct
import subprocess
from BaseClasses import ShopType, Region, Location, Item
from BaseClasses import ShopType, Region, Location, Item, DoorType
from Dungeons import dungeon_music_addresses
from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable
from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts
@@ -533,6 +533,12 @@ def patch_rom(world, player, rom):
if world.mode == 'inverted':
patch_shuffled_dark_sanc(world, rom, player)
# patch doors
if world.doorShuffle != 'vanilla':
for door in world.doors:
if door.dest is not None and door.player == player and door.type == DoorType.Normal:
rom.write_bytes(door.getAddress(), door.dest.getTarget(door.toggle))
write_custom_shops(rom, world, player)
# patch medallion requirements