Skull Woods added

Fixed polarity for stairs
Some swamp fixes
Prep work for dungeons that can require traversal through overworld - like skull woods
Special case for pinball room so it can be in Skull 2 or Skull 1
This commit is contained in:
aerinon
2019-10-17 16:35:13 -06:00
parent 7db3c82e70
commit beb15951a0
9 changed files with 397 additions and 111 deletions

View File

@@ -88,6 +88,8 @@ class World(object):
self.paired_doors = {}
self.rooms = []
self._room_cache = {}
self.dungeon_layouts = {}
self.inaccessible_regions = []
def intialize_regions(self):
for region in self.regions:
@@ -862,17 +864,52 @@ class Direction(Enum):
Down = 5
class Polarity:
def __init__(self, vector):
self.vector = vector
def __add__(self, other):
result = Polarity([0]*len(self.vector))
for i in range(len(self.vector)):
result.vector[i] = pol_add[pol_idx_2[i]](self.vector[i], other.vector[i])
return result
def __iadd__(self, other):
for i in range(len(self.vector)):
self.vector[i] = pol_add[pol_idx_2[i]](self.vector[i], other.vector[i])
return self
def __getitem__(self, item):
return self.vector[item]
def is_neutral(self):
for i in range(len(self.vector)):
if self.vector[i] != 0:
return False
return True
pol_idx = {
Direction.North: (0, 'Pos'),
Direction.South: (0, 'Neg'),
Direction.East: (1, 'Pos'),
Direction.West: (1, 'Neg'),
Direction.Up: (2, 'Pos'),
Direction.Down: (2, 'Neg')
Direction.Up: (2, 'Mod'),
Direction.Down: (2, 'Mod')
}
pol_idx_2 = {
0: 'Add',
1: 'Add',
2: 'Mod'
}
pol_inc = {
'Pos': lambda x: x + 1,
'Neg': lambda x: x - 1,
'Mod': lambda x: (x + 1) % 2
}
pol_add = {
'Add': lambda x, y: x + y,
'Mod': lambda x, y: (x + y) % 2
}
@unique
@@ -1000,11 +1037,11 @@ class Sector(object):
# todo: make these lazy init? - when do you invalidate them
def polarity(self):
polarity = [0, 0, 0]
pol = Polarity([0, 0, 0])
for door in self.outstanding_doors:
idx, inc = pol_idx[door.direction]
polarity[idx] = pol_inc[inc](polarity[idx])
return polarity
pol.vector[idx] = pol_inc[inc](pol.vector[idx])
return pol
def magnitude(self):
magnitude = [0, 0, 0]

View File

@@ -4,7 +4,7 @@ import logging
import operator as op
from functools import reduce
from BaseClasses import RegionType, DoorType, Direction, Sector, CrystalBarrier, pol_idx
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier, Polarity, pol_idx
from Dungeons import hyrule_castle_regions, eastern_regions, desert_regions, hera_regions, tower_regions, pod_regions
from Dungeons import dungeon_regions, region_starts, split_region_starts
from Regions import key_only_locations, dungeon_events
@@ -94,11 +94,11 @@ 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 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)
# 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
@@ -412,11 +412,12 @@ def cross_dungeon(world, player):
combine_layouts(dungeon_layouts)
for layout in dungeon_layouts:
shuffle_key_doors(layout[1], layout[2], world, player)
shuffle_key_doors(layout[0], layout[1], world, player)
def experiment(world, player):
fix_big_key_doors_with_ugly_smalls(world, player)
overworld_prep(world, player)
dungeon_sectors = []
for key in dungeon_regions.keys():
sector_list = convert_to_sectors(dungeon_regions[key], world, player)
@@ -434,10 +435,17 @@ def experiment(world, player):
dungeon_layouts.append((ds, entrance_list))
combine_layouts(dungeon_layouts)
world.dungeon_layouts[player] = {}
for sector, entrances in dungeon_layouts:
world.dungeon_layouts[player][sector.name] = (sector, entrances)
remove_inaccessible_entrances(world, player)
paths = determine_required_paths(world)
check_required_paths(paths, world, player)
# shuffle_key_doors for dungeons
for layout in dungeon_layouts:
shuffle_key_doors(layout[0], layout[1], world, player)
for sector, entrances in world.dungeon_layouts[player].values():
shuffle_key_doors(sector, entrances, world, player)
def convert_regions(region_names, world, player):
@@ -528,18 +536,11 @@ def sum_vector(sector_list, func):
return result
def add_vectors(vector_one, vector_two):
result = [0]*len(vector_one)
for i in range(len(result)):
result[i] = vector_one[i] + vector_two[i]
return result
def is_polarity_neutral(polarity):
for value in polarity:
if value != 0:
return False
return True
def is_polarity_neutral(sector_list):
pol = Polarity([0, 0, 0])
for sector in sector_list:
pol += sector.polarity()
return pol.is_neutral()
search_iterations = 0
@@ -561,7 +562,7 @@ def is_proposal_valid(proposal, buckets, candidates):
for i in range(len(proposal)):
test_bucket[proposal[i]].append(candidates[i])
for test in test_bucket:
valid = is_polarity_neutral(sum_vector(test, lambda s: s.polarity()))
valid = is_polarity_neutral(test)
if not valid:
return False
return True
@@ -744,13 +745,13 @@ class ExplorationState(object):
def add_all_doors_check_key_region(self, region, key_region, world, player):
for door in get_doors(world, region, player):
if door.name not in self.door_krs.keys():
self.door_krs[door.name] = key_region
if self.can_traverse(door):
if door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door, self.event_doors):
self.append_door_to_list(door, self.event_doors)
elif not self.in_door_list(door, self.avail_doors):
self.append_door_to_list(door, self.avail_doors)
if door.name not in self.door_krs.keys():
self.door_krs[door.name] = key_region
def add_all_doors_check_keys(self, region, key_door_proposal, world, player):
for door in get_doors(world, region, player):
@@ -776,6 +777,9 @@ class ExplorationState(object):
return region in self.visited_blue
return False
def visited_at_all(self, region):
return region in self.visited_blue or region in self.visited_orange
def can_traverse(self, door):
if door.blocked:
return False
@@ -783,6 +787,9 @@ class ExplorationState(object):
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
return True
def validate(self, door, region, world):
return self.can_traverse(door) and not self.visited(region) and valid_region_to_explore(region, world)
def in_door_list(self, door, door_list):
for d in door_list:
if d.door == door and d.crystal == self.crystal:
@@ -821,7 +828,7 @@ def extend_reachable_state(search_regions, state, world, player):
entrance = world.get_entrance(explorable_door.door.name, player)
connect_region = entrance.connected_region
if connect_region is not None:
if not local_state.visited(connect_region):
if valid_region_to_explore(connect_region, world) and not local_state.visited(connect_region):
local_state.visit_region(connect_region)
local_state.add_all_doors_check_unattached(connect_region, world, player)
return local_state
@@ -847,6 +854,9 @@ def shuffle_dungeon_no_repeats_new(world, player, available_sectors, entrance_re
explorable_door = random.choice(state.unattached_doors)
door = explorable_door.door
sector = find_sector_for_door(door, available_sectors)
if sector is None:
state.unattached_doors.remove(explorable_door)
continue
sector.outstanding_doors.remove(door)
# door_connected = False
logger.info('Linking %s', door.name)
@@ -872,7 +882,6 @@ def shuffle_dungeon_no_repeats_new(world, player, available_sectors, entrance_re
break # skips else block below
logger.info(' Not Linking %s to %s', door.name, connect_door.name)
if len(compatibles) == 0: # time to try again
sector.outstanding_doors.insert(0, door)
if len(state.unattached_doors) <= 1:
raise Exception('Rejected last option due to dead end... infinite loop ensues')
else:
@@ -924,6 +933,9 @@ def shuffle_dungeon_no_repeats(world, player, available_sectors, entrance_region
# Pick a random available door to connect
door = random.choice(reachable_doors)
sector = find_sector_for_door(door, available_sectors)
if sector is None:
reachable_doors.remove(door)
continue
sector.outstanding_doors.remove(door)
# door_connected = False
logger.info('Linking %s', door.name)
@@ -949,7 +961,6 @@ def shuffle_dungeon_no_repeats(world, player, available_sectors, entrance_region
break # skips else block below
logger.info(' Not Linking %s to %s', door.name, connect_door.name)
if len(compatibles) == 0: # time to try again
sector.outstanding_doors.insert(0, door)
if len(reachable_doors) <= 1:
raise Exception('Rejected last option due to dead end... infinite loop ensues')
else:
@@ -981,6 +992,7 @@ def shuffle_dungeon_no_repeats(world, player, available_sectors, entrance_region
# Check that we used everything, we failed otherwise
if len(available_sectors) != 1:
logger.warning('Failed to add all regions/doors to dungeon, generation will likely fail.')
return available_sectors
return available_sectors[0]
@@ -994,7 +1006,7 @@ def extend_reachable(search_regions, visited_regions, world, player):
for ext in region.exits:
if ext.connected_region is not None:
connect_region = ext.connected_region
if connect_region not in visited_regions and connect_region not in region_list:
if valid_region_to_explore(connect_region, world) and connect_region not in visited_regions and connect_region not in region_list:
region_list.append(connect_region)
else:
door = world.check_for_door(ext.name, player)
@@ -1003,6 +1015,10 @@ def extend_reachable(search_regions, visited_regions, world, player):
return reachable_doors, visited_regions
def valid_region_to_explore(region, world):
return region.type == RegionType.Dungeon or region.name in world.inaccessible_regions
def find_sector_for_door(door, sectors):
for sector in sectors:
if door in sector.outstanding_doors:
@@ -1260,7 +1276,7 @@ def validate_key_layout_r(state, flat_proposal, checked_states, world, player):
exp_door = state.next_avail_door()
door = exp_door.door
connect_region = world.get_entrance(door.name, player).connected_region
if state.can_traverse(door) and not state.visited(connect_region):
if state.validate(door, connect_region, world):
state.visit_region(connect_region, key_checks=True)
state.add_all_doors_check_keys(connect_region, flat_proposal, world, player)
smalls_avail = len(state.small_doors) > 0
@@ -1380,6 +1396,142 @@ def change_door_to_small_key(d, world, player):
room.change(d.doorListPos, DoorKind.SmallKey)
def remove_inaccessible_entrances(world, player):
if world.shuffle == 'vanilla':
for dungeon_name in world.dungeon_layouts[player].keys():
sector, entrances = world.dungeon_layouts[player][dungeon_name]
if dungeon_name == 'Skull Woods':
entrances.remove('Skull 2 West Lobby')
entrances.remove('Skull 3 Lobby')
entrances.remove('Skull Back Drop')
if world.mode == 'standard' and dungeon_name == 'Hyrule Castle':
entrances.remove('Hyrule Castle West Lobby')
entrances.remove('Hyrule Castle East Lobby')
entrances.remove('Sewers Secret Room')
entrances.remove('Sanctuary')
# todo - not sure about what to do in entrance shuffle - tbh
# simple and restricted have interesting effects
def determine_required_paths(world):
paths = {
'Hyrule Castle': [],
'Eastern Palace': ['Eastern Boss'],
'Desert Palace': ['Desert Boss'],
'Tower of Hera': ['Hera Boss'],
'Agahnims Tower': ['Tower Agahnim 1'],
'Palace of Darkness': ['PoD Boss'],
'Swamp Palace': ['Swamp Boss'],
'Skull Woods': ['Skull Boss'],
# 'Thieves Town': [],
}
if world.shuffle == 'vanilla':
# paths['Skull Woods'].remove('Skull Boss') # is this necessary?
paths['Skull Woods'].insert(0, 'Skull 2 West Lobby')
# todo - TR jazz
if world.mode == 'standard':
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
paths['Hyrule Castle'].append('Sanctuary')
return paths
def overworld_prep(world, player):
if world.mode != 'inverted':
if world.mode == 'standard':
world.inaccessible_regions.append('Hyrule Castle Ledge') # maybe only with er off
world.inaccessible_regions.append('Skull Woods Forest (West)')
world.inaccessible_regions.append('Dark Death Mountain Ledge')
world.inaccessible_regions.append('Dark Death Mountain Isolated Ledge')
world.inaccessible_regions.append('Desert Palace Lone Stairs')
world.inaccessible_regions.append('Bumper Cave Ledge')
world.inaccessible_regions.append('Death Mountain Floating Island (Dark World)')
else:
world.inaccessible_regions.append('Desert Ledge')
# world.inaccessible_regions.append('Hyrule Castle Ledge') # accessible via aga 1?
world.inaccessible_regions.append('Desert Palace Lone Stairs')
world.inaccessible_regions.append('Death Mountain Return Ledge')
world.inaccessible_regions.append('Maze Race Ledge')
if world.shuffle == 'vanilla':
skull_doors = [
Door(player, 'Skull Woods Second Section Exit (West)', DoorType.Logical),
Door(player, 'Skull Woods Second Section Door (West)', DoorType.Logical),
Door(player, 'Skull Woods Second Section Hole', DoorType.Logical),
Door(player, 'Skull Woods Final Section', DoorType.Logical)
]
world.doors += skull_doors
connect_simple_door(world, skull_doors[0].name, 'Skull Woods Forest (West)', player)
connect_simple_door(world, skull_doors[1].name, 'Skull 2 West Lobby', player)
connect_simple_door(world, skull_doors[2].name, 'Skull Back Drop', player)
connect_simple_door(world, skull_doors[3].name, 'Skull 3 Lobby', player)
if world.mode == 'standard':
castle_doors = [
Door(player, 'Hyrule Castle Exit (West)', DoorType.Logical),
Door(player, 'Hyrule Castle Exit (East)', DoorType.Logical),
Door(player, 'Hyrule Castle Entrance (East)', DoorType.Logical),
Door(player, 'Hyrule Castle Entrance (West)', DoorType.Logical)
]
world.doors += castle_doors
connect_simple_door(world, castle_doors[0].name, 'Hyrule Castle Ledge', player)
connect_simple_door(world, castle_doors[1].name, 'Hyrule Castle Ledge', player)
connect_simple_door(world, castle_doors[2].name, 'Hyrule Castle East Lobby', player)
connect_simple_door(world, castle_doors[3].name, 'Hyrule Castle West Lobby', player)
def check_required_paths(paths, world, player):
for dungeon_name in paths.keys():
sector, entrances = world.dungeon_layouts[player][dungeon_name]
if len(paths[dungeon_name]) > 0:
check_paths = convert_regions(paths[dungeon_name], world, player)
start_regions = convert_regions(entrances, world, player)
state = ExplorationState()
for region in start_regions:
state.visit_region(region)
state.add_all_doors_check_unattached(region, world, player)
explore_state(state, world, player)
valid, bad_region = check_if_regions_visited(state, check_paths)
if not valid:
if check_for_pinball_fix(state, bad_region, world, player):
explore_state(state, world, player)
valid, bad_region = check_if_regions_visited(state, check_paths)
if not valid:
raise Exception('% cannot reach %', dungeon_name, bad_region.name)
def explore_state(state, world, player):
while len(state.avail_doors) > 0:
door = state.next_avail_door().door
connect_region = world.get_entrance(door.name, player).connected_region
if state.can_traverse(door) and not state.visited(connect_region) and valid_region_to_explore(connect_region, world):
state.visit_region(connect_region)
state.add_all_doors_check_unattached(connect_region, world, player)
def check_if_regions_visited(state, check_paths):
valid = True
breaking_region = None
for region_target in check_paths:
if not state.visited_at_all(region_target):
valid = False
breaking_region = region_target
break
return valid, breaking_region
def check_for_pinball_fix(state, bad_region, world, player):
pinball_region = world.get_region('Skull Pinball', player)
if bad_region.name == 'Skull 2 West Lobby' and state.visited_at_all(pinball_region):
door = world.get_door('Skull Pinball WS', player)
room = world.get_room(door.roomIndex, player)
if room.doorList[door.doorListPos][1] == DoorKind.Trap:
room.change(door.doorListPos, DoorKind.Normal)
door.trapFlag = 0x0
door.blocked = False
connect_two_way(world, door.name, door.dest.name, player)
state.add_all_doors_check_unattached(pinball_region, world, player)
return True
return False
# DATA GOES DOWN HERE
logical_connections = [
@@ -1415,6 +1567,7 @@ logical_connections = [
('Swamp Trench 2 Blocks Pots', 'Swamp Trench 2 Pots'),
('Swamp Trench 2 Departure Wet', 'Swamp Trench 2 Pots'),
('Swamp West Shallows Push Blocks', 'Swamp West Block Path'),
('Swamp West Block Path Drop Down', 'Swamp West Shallows'),
('Swamp West Ledge Drop Down', 'Swamp West Shallows'),
('Swamp West Ledge Hook Path', 'Swamp Barrier Ledge'),
('Swamp Barrier Ledge Drop Down', 'Swamp West Shallows'),
@@ -1424,6 +1577,9 @@ logical_connections = [
('Swamp Drain Right Switch', 'Swamp Drain Left'),
('Swamp Flooded Spot Ladder', 'Swamp Flooded Room'),
('Swamp Flooded Room Ladder', 'Swamp Flooded Spot'),
('Skull Pot Circle Star Path', 'Skull Map Room'),
('Skull Big Chest Hookpath', 'Skull 1 Lobby'),
('Skull Back Drop Star Path', 'Skull Small Hall'),
# ('', ''),
]
@@ -1500,7 +1656,7 @@ falldown_pits = [
('PoD Big Key Landing Hole', 'PoD Stalfos Basement'),
('Swamp Attic Right Pit', 'Swamp Barrier Ledge'),
('Swamp Attic Left Pit', 'Swamp West Ledge'),
# ('', ''),
('Skull Final Drop Hole', 'Skull Boss'),
# ('', ''),
]
@@ -1575,6 +1731,19 @@ interior_doors = [
('Swamp C SE', 'Swamp Waterway NE'),
('Swamp Waterway N', 'Swamp I S'),
('Swamp Waterway NW', 'Swamp T SW'),
('Skull 1 Lobby ES', 'Skull Map Room WS'),
('Skull Pot Circle WN', 'Skull Pull Switch EN'),
('Skull Pull Switch S', 'Skull Big Chest N'),
('Skull Left Drop ES', 'Skull Compass Room WS'),
('Skull 2 East Lobby NW', 'Skull Big Key SW'),
('Skull Big Key WN', 'Skull Lone Pot EN'),
('Skull Small Hall WS', 'Skull 2 West Lobby ES'),
('Skull 2 West Lobby NW', 'Skull X Room SW'),
('Skull 3 Lobby WN', 'Skull East Bridge EN'),
('Skull East Bridge ES', 'Skull West Bridge Nook WS'),
('Skull Star Pits WS', 'Skull Torch Room ES'),
('Skull Torch Room EN', 'Skull Vines WN'),
('Skull Spike Corner WS', 'Skull Final Drop ES'),
# ('', ''),
]
@@ -1646,9 +1815,13 @@ default_door_connections = [
('Swamp Trench 2 Departure WS', 'Swamp West Shallows ES'),
('Swamp Big Key Ledge WN', 'Swamp Barrier EN'),
('Swamp Basement Shallows NW', 'Swamp Waterfall Room SW'),
# ('', ''),
# ('', ''),
# ('', ''),
('Skull 1 Lobby WS', 'Skull Pot Prison ES'),
('Skull Map Room SE', 'Skull Pinball NE'),
('Skull Pinball WS', 'Skull Compass Room ES'),
('Skull Compass Room NE', 'Skull Pot Prison SE'),
('Skull 2 East Lobby WS', 'Skull Small Hall ES'),
('Skull 3 Lobby NW', 'Skull Star Pits SW'),
('Skull Vines NW', 'Skull Spike Corner SW'),
# ('', ''),
]

View File

@@ -323,6 +323,8 @@ def create_doors(world, player):
create_door(player, 'PoD Mimics 2 SW', Nrml).dir(Direction.South, 0x1b, Left, High).pos(1),
create_door(player, 'PoD Mimics 2 NW', Intr).dir(Direction.North, 0x1b, Left, High).pos(0),
create_door(player, 'PoD Bow Statue SW', Intr).dir(Direction.South, 0x1b, Left, High).pos(0),
# 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
create_door(player, 'PoD Bow Statue Down Ladder', Lddr),
create_door(player, 'PoD Dark Pegs Up Ladder', Lddr),
create_door(player, 'PoD Dark Pegs WN', Intr).dir(Direction.West, 0x0b, Mid, High).small_key().pos(2),
@@ -390,6 +392,7 @@ def create_doors(world, player):
create_door(player, 'Swamp West Shallows ES', Nrml).dir(Direction.East, 0x34, Bot, High).pos(1),
create_door(player, 'Swamp West Shallows Push Blocks', Lgcl),
create_door(player, 'Swamp West Block Path Up Stairs', Sprl).dir(Direction.Up, 0x34, 0, HTH).ss(Z, 0x1b, 0x6c, False, True),
create_door(player, 'Swamp West Block Path Drop Down', Lgcl),
create_door(player, 'Swamp West Ledge Drop Down', Lgcl),
create_door(player, 'Swamp West Ledge Hook Path', Lgcl),
create_door(player, 'Swamp Barrier Ledge Drop Down', Lgcl),
@@ -436,6 +439,51 @@ def create_doors(world, player):
create_door(player, 'Swamp T SW', Intr).dir(Direction.South, 0x16, Left, High).small_key().pos(1),
create_door(player, 'Swamp T NW', Nrml).dir(Direction.North, 0x16, Left, High).pos(3),
create_door(player, 'Swamp Boss SW', Nrml).dir(Direction.South, 0x06, Left, High).trap(0x4).pos(0),
create_door(player, 'Skull 1 Lobby WS', Nrml).dir(Direction.West, 0x58, Bot, High).small_key().pos(1),
create_door(player, 'Skull 1 Lobby ES', Intr).dir(Direction.East, 0x58, Bot, High).pos(5),
create_door(player, 'Skull Map Room WS', Intr).dir(Direction.West, 0x58, Bot, High).pos(5),
create_door(player, 'Skull Map Room SE', Nrml).dir(Direction.South, 0x58, Right, High).small_key().pos(2),
create_door(player, 'Skull Pot Circle WN', Intr).dir(Direction.West, 0x58, Top, High).pos(3),
create_door(player, 'Skull Pull Switch EN', Intr).dir(Direction.East, 0x58, Top, High).pos(3),
create_door(player, 'Skull Pot Circle Star Path', Lgcl),
create_door(player, 'Skull Pull Switch S', Intr).dir(Direction.South, 0x58, Left, High).pos(0),
create_door(player, 'Skull Big Chest N', Intr).dir(Direction.North, 0x58, Left, High).no_exit().pos(0),
create_door(player, 'Skull Big Chest Hookpath', Lgcl),
create_door(player, 'Skull Pinball NE', Nrml).dir(Direction.North, 0x68, Right, High).small_key().pos(1),
create_door(player, 'Skull Pinball WS', Nrml).dir(Direction.West, 0x68, Bot, High).no_exit().trap(0x4).pos(0),
create_door(player, 'Skull Compass Room NE', Nrml).dir(Direction.North, 0x67, Right, High).pos(0),
create_door(player, 'Skull Compass Room ES', Nrml).dir(Direction.East, 0x67, Bot, High).pos(2),
create_door(player, 'Skull Left Drop ES', Intr).dir(Direction.East, 0x67, Bot, High).pos(1),
create_door(player, 'Skull Compass Room WS', Intr).dir(Direction.West, 0x67, Bot, High).pos(1),
create_door(player, 'Skull Pot Prison ES', Nrml).dir(Direction.East, 0x57, Bot, High).small_key().pos(2),
create_door(player, 'Skull Pot Prison SE', Nrml).dir(Direction.South, 0x57, Right, High).pos(5),
create_door(player, 'Skull 2 East Lobby WS', Nrml).dir(Direction.West, 0x57, Bot, High).pos(4),
create_door(player, 'Skull 2 East Lobby NW', Intr).dir(Direction.North, 0x57, Left, High).pos(1),
create_door(player, 'Skull Big Key SW', Intr).dir(Direction.South, 0x57, Left, High).pos(1),
create_door(player, 'Skull Big Key WN', Intr).dir(Direction.West, 0x57, Top, High).pos(0),
create_door(player, 'Skull Lone Pot EN', Intr).dir(Direction.East, 0x57, Top, High).pos(0),
create_door(player, 'Skull Small Hall ES', Nrml).dir(Direction.East, 0x56, Bot, High).pos(3),
create_door(player, 'Skull Small Hall WS', Intr).dir(Direction.West, 0x56, Bot, High).pos(2),
create_door(player, 'Skull 2 West Lobby ES', Intr).dir(Direction.East, 0x56, Bot, High).pos(2),
create_door(player, 'Skull 2 West Lobby NW', Intr).dir(Direction.North, 0x56, Left, High).small_key().pos(0),
create_door(player, 'Skull X Room SW', Intr).dir(Direction.South, 0x56, Left, High).small_key().pos(0),
create_door(player, 'Skull Back Drop Star Path', Lgcl),
create_door(player, 'Skull 3 Lobby NW', Nrml).dir(Direction.North, 0x59, Left, High).small_key().pos(0),
create_door(player, 'Skull 3 Lobby WN', Intr).dir(Direction.West, 0x59, Top, High).pos(2),
create_door(player, 'Skull East Bridge EN', Intr).dir(Direction.East, 0x59, Top, High).pos(2),
create_door(player, 'Skull East Bridge ES', Intr).dir(Direction.East, 0x59, Bot, High).pos(3),
create_door(player, 'Skull West Bridge Nook WS', Intr).dir(Direction.West, 0x59, Bot, High).pos(3),
create_door(player, 'Skull Star Pits SW', Nrml).dir(Direction.South, 0x49, Left, High).small_key().pos(2),
create_door(player, 'Skull Star Pits WS', Intr).dir(Direction.West, 0x49, Bot, High).pos(3),
create_door(player, 'Skull Torch Room ES', Intr).dir(Direction.East, 0x49, Bot, High).pos(3),
create_door(player, 'Skull Torch Room EN', Intr).dir(Direction.East, 0x49, Top, High).pos(1),
create_door(player, 'Skull Vines WN', Intr).dir(Direction.West, 0x49, Top, High).pos(1),
create_door(player, 'Skull Vines NW', Nrml).dir(Direction.North, 0x49, Left, High).pos(0),
create_door(player, 'Skull Spike Corner SW', Nrml).dir(Direction.South, 0x39, Left, High).no_exit().trap(0x4).pos(0),
create_door(player, 'Skull Spike Corner WS', Intr).dir(Direction.West, 0x39, Bot, High).small_key().pos(1),
create_door(player, 'Skull Final Drop ES', Intr).dir(Direction.East, 0x39, Bot, High).small_key().pos(1),
create_door(player, 'Skull Final Drop Hole', Hole),
]
create_paired_doors(world, player)
@@ -511,9 +559,9 @@ def create_paired_doors(world, player):
# PairedDoor('', ''), # GT moldorm key door
# PairedDoor('', ''), # Ice BJ key door
PairedDoor('Desert Tiles 2 SE', 'Desert Beamos Hall NE'),
# PairedDoor('', ''), # Skull 3 key door
# PairedDoor('', ''), # Skull 1 key door - pot prison to big chest
# PairedDoor('', ''), # Skull 1 - pinball key door
PairedDoor('Skull 3 Lobby NW', 'Skull Star Pits SW'), # Skull 3 key door
PairedDoor('Skull 1 Lobby WS', 'Skull Pot Prison ES'), # Skull 1 key door - pot prison to big chest
PairedDoor('Skull Map Room SE', 'Skull Pinball NE'), # Skull 1 - pinball key door
# PairedDoor('', ''), # gt main big key door
# PairedDoor('', ''), # ice door to spike chest
# PairedDoor('', ''), # gt right side key door to cape bridge

View File

@@ -20,10 +20,8 @@ def create_dungeons(world, player):
DP = make_dungeon('Desert Palace', 'Lanmolas', desert_regions, ItemFactory('Big Key (Desert Palace)', player), [ItemFactory('Small Key (Desert Palace)', player)], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
ToH = make_dungeon('Tower of Hera', 'Moldorm', hera_regions, ItemFactory('Big Key (Tower of Hera)', player), [ItemFactory('Small Key (Tower of Hera)', player)], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
PoD = make_dungeon('Palace of Darkness', 'Helmasaur King', pod_regions, ItemFactory('Big Key (Palace of Darkness)', player), ItemFactory(['Small Key (Palace of Darkness)'] * 6, player), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], player))
#still standard dungeons
TT = make_dungeon('Thieves Town', 'Blind', ['Thieves Town (Entrance)', 'Thieves Town (Deep)', 'Blind Fight'], ItemFactory('Big Key (Thieves Town)', player), [ItemFactory('Small Key (Thieves Town)', player)], ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'], player))
SW = make_dungeon('Skull Woods', 'Mothula', ['Skull Woods Final Section (Entrance)', 'Skull Woods First Section', 'Skull Woods Second Section', 'Skull Woods Second Section (Drop)', 'Skull Woods Final Section (Mothula)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)'], ItemFactory('Big Key (Skull Woods)', player), ItemFactory(['Small Key (Skull Woods)'] * 2, player), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player))
SW = make_dungeon('Skull Woods', 'Mothula', skull_regions, ItemFactory('Big Key (Skull Woods)', player), ItemFactory(['Small Key (Skull Woods)'] * 2, player), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player))
SP = make_dungeon('Swamp Palace', 'Arrghus', swamp_regions, ItemFactory('Big Key (Swamp Palace)', player), [ItemFactory('Small Key (Swamp Palace)', player)], ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], player))
IP = make_dungeon('Ice Palace', 'Kholdstare', ['Ice Palace (Entrance)', 'Ice Palace (Main)', 'Ice Palace (East)', 'Ice Palace (East Top)', 'Ice Palace (Kholdstare)'], ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
MM = make_dungeon('Misery Mire', 'Vitreous', ['Misery Mire (Entrance)', 'Misery Mire (Main)', 'Misery Mire (West)', 'Misery Mire (Final Area)', 'Misery Mire (Vitreous)'], ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
@@ -226,6 +224,14 @@ swamp_regions = [
'Swamp Refill', 'Swamp Behind Waterfall', 'Swamp C', 'Swamp Waterway', 'Swamp I', 'Swamp T', 'Swamp Boss'
]
skull_regions = [
'Skull 1 Lobby', 'Skull Map Room', 'Skull Pot Circle', 'Skull Pull Switch', 'Skull Big Chest', 'Skull Pinball',
'Skull Pot Prison', 'Skull Compass Room', 'Skull Left Drop', 'Skull 2 East Lobby', 'Skull Big Key',
'Skull Lone Pot', 'Skull Small Hall', 'Skull Back Drop', 'Skull 2 West Lobby', 'Skull X Room', 'Skull 3 Lobby',
'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits', 'Skull Torch Room', 'Skull Vines',
'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss'
]
dungeon_regions = {
'Hyrule Castle': hyrule_castle_regions,
'Eastern Palace': eastern_regions,
@@ -234,7 +240,7 @@ dungeon_regions = {
'Agahnims Tower': tower_regions,
'Palace of Darkness': pod_regions,
'Swamp Palace': swamp_regions,
# 'Skull':
'Skull Woods': skull_regions,
# 'TT':
# 'Ice':
# 'Mire':
@@ -250,6 +256,8 @@ region_starts = {
'Agahnims Tower': ['Tower Lobby'],
'Palace of Darkness': ['PoD Lobby'],
'Swamp Palace': ['Swamp Lobby'],
'Skull Woods': ['Skull 1 Lobby', 'Skull 2 East Lobby', 'Skull 2 West Lobby', 'Skull 3 Lobby', 'Skull Pot Circle',
'Skull Pinball', 'Skull Left Drop', 'Skull Back Drop'],
# ['TT Lobby'],
# ['Ice Lobby'],
# ['Mire Lobby'],
@@ -261,8 +269,12 @@ split_region_starts = {
'Desert Palace': [
['Desert Back Lobby'],
['Desert Main Lobby', 'Desert West Lobby', 'Desert East Lobby']
],
'Skull Woods': [
['Skull 1 Lobby', 'Skull Pot Circle'],
['Skull 2 West Lobby', 'Skull 2 East Lobby', 'Skull Back Drop'],
['Skull 3 Lobby']
]
# 'Skull': ['Skull 1 Lobby', 'Skull 2 Mummy Lobby', 'Skull 2 Key Lobby', 'Skull 3 Lobby'],
}

View File

@@ -1995,7 +1995,7 @@ def connect_doors(world, doors, targets, player):
def skull_woods_shuffle(world, player):
connect_random(world, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'],
['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)', 'Skull Woods Second Section (Drop)'], player)
['Skull Left Drop', 'Skull Pinball', 'Skull Pot Circle', 'Skull Back Drop'], player)
connect_random(world, ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'],
['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'], player, True)
@@ -2948,15 +2948,6 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
('Graveyard Ledge Mirror Spot', 'Graveyard Ledge'),
('Thieves Town Big Key Door', 'Thieves Town (Deep)'),
('Skull Woods Torch Room', 'Skull Woods Final Section (Mothula)'),
('Skull Woods First Section Bomb Jump', 'Skull Woods First Section (Top)'), # represents bomb jumping to big chest
('Skull Woods First Section South Door', 'Skull Woods First Section (Right)'),
('Skull Woods First Section West Door', 'Skull Woods First Section (Left)'),
('Skull Woods First Section (Right) North Door', 'Skull Woods First Section'),
('Skull Woods First Section (Left) Door to Right', 'Skull Woods First Section (Right)'),
('Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section'),
('Skull Woods First Section (Top) One-Way Path', 'Skull Woods First Section'),
('Skull Woods Second Section (Drop)', 'Skull Woods Second Section'),
('Blind Fight', 'Blind Fight'),
('Ice Palace Entrance Room', 'Ice Palace (Main)'),
('Ice Palace (East)', 'Ice Palace (East)'),
@@ -3073,7 +3064,6 @@ inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia
('Swamp Palace (North)', 'Swamp Palace (North)'),
('Thieves Town Big Key Door', 'Thieves Town (Deep)'),
('Skull Woods Torch Room', 'Skull Woods Final Section (Mothula)'),
('Skull Woods First Section Bomb Jump', 'Skull Woods First Section (Top)'),
('Skull Woods First Section South Door', 'Skull Woods First Section (Right)'),
('Skull Woods First Section West Door', 'Skull Woods First Section (Left)'),
('Skull Woods First Section (Right) North Door', 'Skull Woods First Section'),
@@ -3521,17 +3511,17 @@ default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Main L
('Thieves Town', 'Thieves Town (Entrance)'),
('Thieves Town Exit', 'West Dark World'),
('Skull Woods First Section Hole (East)', 'Skull Woods First Section (Right)'),
('Skull Woods First Section Hole (West)', 'Skull Woods First Section (Left)'),
('Skull Woods First Section Hole (North)', 'Skull Woods First Section (Top)'),
('Skull Woods First Section Door', 'Skull Woods First Section'),
('Skull Woods First Section Hole (East)', 'Skull Pinball'),
('Skull Woods First Section Hole (West)', 'Skull Left Drop'),
('Skull Woods First Section Hole (North)', 'Skull Pot Circle'),
('Skull Woods First Section Door', 'Skull 1 Lobby'),
('Skull Woods First Section Exit', 'Skull Woods Forest'),
('Skull Woods Second Section Hole', 'Skull Woods Second Section (Drop)'),
('Skull Woods Second Section Door (East)', 'Skull Woods Second Section'),
('Skull Woods Second Section Door (West)', 'Skull Woods Second Section'),
('Skull Woods Second Section Hole', 'Skull Back Drop'),
('Skull Woods Second Section Door (East)', 'Skull 2 East Lobby'),
('Skull Woods Second Section Door (West)', 'Skull 2 West Lobby'),
('Skull Woods Second Section Exit (East)', 'Skull Woods Forest'),
('Skull Woods Second Section Exit (West)', 'Skull Woods Forest (West)'),
('Skull Woods Final Section', 'Skull Woods Final Section (Entrance)'),
('Skull Woods Final Section', 'Skull 3 Lobby'),
('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'),
('Ice Palace', 'Ice Palace (Entrance)'),
('Ice Palace Exit', 'Dark Lake Hylia Central Island'),

View File

@@ -172,7 +172,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
'Return Smith': (True, False, 'Event', None, None, None, None, None, None, None, None),
'Pick Up Purple Chest': (True, False, 'Event', None, None, None, None, None, None, None, None),
'Open Floodgate': (True, False, 'Event', None, None, None, None, None, None, None, None),
'Trench 1 Filled': (True, False, 'Event', None, None, None, None, None, None, None, None),
'Trench 2 Filled': (True, False, 'Event', None, None, None, None, None, None, None, None),
'Drained Swamp': (True, False, 'Event', None, None, None, None, None, None, None, None),
'Trench 1 Filled': (False, False, 'Event', None, None, None, None, None, None, None, None),
'Trench 2 Filled': (False, False, 'Event', None, None, None, None, None, None, None, None),
'Drained Swamp': (False, False, 'Event', None, None, None, None, None, None, None, None),
}

20
Main.py
View File

@@ -58,11 +58,6 @@ def main(args, seed=None):
create_rooms(world, player)
create_dungeons(world, player)
logger.info('Shuffling dungeons')
for player in range(1, world.players + 1):
link_doors(world, player)
logger.info('Shuffling the World about.')
if world.mode != 'inverted':
@@ -76,6 +71,13 @@ def main(args, seed=None):
mark_dark_world_regions(world)
logger.info('Shuffling dungeons')
for player in range(1, world.players + 1):
link_doors(world, player)
# todo: mark regions after linking doors - for bunny logic?
logger.info('Generating Item Pool.')
for player in range(1, world.players + 1):
@@ -89,7 +91,7 @@ def main(args, seed=None):
# todo: remove this later. this is for debugging
for player in range(1, world.players + 1):
all_state = world.get_all_state(keys=True)
for bossregion in ['Eastern Boss', 'Desert Boss', 'Hera Boss', 'Tower Agahnim 1', 'PoD Boss', 'Swamp Boss']:
for bossregion in ['Eastern Boss', 'Desert Boss', 'Hera Boss', 'Tower Agahnim 1', 'PoD Boss', 'Swamp Boss', 'Skull Boss']:
if world.get_region(bossregion, player) not in all_state.reachable_regions[player]:
raise Exception(bossregion + ' missing from generation')
@@ -274,6 +276,12 @@ def copy_world(world):
ret.precollected_items = world.precollected_items.copy()
ret.state.stale = {player: True for player in range(1, world.players + 1)}
ret.doors = world.doors
ret.paired_doors = world.paired_doors
ret.rooms = world.rooms
ret.inaccessible_regions = world.inaccessible_regions
ret.dungeon_layouts = world.dungeon_layouts
for player in range(1, world.players + 1):
set_rules(ret, player)

View File

@@ -201,14 +201,7 @@ def create_regions(world, player):
'Thieves\' Town - Big Chest',
'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']),
create_dungeon_region(player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
create_dungeon_region(player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
create_dungeon_region(player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
create_dungeon_region(player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
create_dungeon_region(player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
create_dungeon_region(player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
create_dungeon_region(player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
create_dungeon_region(player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
create_dungeon_region(player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
create_dungeon_region(player, 'Ice Palace (Entrance)', 'Ice Palace', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']),
create_dungeon_region(player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest',
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
@@ -435,7 +428,7 @@ def create_regions(world, player):
create_dungeon_region(player, 'Swamp Trench 2 Departure', 'Swamp Palace', None, ['Swamp Trench 2 Departure Wet', 'Swamp Trench 2 Departure WS']),
create_dungeon_region(player, 'Swamp Big Key Ledge', 'Swamp Palace', ['Swamp Palace - Big Key Chest'], ['Swamp Big Key Ledge WN']),
create_dungeon_region(player, 'Swamp West Shallows', 'Swamp Palace', None, ['Swamp West Shallows ES', 'Swamp West Shallows Push Blocks']),
create_dungeon_region(player, 'Swamp West Block Path', 'Swamp Palace', None, ['Swamp West Block Path Up Stairs']),
create_dungeon_region(player, 'Swamp West Block Path', 'Swamp Palace', None, ['Swamp West Block Path Up Stairs', 'Swamp West Block Path Drop Down']),
create_dungeon_region(player, 'Swamp West Ledge', 'Swamp Palace', ['Swamp Palace - West Chest'], ['Swamp West Ledge Drop Down', 'Swamp West Ledge Hook Path']),
create_dungeon_region(player, 'Swamp Barrier Ledge', 'Swamp Palace', None, ['Swamp Barrier Ledge Drop Down', 'Swamp Barrier Ledge - Orange', 'Swamp Barrier Ledge Hook Path']),
create_dungeon_region(player, 'Swamp Barrier', 'Swamp Palace', None, ['Swamp Barrier EN', 'Swamp Barrier - Orange']),
@@ -460,6 +453,32 @@ def create_regions(world, player):
create_dungeon_region(player, 'Swamp Boss', 'Swamp Palace', ['Swamp Palace - Boss', 'Swamp Palace - Prize'], ['Swamp Boss SW']),
# sw
create_dungeon_region(player, 'Skull 1 Lobby', 'Skull Woods', None, ['Skull Woods First Section Exit', 'Skull 1 Lobby WS', 'Skull 1 Lobby ES']),
create_dungeon_region(player, 'Skull Map Room', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Map Room WS', 'Skull Map Room SE']),
create_dungeon_region(player, 'Skull Pot Circle', 'Skull Woods', None, ['Skull Pot Circle WN', 'Skull Pot Circle Star Path']),
create_dungeon_region(player, 'Skull Pull Switch', 'Skull Woods', None, ['Skull Pull Switch EN', 'Skull Pull Switch S']),
create_dungeon_region(player, 'Skull Big Chest', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Big Chest N', 'Skull Big Chest Hookpath']),
create_dungeon_region(player, 'Skull Pinball', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Pinball NE', 'Skull Pinball WS']),
create_dungeon_region(player, 'Skull Pot Prison', 'Skull Woods', ['Skull Woods - Pot Prison'], ['Skull Pot Prison ES', 'Skull Pot Prison SE']),
create_dungeon_region(player, 'Skull Compass Room', 'Skull Woods', ['Skull Woods - Compass Chest'], ['Skull Compass Room NE', 'Skull Compass Room ES', 'Skull Compass Room WS']),
create_dungeon_region(player, 'Skull Left Drop', 'Skull Woods', None, ['Skull Left Drop ES']),
create_dungeon_region(player, 'Skull 2 East Lobby', 'Skull Woods', None, ['Skull 2 East Lobby NW', 'Skull 2 East Lobby WS', 'Skull Woods Second Section Exit (East)']),
create_dungeon_region(player, 'Skull Big Key', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Big Key SW', 'Skull Big Key WN']),
create_dungeon_region(player, 'Skull Lone Pot', 'Skull Woods', None, ['Skull Lone Pot EN']),
create_dungeon_region(player, 'Skull Small Hall', 'Skull Woods', None, ['Skull Small Hall ES', 'Skull Small Hall WS']),
create_dungeon_region(player, 'Skull Back Drop', 'Skull Woods', None, ['Skull Back Drop Star Path', ]),
create_dungeon_region(player, 'Skull 2 West Lobby', 'Skull Woods', ['Skull Woods - West Lobby Pot Key'], ['Skull 2 West Lobby ES', 'Skull 2 West Lobby NW', 'Skull Woods Second Section Exit (West)']),
create_dungeon_region(player, 'Skull X Room', 'Skull Woods', None, ['Skull X Room SW']),
create_dungeon_region(player, 'Skull 3 Lobby', 'Skull Woods', None, ['Skull 3 Lobby NW', 'Skull 3 Lobby WN', 'Skull Woods Final Section Exit']),
create_dungeon_region(player, 'Skull East Bridge', 'Skull Woods', None, ['Skull East Bridge EN', 'Skull East Bridge ES']),
create_dungeon_region(player, 'Skull West Bridge Nook', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull West Bridge Nook WS']),
create_dungeon_region(player, 'Skull Star Pits', 'Skull Woods', None, ['Skull Star Pits SW', 'Skull Star Pits WS']),
create_dungeon_region(player, 'Skull Torch Room', 'Skull Woods', None, ['Skull Torch Room ES', 'Skull Torch Room EN']),
create_dungeon_region(player, 'Skull Vines', 'Skull Woods', None, ['Skull Vines WN', 'Skull Vines NW']),
create_dungeon_region(player, 'Skull Spike Corner', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop'], ['Skull Spike Corner SW', 'Skull Spike Corner WS']),
create_dungeon_region(player, 'Skull Final Drop', 'Skull Woods', None, ['Skull Final Drop ES', 'Skull Final Drop Hole']),
create_dungeon_region(player, 'Skull Boss', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
# tt
# ice
# mire
@@ -590,7 +609,9 @@ key_only_locations = {
'Swamp Palace - Trench 1 Pot Key': 'Small Key (Swamp Palace)',
'Swamp Palace - Hookshot Pot Key': 'Small Key (Swamp Palace)',
'Swamp Palace - Trench 2 Pot Key': 'Small Key (Swamp Palace)',
'Swamp Palace - Waterway Pot Key': 'Small Key (Swamp Palace)'
'Swamp Palace - Waterway Pot Key': 'Small Key (Swamp Palace)',
'Skull Woods - West Lobby Pot Key': 'Small Key (Skull Woods)',
'Skull Woods - Spike Corner Key Drop': 'Small Key (Skull Woods)'
}
dungeon_events = [

View File

@@ -2,7 +2,6 @@ import collections
from collections import defaultdict
import logging
from BaseClasses import CollectionState, DoorType
from Dungeons import region_starts
from DoorShuffle import ExplorationState
@@ -262,7 +261,7 @@ def global_rules(world, player):
# If these generate fine rules with vanilla shuffle - then no.
# Escape/ Hyrule Castle
generate_key_logic(region_starts['Hyrule Castle'], 'Small Key (Escape)', world, player)
generate_key_logic('Hyrule Castle', 'Small Key (Escape)', world, player)
# Eastern Palace
# Eyegore room needs a bow
@@ -273,7 +272,7 @@ def global_rules(world, player):
forbid_item(world.get_location('Eastern Palace - Big Chest', player), '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))
generate_key_logic(region_starts['Eastern Palace'], 'Small Key (Eastern Palace)', world, player)
generate_key_logic('Eastern Palace', 'Small Key (Eastern Palace)', world, player)
# Boss rules. Same as below but no BK or arrow requirement.
set_defeat_dungeon_boss_rule(world.get_location('Eastern Palace - Prize', player))
@@ -287,7 +286,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('Desert Wall Slide NW', player), lambda state: state.has_fire_source(player))
set_defeat_dungeon_boss_rule(world.get_location('Desert Palace - Prize', player))
set_defeat_dungeon_boss_rule(world.get_location('Desert Palace - Boss', player))
generate_key_logic(region_starts['Desert Palace'], 'Small Key (Desert Palace)', world, player)
generate_key_logic('Desert Palace', 'Small Key (Desert Palace)', world, player)
# Tower of Hera
set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player))
@@ -297,10 +296,10 @@ def global_rules(world, player):
set_rule(world.get_entrance('Hera Startile Corner NW', player), lambda state: state.has('Big Key (Tower of Hera)', player))
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player))
generate_key_logic(region_starts['Tower of Hera'], 'Small Key (Tower of Hera)', world, player)
generate_key_logic('Tower of Hera', 'Small Key (Tower of Hera)', world, player)
set_rule(world.get_entrance('Tower Altar NW', player), lambda state: state.has_sword(player))
generate_key_logic(region_starts['Agahnims Tower'], 'Small Key (Agahnims Tower)', world, player)
generate_key_logic('Agahnims Tower', 'Small Key (Agahnims Tower)', world, player)
set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player))
@@ -314,7 +313,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('PoD Dark Pegs Up Ladder', player), lambda state: state.has('Hammer', player))
set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize', player))
generate_key_logic(region_starts['Palace of Darkness'], 'Small Key (Palace of Darkness)', world, player)
generate_key_logic('Palace of Darkness', 'Small Key (Palace of Darkness)', world, player)
set_rule(world.get_entrance('Swamp Lobby Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player))
set_rule(world.get_entrance('Swamp Trench 1 Approach Dry', player), lambda state: not state.has('Trench 1 Filled', player))
@@ -349,7 +348,17 @@ def global_rules(world, player):
forbid_item(world.get_location('Swamp Palace - Big Chest', player), 'Big Key (Swamp Palace)', player)
set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player))
generate_key_logic(region_starts['Swamp Palace'], 'Small Key (Swamp Palace)', world, player)
generate_key_logic('Swamp Palace', 'Small Key (Swamp Palace)', world, player)
set_rule(world.get_entrance('Skull Big Chest Hookpath', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player))
if world.accessibility == 'locations':
forbid_item(world.get_location('Skull Woods - Big Chest', player), 'Big Key (Skull Woods)', player)
set_rule(world.get_entrance('Skull Torch Room EN', player), lambda state: state.has('Fire Rod', player))
set_rule(world.get_entrance('Skull Vines NW', player), lambda state: state.has_sword(player))
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player))
generate_key_logic('Skull Woods', 'Small Key (Skull Woods)', world, player)
# End of door rando rules.
@@ -368,19 +377,6 @@ def global_rules(world, player):
for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Boss']:
forbid_item(world.get_location(location, player), 'Small Key (Thieves Town)', player)
set_rule(world.get_entrance('Skull Woods First Section South Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player))
set_rule(world.get_entrance('Skull Woods First Section (Right) North Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player))
set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section
set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 2))
set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) or item_name(state, 'Skull Woods - Big Chest', player) == ('Big Key (Skull Woods)', player))
if world.accessibility != 'locations':
set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player)
set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and state.has_sword(player)) # sword required for curtain
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player))
for location in ['Skull Woods - Boss']:
forbid_item(world.get_location(location, player), 'Small Key (Skull Woods)', player)
set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_melt_things(player))
set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player))
set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state.has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state.has_key('Small Key (Ice Palace)', player, 1))))
@@ -904,7 +900,6 @@ def no_glitches_rules(world, player):
add_rule(world.get_location(location, player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: False) # no glitches does not require block override
set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False)
set_rule(world.get_entrance('Skull Woods First Section Bomb Jump', player), lambda state: False)
# Light cones in standard depend on which world we actually are in, not which one the location would normally be
# We add Lamp requirements only to those locations which lie in the dark world (or everything if open
@@ -1549,11 +1544,12 @@ def set_bunny_rules(world, player):
# regions for the exits of multi-entrace caves/drops that bunny cannot pass
# Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing.
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)',
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)',
'Turtle Rock (Eye Bridge)', 'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)']
# todo: bunny impassable caves
# sewers drop may or may not be - maybe just new terminology
# desert pots are impassible by bunny - need rules for those transitions
# skull woods drops tend to soft lock bunny
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', 'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge', 'Dark Blacksmith Ruins']
@@ -1691,7 +1687,8 @@ def set_inverted_bunny_rules(world, player):
add_rule(location, get_rule_to_add(location.parent_region))
def generate_key_logic(start_region_names, small_key_name, world, player):
def generate_key_logic(dungeon_name, small_key_name, world, player):
sector, start_region_names = world.dungeon_layouts[player][dungeon_name]
logger = logging.getLogger('')
# Now that the dungeon layout is done, we need to search again to generate key logic.
# TODO: This assumes all start doors are accessible, which isn't always true.
@@ -1716,10 +1713,10 @@ def generate_key_logic(start_region_names, small_key_name, world, player):
explorable_door = state.next_avail_door()
door = explorable_door.door
local_kr = state.door_krs[door.name]
logger.debug(' kr %s: Door %s', local_kr, door.name)
# logger.debug(' kr %s: Door %s', local_kr, door.name)
connect_region = world.get_entrance(door.name, player).connected_region
# Bail early if we've been here before or the door is blocked
if not state.can_traverse(door) or state.visited(connect_region):
if not state.validate(door, connect_region, world):
continue
# Once we open a key door, we need a new region.
if door.smallKey and door not in state.opened_doors: # we tend to open doors in a DFS manner
@@ -1729,7 +1726,7 @@ def generate_key_logic(start_region_names, small_key_name, world, player):
state.opened_doors.append(door)
if door.dest.smallKey:
state.opened_doors.append(door.dest)
logger.debug(' New KR %s', current_kr)
logger.debug('%s: New KR %s', door.name, current_kr)
# Account for the new region
state.visit_region(connect_region, local_kr)
state.add_all_doors_check_key_region(connect_region, local_kr, world, player)