Added Turtle Rock.
Fixed clock -> process_time for Python 3.8 Fixed interior blocked doors Vanilla logical connections for Ice Cross (Push block) Dungeon entrance enhancement for TR, Skull, HC (Standard) Kill on invalid dungeons during key door shuffle Key logic improvements (Smallkey restrictions, Logic Min/Logic Max for key doors, Big Chest doesn't count for small keys if BK not found yet) Key door candidate now accounts for "overworld" dungeon traversal Path checking added some Crystal Logic (Blind's Cell to Boss mostly) Kill on dungeon gen if taking too long
This commit is contained in:
@@ -7,7 +7,7 @@ from Rom import LocalRom, Sprite, apply_rom_settings
|
|||||||
|
|
||||||
|
|
||||||
def adjust(args):
|
def adjust(args):
|
||||||
start = time.clock()
|
start = time.process_time()
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
logger.info('Patching ROM.')
|
logger.info('Patching ROM.')
|
||||||
|
|
||||||
@@ -31,6 +31,6 @@ def adjust(args):
|
|||||||
rom.write_to_file(output_path('%s.sfc' % outfilebase))
|
rom.write_to_file(output_path('%s.sfc' % outfilebase))
|
||||||
|
|
||||||
logger.info('Done. Enjoy.')
|
logger.info('Done. Enjoy.')
|
||||||
logger.debug('Total Time: %s', time.clock() - start)
|
logger.debug('Total Time: %s', time.process_time() - start)
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|||||||
@@ -1054,7 +1054,6 @@ class Sector(object):
|
|||||||
self.regions = []
|
self.regions = []
|
||||||
self.outstanding_doors = []
|
self.outstanding_doors = []
|
||||||
self.name = None
|
self.name = None
|
||||||
# todo: make these lazy init? - when do you invalidate them
|
|
||||||
|
|
||||||
def polarity(self):
|
def polarity(self):
|
||||||
pol = Polarity()
|
pol = Polarity()
|
||||||
|
|||||||
407
DoorShuffle.py
407
DoorShuffle.py
@@ -5,9 +5,9 @@ import logging
|
|||||||
import operator as op
|
import operator as op
|
||||||
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, Polarity
|
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, Polarity, CrystalBarrier
|
||||||
from Dungeons import hyrule_castle_regions, eastern_regions, desert_regions, hera_regions, tower_regions, pod_regions
|
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, dungeon_keys, dungeon_bigs
|
from Dungeons import dungeon_regions, region_starts, split_region_starts, dungeon_keys, dungeon_bigs, flexible_starts
|
||||||
from RoomData import DoorKind, PairedDoor
|
from RoomData import DoorKind, PairedDoor
|
||||||
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon
|
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ def link_doors(world, player):
|
|||||||
connect_simple_door(world, exitName, regionName, player)
|
connect_simple_door(world, exitName, regionName, player)
|
||||||
# These should all be connected for now as normal connections
|
# These should all be connected for now as normal connections
|
||||||
for edge_a, edge_b in interior_doors:
|
for edge_a, edge_b in interior_doors:
|
||||||
connect_two_way(world, edge_a, edge_b, player)
|
connect_interior_doors(edge_a, edge_b, world, player)
|
||||||
|
|
||||||
# These connections are here because they are currently unable to be shuffled
|
# These connections are here because they are currently unable to be shuffled
|
||||||
for entrance, ext in straight_staircases:
|
for entrance, ext in straight_staircases:
|
||||||
@@ -34,6 +34,8 @@ def link_doors(world, player):
|
|||||||
connect_two_way(world, ent, ext, player)
|
connect_two_way(world, ent, ext, player)
|
||||||
|
|
||||||
if world.doorShuffle == 'vanilla':
|
if world.doorShuffle == 'vanilla':
|
||||||
|
for exitName, regionName in vanilla_logical_connections:
|
||||||
|
connect_simple_door(world, exitName, regionName, player)
|
||||||
for entrance, ext in spiral_staircases:
|
for entrance, ext in spiral_staircases:
|
||||||
connect_two_way(world, entrance, ext, player)
|
connect_two_way(world, entrance, ext, player)
|
||||||
for entrance, ext in default_door_connections:
|
for entrance, ext in default_door_connections:
|
||||||
@@ -125,6 +127,24 @@ def connect_simple_door(world, exit_name, region_name, player):
|
|||||||
d.dest = region
|
d.dest = region
|
||||||
|
|
||||||
|
|
||||||
|
def connect_door_only(world, exit_name, region_name, player):
|
||||||
|
region = world.get_region(region_name, player)
|
||||||
|
d = world.check_for_door(exit_name, player)
|
||||||
|
if d is not None:
|
||||||
|
d.dest = region
|
||||||
|
|
||||||
|
|
||||||
|
def connect_interior_doors(a, b, world, player):
|
||||||
|
door_a = world.get_door(a, player)
|
||||||
|
door_b = world.get_door(b, player)
|
||||||
|
if door_a.blocked:
|
||||||
|
connect_one_way(world, b, a, player)
|
||||||
|
elif door_b.blocked:
|
||||||
|
connect_one_way(world, a, b, player)
|
||||||
|
else:
|
||||||
|
connect_two_way(world, a, b, player)
|
||||||
|
|
||||||
|
|
||||||
def connect_two_way(world, entrancename, exitname, player):
|
def connect_two_way(world, entrancename, exitname, player):
|
||||||
entrance = world.get_entrance(entrancename, player)
|
entrance = world.get_entrance(entrancename, player)
|
||||||
ext = world.get_entrance(exitname, player)
|
ext = world.get_entrance(exitname, player)
|
||||||
@@ -174,7 +194,8 @@ def fix_big_key_doors_with_ugly_smalls(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def remove_ugly_small_key_doors(world, player):
|
def remove_ugly_small_key_doors(world, player):
|
||||||
for d in ['Eastern Hint Tile Blocked Path SE', 'Eastern Darkness S', 'Thieves Hallway SE', 'Mire Left Bridge S']:
|
for d in ['Eastern Hint Tile Blocked Path SE', 'Eastern Darkness S', 'Thieves Hallway SE', 'Mire Left Bridge S',
|
||||||
|
'TR Lava Escape SE']:
|
||||||
door = world.get_door(d, player)
|
door = world.get_door(d, player)
|
||||||
room = world.get_room(door.roomIndex, player)
|
room = world.get_room(door.roomIndex, player)
|
||||||
room.change(door.doorListPos, DoorKind.Normal)
|
room.change(door.doorListPos, DoorKind.Normal)
|
||||||
@@ -183,7 +204,8 @@ def remove_ugly_small_key_doors(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def unpair_big_key_doors(world, player):
|
def unpair_big_key_doors(world, player):
|
||||||
problematic_bk_doors = ['Eastern Courtyard N', 'Eastern Big Key NE', 'Thieves BK Corner NE', 'Mire BK Door Room N']
|
problematic_bk_doors = ['Eastern Courtyard N', 'Eastern Big Key NE', 'Thieves BK Corner NE', 'Mire BK Door Room N',
|
||||||
|
'TR Dodgers NE']
|
||||||
for paired_door in world.paired_doors[player]:
|
for paired_door in world.paired_doors[player]:
|
||||||
if paired_door.door_a in problematic_bk_doors or paired_door.door_b in problematic_bk_doors:
|
if paired_door.door_a in problematic_bk_doors or paired_door.door_b in problematic_bk_doors:
|
||||||
paired_door.pair = False
|
paired_door.pair = False
|
||||||
@@ -212,28 +234,35 @@ def within_dungeon(world, player):
|
|||||||
fix_big_key_doors_with_ugly_smalls(world, player)
|
fix_big_key_doors_with_ugly_smalls(world, player)
|
||||||
overworld_prep(world, player)
|
overworld_prep(world, player)
|
||||||
dungeon_sectors = []
|
dungeon_sectors = []
|
||||||
|
entrances_map, potentials, connections = determine_entrance_list(world, player)
|
||||||
for key in dungeon_regions.keys():
|
for key in dungeon_regions.keys():
|
||||||
sector_list = convert_to_sectors(dungeon_regions[key], world, player)
|
sector_list = convert_to_sectors(dungeon_regions[key], world, player)
|
||||||
if key in split_region_starts.keys():
|
if key in split_region_starts.keys():
|
||||||
split_sectors = split_up_sectors(sector_list, split_region_starts[key])
|
split_sectors = split_up_sectors(sector_list, split_region_starts[key])
|
||||||
for idx, sub_sector_list in enumerate(split_sectors):
|
for idx, sub_sector_list in enumerate(split_sectors):
|
||||||
dungeon_sectors.append((key, sub_sector_list, split_region_starts[key][idx]))
|
entrance_list = list(split_region_starts[key][idx])
|
||||||
# todo: shuffable entrances like pinball, left pit need to be added to entrance list
|
# shuffable entrances like pinball, left pit need to be added to entrance list
|
||||||
|
if key in flexible_starts.keys():
|
||||||
|
add_shuffled_entrances(sub_sector_list, flexible_starts[key], entrance_list)
|
||||||
|
dungeon_sectors.append((key, sub_sector_list, entrance_list))
|
||||||
else:
|
else:
|
||||||
dungeon_sectors.append((key, sector_list, region_starts[key]))
|
dungeon_sectors.append((key, sector_list, entrances_map[key]))
|
||||||
|
|
||||||
|
enabled_entrances = []
|
||||||
dungeon_layouts = []
|
dungeon_layouts = []
|
||||||
for key, sector_list, entrance_list in dungeon_sectors:
|
for key, sector_list, entrance_list in dungeon_sectors:
|
||||||
ds = generate_dungeon(sector_list, entrance_list, world, player)
|
origin_list = list(entrance_list)
|
||||||
|
find_enabled_origins(sector_list, enabled_entrances, origin_list)
|
||||||
|
ds = generate_dungeon(sector_list, origin_list, world, player)
|
||||||
|
find_new_entrances(ds, connections, potentials, enabled_entrances)
|
||||||
ds.name = key
|
ds.name = key
|
||||||
dungeon_layouts.append((ds, entrance_list))
|
dungeon_layouts.append((ds, entrance_list))
|
||||||
|
|
||||||
combine_layouts(dungeon_layouts)
|
combine_layouts(dungeon_layouts, entrances_map)
|
||||||
world.dungeon_layouts[player] = {}
|
world.dungeon_layouts[player] = {}
|
||||||
for sector, entrances in dungeon_layouts:
|
for sector, entrances in dungeon_layouts:
|
||||||
world.dungeon_layouts[player][sector.name] = (sector, entrances)
|
world.dungeon_layouts[player][sector.name] = (sector, entrances)
|
||||||
|
|
||||||
remove_inaccessible_entrances(world, player)
|
|
||||||
paths = determine_required_paths(world)
|
paths = determine_required_paths(world)
|
||||||
check_required_paths(paths, world, player)
|
check_required_paths(paths, world, player)
|
||||||
|
|
||||||
@@ -242,6 +271,47 @@ def within_dungeon(world, player):
|
|||||||
shuffle_key_doors(sector, entrances, world, player)
|
shuffle_key_doors(sector, entrances, world, player)
|
||||||
|
|
||||||
|
|
||||||
|
def determine_entrance_list(world, player):
|
||||||
|
entrance_map = {}
|
||||||
|
potential_entrances = {}
|
||||||
|
connections = {}
|
||||||
|
for key, r_names in region_starts.items():
|
||||||
|
entrance_map[key] = []
|
||||||
|
for region_name in r_names:
|
||||||
|
region = world.get_region(region_name, player)
|
||||||
|
for ent in region.entrances:
|
||||||
|
parent = ent.parent_region
|
||||||
|
if parent.type != RegionType.Dungeon or parent.name == 'Sewer Drop':
|
||||||
|
if parent.name not in world.inaccessible_regions:
|
||||||
|
entrance_map[key].append(region_name)
|
||||||
|
else:
|
||||||
|
if ent.parent_region not in potential_entrances.keys():
|
||||||
|
potential_entrances[parent] = []
|
||||||
|
potential_entrances[parent].append(region_name)
|
||||||
|
connections[region_name] = parent
|
||||||
|
return entrance_map, potential_entrances, connections
|
||||||
|
|
||||||
|
|
||||||
|
def add_shuffled_entrances(sectors, region_list, entrance_list):
|
||||||
|
for sector in sectors:
|
||||||
|
for region in sector.regions:
|
||||||
|
if region.name in region_list:
|
||||||
|
entrance_list.append(region.name)
|
||||||
|
|
||||||
|
|
||||||
|
def find_enabled_origins(sectors, enabled, entrance_list):
|
||||||
|
for sector in sectors:
|
||||||
|
for region in sector.regions:
|
||||||
|
if region.name in enabled and region.name not in entrance_list:
|
||||||
|
entrance_list.append(region.name)
|
||||||
|
|
||||||
|
|
||||||
|
def find_new_entrances(sector, connections, potentials, enabled):
|
||||||
|
for region in sector.regions:
|
||||||
|
if region.name in connections.keys() and connections[region.name] in potentials.keys():
|
||||||
|
enabled.extend(potentials.pop(connections[region.name]))
|
||||||
|
|
||||||
|
|
||||||
def within_dungeon_legacy(world, player):
|
def within_dungeon_legacy(world, player):
|
||||||
# TODO: The "starts" regions need access logic
|
# TODO: The "starts" regions need access logic
|
||||||
# Aerinon's note: I think this is handled already by ER Rules - may need to check correct requirements
|
# Aerinon's note: I think this is handled already by ER Rules - may need to check correct requirements
|
||||||
@@ -464,37 +534,7 @@ def cross_dungeon(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def experiment(world, player):
|
def experiment(world, player):
|
||||||
fix_big_key_doors_with_ugly_smalls(world, player)
|
within_dungeon(world, player)
|
||||||
overworld_prep(world, player)
|
|
||||||
dungeon_sectors = []
|
|
||||||
for key in dungeon_regions.keys():
|
|
||||||
sector_list = convert_to_sectors(dungeon_regions[key], world, player)
|
|
||||||
if key in split_region_starts.keys():
|
|
||||||
split_sectors = split_up_sectors(sector_list, split_region_starts[key])
|
|
||||||
for idx, sub_sector_list in enumerate(split_sectors):
|
|
||||||
dungeon_sectors.append((key, sub_sector_list, split_region_starts[key][idx]))
|
|
||||||
# todo: shuffable entrances like pinball, left pit need to be added to entrance list
|
|
||||||
else:
|
|
||||||
dungeon_sectors.append((key, sector_list, region_starts[key]))
|
|
||||||
|
|
||||||
dungeon_layouts = []
|
|
||||||
for key, sector_list, entrance_list in dungeon_sectors:
|
|
||||||
ds = generate_dungeon(sector_list, entrance_list, world, player)
|
|
||||||
ds.name = key
|
|
||||||
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 sector, entrances in world.dungeon_layouts[player].values():
|
|
||||||
shuffle_key_doors(sector, entrances, world, player)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_to_sectors(region_names, world, player):
|
def convert_to_sectors(region_names, world, player):
|
||||||
@@ -523,7 +563,7 @@ def convert_to_sectors(region_names, world, player):
|
|||||||
new_sector = False
|
new_sector = False
|
||||||
else:
|
else:
|
||||||
door = world.check_for_door(ext.name, player)
|
door = world.check_for_door(ext.name, player)
|
||||||
if door is not None and door.controller is None:
|
if door is not None and door.controller is None and door.dest is None:
|
||||||
outstanding_doors.append(door)
|
outstanding_doors.append(door)
|
||||||
if new_sector:
|
if new_sector:
|
||||||
sector = Sector()
|
sector = Sector()
|
||||||
@@ -535,7 +575,7 @@ def convert_to_sectors(region_names, world, player):
|
|||||||
|
|
||||||
|
|
||||||
# those with split region starts like Desert/Skull combine for key layouts
|
# those with split region starts like Desert/Skull combine for key layouts
|
||||||
def combine_layouts(dungeon_layouts):
|
def combine_layouts(dungeon_layouts, entrances_map):
|
||||||
combined = {}
|
combined = {}
|
||||||
queue = collections.deque(dungeon_layouts)
|
queue = collections.deque(dungeon_layouts)
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
@@ -548,7 +588,7 @@ def combine_layouts(dungeon_layouts):
|
|||||||
else:
|
else:
|
||||||
combined[sector.name].regions.extend(sector.regions)
|
combined[sector.name].regions.extend(sector.regions)
|
||||||
for key in combined.keys():
|
for key in combined.keys():
|
||||||
dungeon_layouts.append((combined[key], region_starts[key]))
|
dungeon_layouts.append((combined[key], entrances_map[key]))
|
||||||
|
|
||||||
|
|
||||||
def split_up_sectors(sector_list, entrance_sets):
|
def split_up_sectors(sector_list, entrance_sets):
|
||||||
@@ -742,6 +782,8 @@ def shuffle_key_doors(dungeon_sector, entrances, world, player):
|
|||||||
if itr >= combinations:
|
if itr >= combinations:
|
||||||
logging.getLogger('').info('Lowering key door count because no valid layouts: %s', dungeon_sector.name)
|
logging.getLogger('').info('Lowering key door count because no valid layouts: %s', dungeon_sector.name)
|
||||||
num_key_doors -= 1
|
num_key_doors -= 1
|
||||||
|
if num_key_doors < 0:
|
||||||
|
raise Exception('Bad dungeon %s - 0 key doors not valid' % dungeon_sector.name)
|
||||||
combinations = ncr(len(paired_candidates), num_key_doors)
|
combinations = ncr(len(paired_candidates), num_key_doors)
|
||||||
itr = 0
|
itr = 0
|
||||||
proposal = kth_combination(itr, paired_candidates, num_key_doors)
|
proposal = kth_combination(itr, paired_candidates, num_key_doors)
|
||||||
@@ -758,8 +800,11 @@ class KeyLogic(object):
|
|||||||
def __init__(self, dungeon_name):
|
def __init__(self, dungeon_name):
|
||||||
self.door_rules = {}
|
self.door_rules = {}
|
||||||
self.bk_restricted = []
|
self.bk_restricted = []
|
||||||
|
self.sm_restricted = []
|
||||||
self.small_key_name = dungeon_keys[dungeon_name]
|
self.small_key_name = dungeon_keys[dungeon_name]
|
||||||
self.bk_name = dungeon_bigs[dungeon_name]
|
self.bk_name = dungeon_bigs[dungeon_name]
|
||||||
|
self.logic_min = {}
|
||||||
|
self.logic_max = {}
|
||||||
|
|
||||||
|
|
||||||
def build_pair_list(flat_list):
|
def build_pair_list(flat_list):
|
||||||
@@ -789,12 +834,12 @@ def flatten_pair_list(paired_list):
|
|||||||
def find_key_door_candidates(region, checked, world, player):
|
def find_key_door_candidates(region, checked, world, player):
|
||||||
candidates = []
|
candidates = []
|
||||||
checked_doors = list(checked)
|
checked_doors = list(checked)
|
||||||
queue = collections.deque([(region, None)])
|
queue = collections.deque([(region, None, None)])
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
current, last_door = queue.pop()
|
current, last_door, last_region = queue.pop()
|
||||||
for ext in current.exits:
|
for ext in current.exits:
|
||||||
d = world.check_for_door(ext.name, player)
|
d = world.check_for_door(ext.name, player)
|
||||||
if d is not None and not d.blocked and d.dest is not last_door and d not in checked_doors:
|
if d is not None and not d.blocked and d.dest is not last_door and d.dest is not last_region and d not in checked_doors:
|
||||||
valid = False
|
valid = False
|
||||||
if 0 <= d.doorListPos < 4 and d.type in [DoorType.Interior, DoorType.Normal, DoorType.SpiralStairs]:
|
if 0 <= d.doorListPos < 4 and d.type in [DoorType.Interior, DoorType.Normal, DoorType.SpiralStairs]:
|
||||||
room = world.get_room(d.roomIndex, player)
|
room = world.get_room(d.roomIndex, player)
|
||||||
@@ -816,7 +861,7 @@ def find_key_door_candidates(region, checked, world, player):
|
|||||||
valid = True
|
valid = True
|
||||||
if valid:
|
if valid:
|
||||||
candidates.append(d)
|
candidates.append(d)
|
||||||
queue.append((ext.connected_region, d))
|
queue.append((ext.connected_region, d, current)) # - todo: fix isolated ledge from re-entering
|
||||||
if d is not None:
|
if d is not None:
|
||||||
checked_doors.append(d)
|
checked_doors.append(d)
|
||||||
return candidates, checked_doors
|
return candidates, checked_doors
|
||||||
@@ -872,8 +917,9 @@ def validate_key_layout_r(state, flat_proposal, checked_states, key_logic, world
|
|||||||
num_bigs = 1 if len(state.big_doors) > 0 else 0 # all or nothing
|
num_bigs = 1 if len(state.big_doors) > 0 else 0 # all or nothing
|
||||||
if not smalls_avail and num_bigs == 0:
|
if not smalls_avail and num_bigs == 0:
|
||||||
return True # I think that's the end
|
return True # I think that's the end
|
||||||
available_small_locations = min(state.ttl_locations - state.used_locations, state.key_locations - state.used_smalls)
|
ttl_locations = state.ttl_locations if state.big_key_opened else count_locations_exclude_big_chest(state)
|
||||||
available_big_locations = state.ttl_locations - state.used_locations if not state.big_key_special else 0
|
available_small_locations = min(ttl_locations - state.used_locations, state.key_locations - state.used_smalls)
|
||||||
|
available_big_locations = ttl_locations - state.used_locations if not state.big_key_special else 0
|
||||||
valid = True
|
valid = True
|
||||||
if (not smalls_avail or available_small_locations == 0) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0):
|
if (not smalls_avail or available_small_locations == 0) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0):
|
||||||
return False
|
return False
|
||||||
@@ -889,10 +935,15 @@ def validate_key_layout_r(state, flat_proposal, checked_states, key_logic, world
|
|||||||
valid = validate_key_layout_r(state_copy, flat_proposal, checked_states, key_logic, world, player)
|
valid = validate_key_layout_r(state_copy, flat_proposal, checked_states, key_logic, world, player)
|
||||||
if valid:
|
if valid:
|
||||||
checked_states.add(code)
|
checked_states.add(code)
|
||||||
elif smalls_avail and available_small_locations > 0:
|
if not valid:
|
||||||
key_rule_num = min(state.key_locations, count_unique_doors(state.small_doors) + state.used_smalls)
|
return False
|
||||||
if key_rule_num == len(state.found_locations):
|
if smalls_avail and available_small_locations > 0:
|
||||||
key_logic.bk_restricted.extend([x for x in state.found_locations if x not in key_logic.bk_restricted])
|
key_rule_num = min(available_small_locations, count_unique_doors(state.small_doors)) + state.used_smalls
|
||||||
|
if key_rule_num == ttl_locations:
|
||||||
|
key_logic.bk_restricted.extend([x for x in get_valid_small_key_locations(state) if x not in key_logic.bk_restricted])
|
||||||
|
set_logic_min(key_logic, state, key_rule_num)
|
||||||
|
if not state.big_key_opened and big_chest_in_locations(state):
|
||||||
|
key_logic.sm_restricted.extend([x for x in find_big_chest_locations(state) if x not in key_logic.sm_restricted])
|
||||||
for exp_door in state.small_doors:
|
for exp_door in state.small_doors:
|
||||||
state_copy = state.copy()
|
state_copy = state.copy()
|
||||||
state_copy.opened_doors.append(exp_door.door)
|
state_copy.opened_doors.append(exp_door.door)
|
||||||
@@ -916,10 +967,46 @@ def validate_key_layout_r(state, flat_proposal, checked_states, key_logic, world
|
|||||||
if valid:
|
if valid:
|
||||||
checked_states.add(code)
|
checked_states.add(code)
|
||||||
if not valid:
|
if not valid:
|
||||||
return valid
|
return False
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
|
|
||||||
|
def count_locations_exclude_big_chest(state):
|
||||||
|
cnt = 0
|
||||||
|
for loc in state.found_locations:
|
||||||
|
if '- Big Chest' not in loc.name and '- Prize' not in loc.name:
|
||||||
|
cnt += 1
|
||||||
|
return cnt
|
||||||
|
|
||||||
|
|
||||||
|
def big_chest_in_locations(state):
|
||||||
|
return len(find_big_chest_locations(state)) > 0
|
||||||
|
|
||||||
|
|
||||||
|
def find_big_chest_locations(state):
|
||||||
|
ret = []
|
||||||
|
for loc in state.found_locations:
|
||||||
|
if 'Big Chest' in loc.name:
|
||||||
|
ret.append(loc)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def get_valid_small_key_locations(state):
|
||||||
|
locations = []
|
||||||
|
for loc in state.found_locations:
|
||||||
|
if '- Prize' not in loc.name and (state.big_key_opened or '- Big Chest' not in loc.name):
|
||||||
|
locations.append(loc)
|
||||||
|
return locations
|
||||||
|
|
||||||
|
|
||||||
|
def get_valid_big_key_locations(state, key_logic):
|
||||||
|
locs = []
|
||||||
|
for loc in state.found_locations:
|
||||||
|
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc not in key_logic.bk_restricted:
|
||||||
|
locs.append(loc)
|
||||||
|
return locs
|
||||||
|
|
||||||
|
|
||||||
def count_unique_doors(doors_to_count):
|
def count_unique_doors(doors_to_count):
|
||||||
cnt = 0
|
cnt = 0
|
||||||
counted = set()
|
counted = set()
|
||||||
@@ -931,11 +1018,34 @@ def count_unique_doors(doors_to_count):
|
|||||||
return cnt
|
return cnt
|
||||||
|
|
||||||
|
|
||||||
|
def set_logic_min(key_logic, state, number):
|
||||||
|
for exp_door in state.small_doors:
|
||||||
|
name = exp_door.door.name
|
||||||
|
if name not in key_logic.logic_min.keys():
|
||||||
|
c_min = key_logic.logic_min[name] = number
|
||||||
|
else:
|
||||||
|
new_min = max(number, key_logic.logic_min[name])
|
||||||
|
if name in key_logic.logic_max.keys():
|
||||||
|
new_min = min(new_min, key_logic.logic_max[name])
|
||||||
|
c_min = key_logic.logic_min[name] = new_min
|
||||||
|
if name not in key_logic.door_rules.keys():
|
||||||
|
key_logic.door_rules[name] = max(c_min, number)
|
||||||
|
else:
|
||||||
|
key_logic.door_rules[name] = max(c_min, key_logic.door_rules[name])
|
||||||
|
for door in state.opened_doors:
|
||||||
|
if door.name in key_logic.logic_min.keys():
|
||||||
|
key_logic.logic_max[door.name] = key_logic.logic_min[door.name]
|
||||||
|
|
||||||
|
|
||||||
def set_key_rules(key_logic, door, number):
|
def set_key_rules(key_logic, door, number):
|
||||||
|
if door.name not in key_logic.logic_min.keys():
|
||||||
|
key_logic.logic_min[door.name] = 0
|
||||||
|
logic_min = key_logic.logic_min[door.name]
|
||||||
if door.name not in key_logic.door_rules.keys():
|
if door.name not in key_logic.door_rules.keys():
|
||||||
key_logic.door_rules[door.name] = number
|
key_logic.door_rules[door.name] = max(logic_min, number)
|
||||||
else:
|
else:
|
||||||
key_logic.door_rules[door.name] = min(number, key_logic.door_rules[door.name])
|
smallest_logic = min(number, key_logic.door_rules[door.name])
|
||||||
|
key_logic.door_rules[door.name] = max(logic_min, smallest_logic)
|
||||||
|
|
||||||
|
|
||||||
def state_id(state, flat_proposal):
|
def state_id(state, flat_proposal):
|
||||||
@@ -1009,23 +1119,6 @@ def change_door_to_small_key(d, world, player):
|
|||||||
room.change(d.doorListPos, DoorKind.SmallKey)
|
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):
|
def determine_required_paths(world):
|
||||||
paths = {
|
paths = {
|
||||||
'Hyrule Castle': [],
|
'Hyrule Castle': [],
|
||||||
@@ -1038,11 +1131,14 @@ def determine_required_paths(world):
|
|||||||
'Skull Woods': ['Skull Boss'],
|
'Skull Woods': ['Skull Boss'],
|
||||||
'Thieves Town': ['Thieves Boss', ('Thieves Blind\'s Cell', 'Thieves Boss')],
|
'Thieves Town': ['Thieves Boss', ('Thieves Blind\'s Cell', 'Thieves Boss')],
|
||||||
'Ice Palace': ['Ice Boss'],
|
'Ice Palace': ['Ice Boss'],
|
||||||
|
'Misery Mire': ['Mire Boss'],
|
||||||
|
'Turtle Rock': ['TR Boss'],
|
||||||
}
|
}
|
||||||
if world.shuffle == 'vanilla':
|
if world.shuffle == 'vanilla':
|
||||||
# paths['Skull Woods'].remove('Skull Boss') # is this necessary?
|
|
||||||
paths['Skull Woods'].insert(0, 'Skull 2 West Lobby')
|
paths['Skull Woods'].insert(0, 'Skull 2 West Lobby')
|
||||||
# todo - TR jazz
|
paths['Turtle Rock'].insert(0, 'TR Eye Bridge')
|
||||||
|
paths['Turtle Rock'].insert(0, 'TR Big Chest Entrance')
|
||||||
|
paths['Turtle Rock'].insert(0, 'TR Lazy Eyes')
|
||||||
if world.mode == 'standard':
|
if world.mode == 'standard':
|
||||||
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
||||||
paths['Hyrule Castle'].append('Sanctuary')
|
paths['Hyrule Castle'].append('Sanctuary')
|
||||||
@@ -1055,6 +1151,7 @@ def overworld_prep(world, player):
|
|||||||
if world.mode != 'inverted':
|
if world.mode != 'inverted':
|
||||||
if world.mode == 'standard':
|
if world.mode == 'standard':
|
||||||
world.inaccessible_regions.append('Hyrule Castle Ledge') # maybe only with er off
|
world.inaccessible_regions.append('Hyrule Castle Ledge') # maybe only with er off
|
||||||
|
world.inaccessible_regions.append('Sewer Drop')
|
||||||
world.inaccessible_regions.append('Skull Woods Forest (West)')
|
world.inaccessible_regions.append('Skull Woods Forest (West)')
|
||||||
world.inaccessible_regions.append('Dark Death Mountain Ledge')
|
world.inaccessible_regions.append('Dark Death Mountain Ledge')
|
||||||
world.inaccessible_regions.append('Dark Death Mountain Isolated Ledge')
|
world.inaccessible_regions.append('Dark Death Mountain Isolated Ledge')
|
||||||
@@ -1075,10 +1172,25 @@ def overworld_prep(world, player):
|
|||||||
Door(player, 'Skull Woods Final Section', DoorType.Logical)
|
Door(player, 'Skull Woods Final Section', DoorType.Logical)
|
||||||
]
|
]
|
||||||
world.doors += skull_doors
|
world.doors += skull_doors
|
||||||
connect_simple_door(world, skull_doors[0].name, 'Skull Woods Forest (West)', player)
|
connect_door_only(world, skull_doors[0].name, 'Skull Woods Forest (West)', player)
|
||||||
connect_simple_door(world, skull_doors[1].name, 'Skull 2 West Lobby', player)
|
connect_door_only(world, skull_doors[1].name, 'Skull 2 West Lobby', player)
|
||||||
connect_simple_door(world, skull_doors[2].name, 'Skull Back Drop', player)
|
connect_door_only(world, skull_doors[2].name, 'Skull Back Drop', player)
|
||||||
connect_simple_door(world, skull_doors[3].name, 'Skull 3 Lobby', player)
|
connect_door_only(world, skull_doors[3].name, 'Skull 3 Lobby', player)
|
||||||
|
tr_doors = [
|
||||||
|
Door(player, 'Turtle Rock Ledge Exit (West)', DoorType.Logical),
|
||||||
|
Door(player, 'Turtle Rock Ledge Exit (East)', DoorType.Logical),
|
||||||
|
Door(player, 'Dark Death Mountain Ledge (East)', DoorType.Logical),
|
||||||
|
Door(player, 'Dark Death Mountain Ledge (West)', DoorType.Logical),
|
||||||
|
Door(player, 'Turtle Rock Isolated Ledge Exit', DoorType.Logical),
|
||||||
|
Door(player, 'Turtle Rock Isolated Ledge Entrance', DoorType.Logical),
|
||||||
|
]
|
||||||
|
world.doors += tr_doors
|
||||||
|
connect_door_only(world, tr_doors[0].name, 'Dark Death Mountain Ledge', player)
|
||||||
|
connect_door_only(world, tr_doors[1].name, 'Dark Death Mountain Ledge', player)
|
||||||
|
connect_door_only(world, tr_doors[2].name, 'TR Big Chest Entrance', player)
|
||||||
|
connect_door_only(world, tr_doors[3].name, 'TR Lazy Eyes', player)
|
||||||
|
connect_door_only(world, tr_doors[4].name, 'Dark Death Mountain Isolated Ledge', player)
|
||||||
|
connect_door_only(world, tr_doors[5].name, 'TR Eye Bridge', player)
|
||||||
if world.mode == 'standard':
|
if world.mode == 'standard':
|
||||||
castle_doors = [
|
castle_doors = [
|
||||||
Door(player, 'Hyrule Castle Exit (West)', DoorType.Logical),
|
Door(player, 'Hyrule Castle Exit (West)', DoorType.Logical),
|
||||||
@@ -1087,10 +1199,10 @@ def overworld_prep(world, player):
|
|||||||
Door(player, 'Hyrule Castle Entrance (West)', DoorType.Logical)
|
Door(player, 'Hyrule Castle Entrance (West)', DoorType.Logical)
|
||||||
]
|
]
|
||||||
world.doors += castle_doors
|
world.doors += castle_doors
|
||||||
connect_simple_door(world, castle_doors[0].name, 'Hyrule Castle Ledge', player)
|
connect_door_only(world, castle_doors[0].name, 'Hyrule Castle Ledge', player)
|
||||||
connect_simple_door(world, castle_doors[1].name, 'Hyrule Castle Ledge', player)
|
connect_door_only(world, castle_doors[1].name, 'Hyrule Castle Ledge', player)
|
||||||
connect_simple_door(world, castle_doors[2].name, 'Hyrule Castle East Lobby', player)
|
connect_door_only(world, castle_doors[2].name, 'Hyrule Castle East Lobby', player)
|
||||||
connect_simple_door(world, castle_doors[3].name, 'Hyrule Castle West Lobby', player)
|
connect_door_only(world, castle_doors[3].name, 'Hyrule Castle West Lobby', player)
|
||||||
|
|
||||||
|
|
||||||
def check_required_paths(paths, world, player):
|
def check_required_paths(paths, world, player):
|
||||||
@@ -1103,14 +1215,21 @@ def check_required_paths(paths, world, player):
|
|||||||
states_to_explore[tuple([path[0]])].append(path[1])
|
states_to_explore[tuple([path[0]])].append(path[1])
|
||||||
else:
|
else:
|
||||||
states_to_explore[tuple(entrances)].append(path)
|
states_to_explore[tuple(entrances)].append(path)
|
||||||
|
cached_initial_state = None
|
||||||
for start_regs, dest_regs in states_to_explore.items():
|
for start_regs, dest_regs in states_to_explore.items():
|
||||||
check_paths = convert_regions(dest_regs, world, player)
|
check_paths = convert_regions(dest_regs, world, player)
|
||||||
start_regions = convert_regions(start_regs, world, player)
|
start_regions = convert_regions(start_regs, world, player)
|
||||||
state = ExplorationState()
|
initial = start_regs == tuple(entrances)
|
||||||
for region in start_regions:
|
if not initial or cached_initial_state is None:
|
||||||
state.visit_region(region)
|
state = ExplorationState(determine_init_crystal(initial, cached_initial_state, start_regions))
|
||||||
state.add_all_doors_check_unattached(region, world, player)
|
for region in start_regions:
|
||||||
explore_state(state, world, player)
|
state.visit_region(region)
|
||||||
|
state.add_all_doors_check_unattached(region, world, player)
|
||||||
|
explore_state(state, world, player)
|
||||||
|
if initial and cached_initial_state is None:
|
||||||
|
cached_initial_state = state
|
||||||
|
else:
|
||||||
|
state = cached_initial_state
|
||||||
valid, bad_region = check_if_regions_visited(state, check_paths)
|
valid, bad_region = check_if_regions_visited(state, check_paths)
|
||||||
if not valid:
|
if not valid:
|
||||||
if check_for_pinball_fix(state, bad_region, world, player):
|
if check_for_pinball_fix(state, bad_region, world, player):
|
||||||
@@ -1120,6 +1239,24 @@ def check_required_paths(paths, world, player):
|
|||||||
raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name))
|
raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name))
|
||||||
|
|
||||||
|
|
||||||
|
def determine_init_crystal(initial, state, start_regions):
|
||||||
|
if initial:
|
||||||
|
return CrystalBarrier.Orange
|
||||||
|
if state is None:
|
||||||
|
raise Exception('Please start path checking from the entrances')
|
||||||
|
if len(start_regions) > 1:
|
||||||
|
raise NotImplementedError('Path checking for multiple start regions (not the entrances) not implemented, use more paths instead')
|
||||||
|
start_region = start_regions[0]
|
||||||
|
if start_region in state.visited_blue and start_region in state.visited_orange:
|
||||||
|
return CrystalBarrier.Either
|
||||||
|
elif start_region in state.visited_blue:
|
||||||
|
return CrystalBarrier.Blue
|
||||||
|
elif start_region in state.visited_orange:
|
||||||
|
return CrystalBarrier.Orange
|
||||||
|
else:
|
||||||
|
raise Exception('Can\'t get to %s from initial state', start_region.name)
|
||||||
|
|
||||||
|
|
||||||
def explore_state(state, world, player):
|
def explore_state(state, world, player):
|
||||||
while len(state.avail_doors) > 0:
|
while len(state.avail_doors) > 0:
|
||||||
door = state.next_avail_door().door
|
door = state.next_avail_door().door
|
||||||
@@ -1215,10 +1352,6 @@ logical_connections = [
|
|||||||
('Thieves Blocked Entry Path', 'Thieves Basement Block'),
|
('Thieves Blocked Entry Path', 'Thieves Basement Block'),
|
||||||
('Thieves Conveyor Bridge Block Path', 'Thieves Conveyor Block'),
|
('Thieves Conveyor Bridge Block Path', 'Thieves Conveyor Block'),
|
||||||
('Thieves Conveyor Block Path', 'Thieves Conveyor Bridge'),
|
('Thieves Conveyor Block Path', 'Thieves Conveyor Bridge'),
|
||||||
# ('Ice Cross Left Push Block', ''), # todo: vanilla connections
|
|
||||||
# ('Ice Cross Right Push Block Bottom', ''),
|
|
||||||
# ('Ice Cross Bottom Push Block Right', ''),
|
|
||||||
# ('Ice Cross Top Push Block Right', ''),
|
|
||||||
('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'),
|
('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'),
|
||||||
('Ice Cross Right Push Block Top', 'Ice Bomb Drop'),
|
('Ice Cross Right Push Block Top', 'Ice Bomb Drop'),
|
||||||
('Ice Cross Top Push Block Left', 'Ice Floor Switch'),
|
('Ice Cross Top Push Block Left', 'Ice Floor Switch'),
|
||||||
@@ -1254,9 +1387,21 @@ logical_connections = [
|
|||||||
('Mire Compass Chest Exit', 'Mire Compass Room'),
|
('Mire Compass Chest Exit', 'Mire Compass Room'),
|
||||||
('Mire South Fish Blue Barrier', 'Mire Fishbone'),
|
('Mire South Fish Blue Barrier', 'Mire Fishbone'),
|
||||||
('Mire Fishbone Blue Barrier', 'Mire South Fish'),
|
('Mire Fishbone Blue Barrier', 'Mire South Fish'),
|
||||||
|
('TR Main Lobby Gap', 'TR Lobby Ledge'),
|
||||||
|
('TR Lobby Ledge Gap', 'TR Main Lobby'),
|
||||||
|
('TR Pipe Ledge Drop Down', 'TR Pipe Pit'),
|
||||||
|
('TR Big Chest Gap', 'TR Big Chest Entrance'),
|
||||||
|
('TR Big Chest Entrance Gap', 'TR Big Chest'),
|
||||||
# ('', ''),
|
# ('', ''),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
vanilla_logical_connections = [
|
||||||
|
('Ice Cross Left Push Block', 'Ice Compass Room'),
|
||||||
|
('Ice Cross Right Push Block Bottom', 'Ice Compass Room'),
|
||||||
|
('Ice Cross Bottom Push Block Right', 'Ice Pengator Switch'),
|
||||||
|
('Ice Cross Top Push Block Right', 'Ice Pengator Switch'),
|
||||||
|
]
|
||||||
|
|
||||||
spiral_staircases = [
|
spiral_staircases = [
|
||||||
('Hyrule Castle Back Hall Down Stairs', 'Hyrule Dungeon Map Room Up Stairs'),
|
('Hyrule Castle Back Hall Down Stairs', 'Hyrule Dungeon Map Room Up Stairs'),
|
||||||
('Hyrule Dungeon Armory Down Stairs', 'Hyrule Dungeon Staircase Up Stairs'),
|
('Hyrule Dungeon Armory Down Stairs', 'Hyrule Dungeon Staircase Up Stairs'),
|
||||||
@@ -1298,6 +1443,8 @@ spiral_staircases = [
|
|||||||
('Mire Left Bridge Down Stairs', 'Mire Dark Shooters Up Stairs'),
|
('Mire Left Bridge Down Stairs', 'Mire Dark Shooters Up Stairs'),
|
||||||
('Mire Conveyor Barrier Up Stairs', 'Mire Torches Top Down Stairs'),
|
('Mire Conveyor Barrier Up Stairs', 'Mire Torches Top Down Stairs'),
|
||||||
('Mire Falling Foes Up Stairs', 'Mire Firesnake Skip Down Stairs'),
|
('Mire Falling Foes Up Stairs', 'Mire Firesnake Skip Down Stairs'),
|
||||||
|
('TR Chain Chomps Down Stairs', 'TR Pipe Pit Up Stairs'),
|
||||||
|
('TR Crystaroller Down Stairs', 'TR Dark Ride Up Stairs'),
|
||||||
# ('', ''),
|
# ('', ''),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1306,6 +1453,7 @@ straight_staircases = [
|
|||||||
('Sewers Rope Room North Stairs', 'Sewers Dark Cross South Stairs'),
|
('Sewers Rope Room North Stairs', 'Sewers Dark Cross South Stairs'),
|
||||||
('Tower Catwalk North Stairs', 'Tower Antechamber South Stairs'),
|
('Tower Catwalk North Stairs', 'Tower Antechamber South Stairs'),
|
||||||
('PoD Conveyor North Stairs', 'PoD Map Balcony South Stairs'),
|
('PoD Conveyor North Stairs', 'PoD Map Balcony South Stairs'),
|
||||||
|
('TR Crystal Maze North Stairs', 'TR Final Abyss South Stairs'),
|
||||||
]
|
]
|
||||||
|
|
||||||
open_edges = [
|
open_edges = [
|
||||||
@@ -1516,7 +1664,15 @@ interior_doors = [
|
|||||||
('Mire Tall Dark and Roomy WS', 'Mire Crystal Right ES'),
|
('Mire Tall Dark and Roomy WS', 'Mire Crystal Right ES'),
|
||||||
('Mire Tall Dark and Roomy WN', 'Mire Shooter Rupees EN'),
|
('Mire Tall Dark and Roomy WN', 'Mire Shooter Rupees EN'),
|
||||||
('Mire Crystal Mid NW', 'Mire Crystal Top SW'),
|
('Mire Crystal Mid NW', 'Mire Crystal Top SW'),
|
||||||
# ('', ''),
|
('TR Tile Room NE', 'TR Refill SE'),
|
||||||
|
('TR Pokey 1 NW', 'TR Chain Chomps SW'),
|
||||||
|
('TR Twin Pokeys EN', 'TR Dodgers WN'),
|
||||||
|
('TR Twin Pokeys SW', 'TR Hallway NW'),
|
||||||
|
('TR Hallway ES', 'TR Big View WS'),
|
||||||
|
('TR Big Chest NE', 'TR Dodgers SE'),
|
||||||
|
('TR Dash Room ES', 'TR Tongue Pull WS'),
|
||||||
|
('TR Dash Room NW', 'TR Crystaroller SW'),
|
||||||
|
('TR Tongue Pull NE', 'TR Rupees SE'),
|
||||||
# ('', ''),
|
# ('', ''),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1639,9 +1795,24 @@ default_door_connections = [
|
|||||||
('Mire Tile Room SW', 'Mire Conveyor Barrier NW'),
|
('Mire Tile Room SW', 'Mire Conveyor Barrier NW'),
|
||||||
('Mire Block X WS', 'Mire Tall Dark and Roomy ES'),
|
('Mire Block X WS', 'Mire Tall Dark and Roomy ES'),
|
||||||
('Mire Crystal Left WS', 'Mire Falling Foes ES'),
|
('Mire Crystal Left WS', 'Mire Falling Foes ES'),
|
||||||
# ('', ''),
|
('TR Lobby Ledge NE', 'TR Hub SE'),
|
||||||
# ('', ''),
|
('TR Compass Room NW', 'TR Hub SW'),
|
||||||
# ('', ''),
|
('TR Hub ES', 'TR Torches Ledge WS'),
|
||||||
|
('TR Hub EN', 'TR Torches WN'),
|
||||||
|
('TR Hub NW', 'TR Pokey 1 SW'),
|
||||||
|
('TR Hub NE', 'TR Tile Room SE'),
|
||||||
|
('TR Torches NW', 'TR Roller Room SW'),
|
||||||
|
('TR Pipe Pit WN', 'TR Lava Dual Pipes EN'),
|
||||||
|
('TR Lava Island ES', 'TR Pipe Ledge WS'),
|
||||||
|
('TR Lava Dual Pipes WN', 'TR Pokey 2 EN'),
|
||||||
|
('TR Lava Dual Pipes SW', 'TR Twin Pokeys NW'),
|
||||||
|
('TR Pokey 2 ES', 'TR Lava Island WS'),
|
||||||
|
('TR Dodgers NE', 'TR Lava Escape SE'),
|
||||||
|
('TR Lava Escape NW', 'TR Dash Room SW'),
|
||||||
|
('TR Hallway WS', 'TR Lazy Eyes ES'),
|
||||||
|
('TR Dark Ride SW', 'TR Dash Bridge NW'),
|
||||||
|
('TR Dash Bridge SW', 'TR Eye Bridge NW'),
|
||||||
|
('TR Dash Bridge WS', 'TR Crystal Maze ES'),
|
||||||
# ('', ''),
|
# ('', ''),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1656,42 +1827,26 @@ default_one_way_connections = [
|
|||||||
('Swamp T NW', 'Swamp Boss SW'),
|
('Swamp T NW', 'Swamp Boss SW'),
|
||||||
('Thieves Hallway NE', 'Thieves Boss SE'),
|
('Thieves Hallway NE', 'Thieves Boss SE'),
|
||||||
('Mire Antechamber NW', 'Mire Boss SW'),
|
('Mire Antechamber NW', 'Mire Boss SW'),
|
||||||
|
('TR Final Abyss NW', 'TR Boss SW'),
|
||||||
# ('', ''),
|
# ('', ''),
|
||||||
]
|
]
|
||||||
|
|
||||||
# todo: these path rules are more complicated I think...
|
|
||||||
# there may be a better way to do them if we randomize dungeon entrances
|
|
||||||
dungeon_paths = {
|
|
||||||
'Hyrule Castle': [('Hyrule Castle Lobby', 'Hyrule Castle West Lobby'),
|
|
||||||
('Hyrule Castle Lobby', 'Hyrule Castle East Lobby'),
|
|
||||||
('Hyrule Castle Lobby', 'Hyrule Dungeon Cellblock'), # just for standard mode?
|
|
||||||
('Hyrule Dungeon Cellblock', 'Sanctuary')], # again, standard mode?
|
|
||||||
'Eastern Palace': [('Eastern Lobby', 'Eastern Boss')],
|
|
||||||
'Desert Palace': [('Desert Main Lobby', 'Desert West Lobby'),
|
|
||||||
('Desert Main Lobby', 'Desert East Lobby'),
|
|
||||||
('Desert Back Lobby', 'Desert Boss')], # or Desert Main Lobby to Desert Boss would be fine I guess
|
|
||||||
'Tower of Hera': [],
|
|
||||||
'Agahnims Tower': [],
|
|
||||||
'Palace of Darkness': [],
|
|
||||||
'Thieves Town': [],
|
|
||||||
'Skull Woods': [],
|
|
||||||
'Swamp Palace': [],
|
|
||||||
'Ice Palace': [],
|
|
||||||
'Misery Mire': [],
|
|
||||||
'Turtle Rock': [],
|
|
||||||
'Ganons Tower': []
|
|
||||||
}
|
|
||||||
|
|
||||||
# For crossed
|
# For crossed
|
||||||
default_dungeon_sets = [
|
default_dungeon_sets = [
|
||||||
['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Secret Room', 'Sanctuary',
|
['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Rat Path', 'Sanctuary',
|
||||||
'Hyrule Dungeon Cellblock'],
|
'Hyrule Dungeon Cellblock'],
|
||||||
['Eastern Lobby', 'Eastern Boss'],
|
['Eastern Lobby', 'Eastern Boss'],
|
||||||
['Desert Back Lobby', 'Desert Boss', 'Desert Main Lobby', 'Desert West Lobby', 'Desert East Lobby'],
|
['Desert Back Lobby', 'Desert Boss', 'Desert Main Lobby', 'Desert West Lobby', 'Desert East Lobby'],
|
||||||
['Hera Lobby', 'Hera Boss'],
|
['Hera Lobby', 'Hera Boss'],
|
||||||
['Tower Lobby', 'Tower Agahnim 1'],
|
['Tower Lobby', 'Tower Agahnim 1'],
|
||||||
['PoD Lobby', 'PoD Boss'],
|
['PoD Lobby', 'PoD Boss'],
|
||||||
['Swamp Lobby', 'Swamp Boss']
|
['Swamp Lobby', 'Swamp Boss'],
|
||||||
|
['Skull 1 Lobby', 'Skull Pinball', 'Skull Left Drop', 'Skull Pot Circle', 'Skull 2 East Lobby',
|
||||||
|
'Skull 2 West Lobby', 'Skull Back Drop', 'Skull 3 Lobby', 'Skull Boss'],
|
||||||
|
['Thieves Lobby', 'Thieves Attic Window', 'Thieves Blind\'s Cell', 'Thieves Boss'],
|
||||||
|
['Ice Lobby', 'Ice Boss'],
|
||||||
|
['Mire Lobby', 'Mire Boss'],
|
||||||
|
['TR Main Lobby', 'TR Boss', 'TR Eye Bridge', 'TR Big Chest Entrance', 'TR Lazy Eyes']
|
||||||
]
|
]
|
||||||
|
|
||||||
dungeon_x_idx_to_name = {
|
dungeon_x_idx_to_name = {
|
||||||
@@ -1704,6 +1859,8 @@ dungeon_x_idx_to_name = {
|
|||||||
6: 'Swamp Palace',
|
6: 'Swamp Palace',
|
||||||
7: 'Skull Woods',
|
7: 'Skull Woods',
|
||||||
8: 'Thieves Town',
|
8: 'Thieves Town',
|
||||||
9: 'Ice Palace'
|
9: 'Ice Palace',
|
||||||
# etc
|
10: 'Misery Mire',
|
||||||
|
11: 'Turtle Rock',
|
||||||
|
12: 'Ganon\'s Tower'
|
||||||
}
|
}
|
||||||
|
|||||||
85
Doors.py
85
Doors.py
@@ -814,6 +814,74 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Mire Antechamber NW', Nrml).dir(No, 0xa0, Left, High).big_key().pos(0),
|
create_door(player, 'Mire Antechamber NW', Nrml).dir(No, 0xa0, Left, High).big_key().pos(0),
|
||||||
create_door(player, 'Mire Boss SW', Nrml).dir(So, 0x90, Left, High).no_exit().trap(0x4).pos(0),
|
create_door(player, 'Mire Boss SW', Nrml).dir(So, 0x90, Left, High).no_exit().trap(0x4).pos(0),
|
||||||
|
|
||||||
|
create_door(player, 'TR Lobby Ledge NE', Nrml).dir(No, 0xd6, Right, High).pos(2),
|
||||||
|
create_door(player, 'TR Main Lobby Gap', Lgcl),
|
||||||
|
create_door(player, 'TR Lobby Ledge Gap', Lgcl),
|
||||||
|
create_door(player, 'TR Compass Room NW', Nrml).dir(No, 0xd6, Left, High).pos(0),
|
||||||
|
create_door(player, 'TR Hub SW', Nrml).dir(So, 0xc6, Left, High).pos(4),
|
||||||
|
create_door(player, 'TR Hub SE', Nrml).dir(So, 0xc6, Right, High).pos(5),
|
||||||
|
create_door(player, 'TR Hub ES', Nrml).dir(Ea, 0xc6, Bot, High).pos(3),
|
||||||
|
create_door(player, 'TR Hub EN', Nrml).dir(Ea, 0xc6, Top, High).pos(2),
|
||||||
|
create_door(player, 'TR Hub NW', Nrml).dir(No, 0xc6, Left, High).small_key().pos(0),
|
||||||
|
create_door(player, 'TR Hub NE', Nrml).dir(No, 0xc6, Right, High).pos(1),
|
||||||
|
create_door(player, 'TR Torches Ledge WS', Nrml).dir(We, 0xc7, Bot, High).pos(2),
|
||||||
|
create_door(player, 'TR Torches WN', Nrml).dir(We, 0xc7, Top, High).pos(1),
|
||||||
|
create_door(player, 'TR Torches NW', Nrml).dir(No, 0xc7, Left, High).trap(0x4).pos(0),
|
||||||
|
create_door(player, 'TR Roller Room SW', Nrml).dir(So, 0xb7, Left, High).pos(0),
|
||||||
|
create_door(player, 'TR Pokey 1 SW', Nrml).dir(So, 0xb6, Left, High).small_key().pos(2),
|
||||||
|
create_door(player, 'TR Tile Room SE', Nrml).dir(So, 0xb6, Right, High).pos(4),
|
||||||
|
create_door(player, 'TR Tile Room NE', Intr).dir(No, 0xb6, Right, High).pos(1),
|
||||||
|
create_door(player, 'TR Refill SE', Intr).dir(So, 0xb6, Right, High).pos(1),
|
||||||
|
create_door(player, 'TR Pokey 1 NW', Intr).dir(No, 0xb6, Left, High).small_key().pos(3),
|
||||||
|
create_door(player, 'TR Chain Chomps SW', Intr).dir(So, 0xb6, Left, High).small_key().pos(3),
|
||||||
|
create_door(player, 'TR Chain Chomps Down Stairs', Sprl).dir(Dn, 0xb6, 0, HTH).ss(A, 0x12, 0x80, True, True).small_key().pos(0),
|
||||||
|
create_door(player, 'TR Pipe Pit Up Stairs', Sprl).dir(Up, 0x15, 0, HTH).ss(A, 0x1b, 0x6c),
|
||||||
|
create_door(player, 'TR Pipe Pit WN', Nrml).dir(We, 0x15, Top, High).pos(1),
|
||||||
|
create_door(player, 'TR Pipe Ledge WS', Nrml).dir(We, 0x15, Left, High).no_exit().trap(0x4).pos(0),
|
||||||
|
create_door(player, 'TR Pipe Ledge Drop Down', Lgcl),
|
||||||
|
create_door(player, 'TR Lava Dual Pipes EN', Nrml).dir(Ea, 0x14, Top, High).pos(5),
|
||||||
|
create_door(player, 'TR Lava Dual Pipes WN', Nrml).dir(We, 0x14, Top, High).pos(3),
|
||||||
|
create_door(player, 'TR Lava Dual Pipes SW', Nrml).dir(So, 0x14, Left, High).pos(4),
|
||||||
|
create_door(player, 'TR Lava Island WS', Nrml).dir(We, 0x14, Bot, High).small_key().pos(1),
|
||||||
|
create_door(player, 'TR Lava Island ES', Nrml).dir(Ea, 0x14, Bot, High).pos(6),
|
||||||
|
create_door(player, 'TR Lava Escape SE', Nrml).dir(So, 0x14, Right, High).small_key().pos(0),
|
||||||
|
create_door(player, 'TR Lava Escape NW', Nrml).dir(No, 0x14, Left, High).pos(2),
|
||||||
|
create_door(player, 'TR Pokey 2 EN', Nrml).dir(Ea, 0x13, Top, High).pos(1),
|
||||||
|
create_door(player, 'TR Pokey 2 ES', Nrml).dir(Ea, 0x13, Bot, High).small_key().pos(0),
|
||||||
|
create_door(player, 'TR Twin Pokeys NW', Nrml).dir(No, 0x24, Left, High).pos(5),
|
||||||
|
create_door(player, 'TR Twin Pokeys SW', Intr).dir(So, 0x24, Left, High).pos(2),
|
||||||
|
create_door(player, 'TR Hallway NW', Intr).dir(No, 0x24, Left, High).pos(2),
|
||||||
|
create_door(player, 'TR Hallway WS', Nrml).dir(We, 0x24, Bot, High).pos(6),
|
||||||
|
create_door(player, 'TR Twin Pokeys EN', Intr).dir(Ea, 0x24, Top, High).pos(1),
|
||||||
|
create_door(player, 'TR Dodgers WN', Intr).dir(We, 0x24, Top, High).pos(1),
|
||||||
|
create_door(player, 'TR Hallway ES', Intr).dir(Ea, 0x24, Bot, High).pos(7),
|
||||||
|
create_door(player, 'TR Big View WS', Intr).dir(We, 0x24, Bot, High).pos(7),
|
||||||
|
create_door(player, 'TR Big Chest Gap', Lgcl),
|
||||||
|
create_door(player, 'TR Big Chest Entrance Gap', Lgcl),
|
||||||
|
create_door(player, 'TR Big Chest NE', Intr).dir(No, 0x24, Right, High).pos(3),
|
||||||
|
create_door(player, 'TR Dodgers SE', Intr).dir(So, 0x24, Right, High).no_exit().pos(3),
|
||||||
|
create_door(player, 'TR Dodgers NE', Nrml).dir(No, 0x24, Right, High).big_key().pos(0),
|
||||||
|
create_door(player, 'TR Lazy Eyes ES', Nrml).dir(Ea, 0x23, Bot, High).pos(1),
|
||||||
|
create_door(player, 'TR Dash Room SW', Nrml).dir(So, 0x04, Left, High).pos(4),
|
||||||
|
create_door(player, 'TR Dash Room ES', Intr).dir(Ea, 0x04, Bot, High).pos(2),
|
||||||
|
create_door(player, 'TR Tongue Pull WS', Intr).dir(We, 0x04, Bot, High).pos(2),
|
||||||
|
create_door(player, 'TR Tongue Pull NE', Intr).dir(No, 0x04, Right, High).pos(3),
|
||||||
|
create_door(player, 'TR Rupees SE', Intr).dir(So, 0x04, Right, High).pos(3),
|
||||||
|
create_door(player, 'TR Dash Room NW', Intr).dir(No, 0x04, Left, High).pos(1),
|
||||||
|
create_door(player, 'TR Crystaroller SW', Intr).dir(So, 0x04, Left, High).pos(1),
|
||||||
|
create_door(player, 'TR Crystaroller Down Stairs', Sprl).dir(Dn, 0x04, 0, HTH).ss(A, 0x12, 0x80, True, True).small_key().pos(0),
|
||||||
|
create_door(player, 'TR Dark Ride Up Stairs', Sprl).dir(Up, 0xb5, 0, HTH).ss(A, 0x1b, 0x6c),
|
||||||
|
create_door(player, 'TR Dark Ride SW', Nrml).dir(So, 0xb5, Left, High).trap(0x4).pos(0),
|
||||||
|
create_door(player, 'TR Dash Bridge NW', Nrml).dir(No, 0xc5, Left, High).pos(1),
|
||||||
|
create_door(player, 'TR Dash Bridge SW', Nrml).dir(So, 0xc5, Left, High).pos(2),
|
||||||
|
create_door(player, 'TR Dash Bridge WS', Nrml).dir(We, 0xc5, Bot, High).small_key().pos(0),
|
||||||
|
create_door(player, 'TR Eye Bridge NW', Nrml).dir(No, 0xd5, Left, High).pos(1),
|
||||||
|
create_door(player, 'TR Crystal Maze ES', Nrml).dir(Ea, 0xc4, Bot, High).small_key().pos(0),
|
||||||
|
create_door(player, 'TR Crystal Maze North Stairs', StrS).dir(No, 0xc4, Mid, High),
|
||||||
|
create_door(player, 'TR Final Abyss South Stairs', StrS).dir(No, 0xb4, Right, High),
|
||||||
|
create_door(player, 'TR Final Abyss NW', Nrml).dir(No, 0xb4, Left, High).big_key().pos(0),
|
||||||
|
create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0),
|
||||||
|
|
||||||
# Door Templates
|
# Door Templates
|
||||||
# create_door(player, '', Nrml).dir(No, 0x00, Right, High).pos(),
|
# create_door(player, '', Nrml).dir(No, 0x00, Right, High).pos(),
|
||||||
# create_door(player, '', Intr).dir(No, 0x00, Right, High).pos(),
|
# create_door(player, '', Intr).dir(No, 0x00, Right, High).pos(),
|
||||||
@@ -926,6 +994,15 @@ def create_doors(world, player):
|
|||||||
world.get_door('Mire Firesnake Skip Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
world.get_door('Mire Firesnake Skip Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||||
world.get_door('Mire Antechamber Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
world.get_door('Mire Antechamber Orange Barrier', player).barrier(CrystalBarrier.Orange)
|
||||||
|
|
||||||
|
world.get_door('TR Chain Chomps SW', player).c_switch()
|
||||||
|
world.get_door('TR Chain Chomps Down Stairs', player).c_switch()
|
||||||
|
world.get_door('TR Pokey 2 EN', player).c_switch()
|
||||||
|
world.get_door('TR Pokey 2 ES', player).c_switch()
|
||||||
|
world.get_door('TR Crystaroller SW', player).c_switch()
|
||||||
|
world.get_door('TR Crystaroller Down Stairs', player).c_switch()
|
||||||
|
world.get_door('TR Crystal Maze ES', player).c_switch()
|
||||||
|
world.get_door('TR Crystal Maze North Stairs', player).c_switch()
|
||||||
|
|
||||||
# nifty dynamic logical doors:
|
# nifty dynamic logical doors:
|
||||||
south_controller = world.get_door('Ice Cross Bottom SE', player)
|
south_controller = world.get_door('Ice Cross Bottom SE', player)
|
||||||
east_controller = world.get_door('Ice Cross Right ES', player)
|
east_controller = world.get_door('Ice Cross Right ES', player)
|
||||||
@@ -938,8 +1015,8 @@ def create_doors(world, player):
|
|||||||
def create_paired_doors(world, player):
|
def create_paired_doors(world, player):
|
||||||
world.paired_doors[player] = [
|
world.paired_doors[player] = [
|
||||||
PairedDoor('Sewers Secret Room Key Door S', 'Sewers Key Rat Key Door N'),
|
PairedDoor('Sewers Secret Room Key Door S', 'Sewers Key Rat Key Door N'),
|
||||||
# PairedDoor('', ''), # TR Pokey Key
|
PairedDoor('TR Pokey 2 ES', 'TR Lava Island WS'), # TR Pokey Key
|
||||||
# PairedDoor('', ''), # TR Big key door by pipes
|
PairedDoor('TR Dodgers NE', 'TR Lava Escape SE'), # TR Big key door by pipes
|
||||||
PairedDoor('PoD Falling Bridge WN', 'PoD Dark Maze EN'), # Pod Dark maze door
|
PairedDoor('PoD Falling Bridge WN', 'PoD Dark Maze EN'), # Pod Dark maze door
|
||||||
PairedDoor('PoD Dark Maze E', 'PoD Big Chest Balcony W'), # PoD Bombable by Big Chest
|
PairedDoor('PoD Dark Maze E', 'PoD Big Chest Balcony W'), # PoD Bombable by Big Chest
|
||||||
PairedDoor('PoD Arena Main NW', 'PoD Falling Bridge SW'), # Pod key door by bridge
|
PairedDoor('PoD Arena Main NW', 'PoD Falling Bridge SW'), # Pod key door by bridge
|
||||||
@@ -966,14 +1043,14 @@ def create_paired_doors(world, player):
|
|||||||
PairedDoor('Mire Fishbone SE', 'Mire Spike Barrier NE'), # mire fishbone key door
|
PairedDoor('Mire Fishbone SE', 'Mire Spike Barrier NE'), # mire fishbone key door
|
||||||
PairedDoor('Mire BK Door Room N', 'Mire Left Bridge S'), # mire big key door to bridges
|
PairedDoor('Mire BK Door Room N', 'Mire Left Bridge S'), # mire big key door to bridges
|
||||||
PairedDoor('Eastern Big Key NE', 'Eastern Hint Tile Blocked Path SE'),
|
PairedDoor('Eastern Big Key NE', 'Eastern Hint Tile Blocked Path SE'),
|
||||||
# PairedDoor('', ''), # TR somaria hub to pokey
|
PairedDoor('TR Hub NW', 'TR Pokey 1 SW'), # TR somaria hub to pokey
|
||||||
PairedDoor('Eastern Dark Square Key Door WN', 'Eastern Cannonball Ledge Key Door EN'),
|
PairedDoor('Eastern Dark Square Key Door WN', 'Eastern Cannonball Ledge Key Door EN'),
|
||||||
PairedDoor('Thieves Rail Ledge NW', 'Thieves Pot Alcove Bottom SW'), # TT random bomb to pots
|
PairedDoor('Thieves Rail Ledge NW', 'Thieves Pot Alcove Bottom SW'), # TT random bomb to pots
|
||||||
PairedDoor('Thieves BK Corner NE', 'Thieves Hallway SE'), # TT big key door
|
PairedDoor('Thieves BK Corner NE', 'Thieves Hallway SE'), # TT big key door
|
||||||
PairedDoor('Ice Switch Room ES', 'Ice Refill WS'), # Ice last key door to crystal switch
|
PairedDoor('Ice Switch Room ES', 'Ice Refill WS'), # Ice last key door to crystal switch
|
||||||
PairedDoor('Mire Hub WS', 'Mire Conveyor Crystal ES'), # mire hub key door to attic
|
PairedDoor('Mire Hub WS', 'Mire Conveyor Crystal ES'), # mire hub key door to attic
|
||||||
PairedDoor('Mire Hub Right EN', 'Mire Map Spot WN'), # mire hub key door to map
|
PairedDoor('Mire Hub Right EN', 'Mire Map Spot WN'), # mire hub key door to map
|
||||||
# PairedDoor('', ''), # tr last key door to switch maze
|
PairedDoor('TR Dash Bridge WS', 'TR Crystal Maze ES'), # tr last key door to switch maze
|
||||||
PairedDoor('Thieves Ambush E', 'Thieves Rail Ledge W') # TT dashable above
|
PairedDoor('Thieves Ambush E', 'Thieves Rail Ledge W') # TT dashable above
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,13 @@ def generate_dungeon(available_sectors, entrance_region_names, world, player):
|
|||||||
depth = 0
|
depth = 0
|
||||||
dungeon_cache = {}
|
dungeon_cache = {}
|
||||||
backtrack = False
|
backtrack = False
|
||||||
|
itr = 0
|
||||||
# last_choice = None
|
# last_choice = None
|
||||||
while len(proposed_map) < len(doors_to_connect):
|
while len(proposed_map) < len(doors_to_connect):
|
||||||
# what are my choices?
|
# what are my choices?
|
||||||
|
itr += 1
|
||||||
|
if itr > 5000:
|
||||||
|
raise Exception('Generation taking too long. Ref %s' % entrance_region_names[0])
|
||||||
if depth not in dungeon_cache.keys():
|
if depth not in dungeon_cache.keys():
|
||||||
dungeon, hangers, hooks = gen_dungeon_info(available_sectors, entrance_regions, proposed_map, doors_to_connect, world, player)
|
dungeon, hangers, hooks = gen_dungeon_info(available_sectors, entrance_regions, proposed_map, doors_to_connect, world, player)
|
||||||
dungeon_cache[depth] = dungeon, hangers, hooks
|
dungeon_cache[depth] = dungeon, hangers, hooks
|
||||||
@@ -234,6 +238,14 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
|||||||
if len(hooks[key]) > 0 and len(hangers[key]) == 0:
|
if len(hooks[key]) > 0 and len(hangers[key]) == 0:
|
||||||
return False
|
return False
|
||||||
# todo: stonewall - check that there's no hook-only that is without a matching hanger
|
# todo: stonewall - check that there's no hook-only that is without a matching hanger
|
||||||
|
outstanding_doors = defaultdict(list)
|
||||||
|
for d in doors_to_connect:
|
||||||
|
if d not in proposed_map.keys():
|
||||||
|
outstanding_doors[hook_from_door(d)].append(d)
|
||||||
|
for key in outstanding_doors.keys():
|
||||||
|
opp_key = opposite_h_type(key)
|
||||||
|
if len(outstanding_doors[key]) > 0 and len(hangers[key]) == 0 and len(hooks[opp_key]) == 0:
|
||||||
|
return False
|
||||||
all_visited = set()
|
all_visited = set()
|
||||||
for piece in dungeon.values():
|
for piece in dungeon.values():
|
||||||
all_visited.update(piece.visited_regions)
|
all_visited.update(piece.visited_regions)
|
||||||
@@ -277,7 +289,7 @@ def winnow_hangers(hangers, hooks):
|
|||||||
found_valid = False
|
found_valid = False
|
||||||
for door_hook, crystal, orig_hanger in hook_set:
|
for door_hook, crystal, orig_hanger in hook_set:
|
||||||
if orig_hanger != door:
|
if orig_hanger != door:
|
||||||
found_valid = True
|
found_valid = True # todo: break
|
||||||
if not found_valid:
|
if not found_valid:
|
||||||
removal_info.append((hanger, door))
|
removal_info.append((hanger, door))
|
||||||
for hanger, door in removal_info:
|
for hanger, door in removal_info:
|
||||||
@@ -322,6 +334,18 @@ def parent_region(door, world, player):
|
|||||||
return world.get_entrance(door.name, player)
|
return world.get_entrance(door.name, player)
|
||||||
|
|
||||||
|
|
||||||
|
def opposite_h_type(h_type):
|
||||||
|
type_map = {
|
||||||
|
Hook.Stairs: Hook.Stairs,
|
||||||
|
Hook.North: Hook.South,
|
||||||
|
Hook.South: Hook.North,
|
||||||
|
Hook.West: Hook.East,
|
||||||
|
Hook.East: Hook.West,
|
||||||
|
|
||||||
|
}
|
||||||
|
return type_map[h_type]
|
||||||
|
|
||||||
|
|
||||||
def hook_from_door(door):
|
def hook_from_door(door):
|
||||||
if door.type == DoorType.SpiralStairs:
|
if door.type == DoorType.SpiralStairs:
|
||||||
return Hook.Stairs
|
return Hook.Stairs
|
||||||
@@ -565,8 +589,8 @@ class ExplorationState(object):
|
|||||||
self.append_door_to_list(door, self.avail_doors)
|
self.append_door_to_list(door, self.avail_doors)
|
||||||
|
|
||||||
def add_all_doors_check_proposed(self, region, proposed_map, valid_doors, world, player):
|
def add_all_doors_check_proposed(self, region, proposed_map, valid_doors, world, player):
|
||||||
for door in get_dungeon_doors(region, world, player):
|
for door in get_doors(world, region, player):
|
||||||
if self.can_traverse(door):
|
if self.can_traverse_bk_check(door):
|
||||||
if door.controller is not None:
|
if door.controller is not None:
|
||||||
door = door.controller
|
door = door.controller
|
||||||
if door.dest is None and door not in proposed_map.keys() and door in valid_doors:
|
if door.dest is None and door not in proposed_map.keys() and door in valid_doors:
|
||||||
@@ -628,6 +652,14 @@ class ExplorationState(object):
|
|||||||
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def can_traverse_bk_check(self, door):
|
||||||
|
if door.blocked:
|
||||||
|
return False
|
||||||
|
if door.crystal not in [CrystalBarrier.Null, CrystalBarrier.Either]:
|
||||||
|
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
||||||
|
return not door.bigKey or len(self.found_locations) > 0
|
||||||
|
# return not door.bigKey or len([x for x in self.found_locations if '- Prize' not in x.name]) > 0
|
||||||
|
|
||||||
def validate(self, door, region, world):
|
def validate(self, door, region, world):
|
||||||
return self.can_traverse(door) and not self.visited(region) and valid_region_to_explore(region, world)
|
return self.can_traverse(door) and not self.visited(region) and valid_region_to_explore(region, world)
|
||||||
|
|
||||||
|
|||||||
22
Dungeons.py
22
Dungeons.py
@@ -25,7 +25,7 @@ def create_dungeons(world, 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))
|
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_regions, ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
|
IP = make_dungeon('Ice Palace', 'Kholdstare', ice_regions, 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', mire_regions, ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
|
MM = make_dungeon('Misery Mire', 'Vitreous', mire_regions, ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
|
||||||
TR = make_dungeon('Turtle Rock', 'Trinexx', ['Turtle Rock (Entrance)', 'Turtle Rock (First Section)', 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Turtle Rock (Crystaroller Room)', 'Turtle Rock (Dark Room)', 'Turtle Rock (Eye Bridge)', 'Turtle Rock (Trinexx)'], ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
|
TR = make_dungeon('Turtle Rock', 'Trinexx', tr_regions, ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
|
||||||
|
|
||||||
if world.mode != 'inverted':
|
if world.mode != 'inverted':
|
||||||
AT = make_dungeon('Agahnims Tower', 'Agahnim', tower_regions, None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
|
AT = make_dungeon('Agahnims Tower', 'Agahnim', tower_regions, None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
|
||||||
@@ -269,6 +269,14 @@ mire_regions = [
|
|||||||
'Mire Falling Foes', 'Mire Firesnake Skip', 'Mire Antechamber', 'Mire Boss'
|
'Mire Falling Foes', 'Mire Firesnake Skip', 'Mire Antechamber', 'Mire Boss'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
tr_regions = [
|
||||||
|
'TR Main Lobby', 'TR Lobby Ledge', 'TR Compass Room', 'TR Hub', 'TR Torches Ledge', 'TR Torches', 'TR Roller Room',
|
||||||
|
'TR Tile Room', 'TR Refill', 'TR Pokey 1', 'TR Chain Chomps', 'TR Pipe Pit', 'TR Pipe Ledge', 'TR Lava Dual Pipes',
|
||||||
|
'TR Lava Island', 'TR Lava Escape', 'TR Pokey 2', 'TR Twin Pokeys', 'TR Hallway', 'TR Dodgers', 'TR Big View',
|
||||||
|
'TR Big Chest', 'TR Big Chest Entrance', 'TR Lazy Eyes', 'TR Dash Room', 'TR Tongue Pull', 'TR Rupees',
|
||||||
|
'TR Crystaroller', 'TR Dark Ride', 'TR Dash Bridge', 'TR Eye Bridge', 'TR Crystal Maze', 'TR Final Abyss', 'TR Boss'
|
||||||
|
]
|
||||||
|
|
||||||
dungeon_regions = {
|
dungeon_regions = {
|
||||||
'Hyrule Castle': hyrule_castle_regions,
|
'Hyrule Castle': hyrule_castle_regions,
|
||||||
'Eastern Palace': eastern_regions,
|
'Eastern Palace': eastern_regions,
|
||||||
@@ -281,12 +289,12 @@ dungeon_regions = {
|
|||||||
'Thieves Town': thieves_regions,
|
'Thieves Town': thieves_regions,
|
||||||
'Ice Palace': ice_regions,
|
'Ice Palace': ice_regions,
|
||||||
'Misery Mire': mire_regions,
|
'Misery Mire': mire_regions,
|
||||||
# 'TR':
|
'Turtle Rock': tr_regions,
|
||||||
# 'GT':
|
# 'GT':
|
||||||
}
|
}
|
||||||
|
|
||||||
region_starts = {
|
region_starts = {
|
||||||
'Hyrule Castle': ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Secret Room', 'Sanctuary'],
|
'Hyrule Castle': ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Sewers Rat Path', 'Sanctuary'],
|
||||||
'Eastern Palace': ['Eastern Lobby'],
|
'Eastern Palace': ['Eastern Lobby'],
|
||||||
'Desert Palace': ['Desert Back Lobby', 'Desert Main Lobby', 'Desert West Lobby', 'Desert East Lobby'],
|
'Desert Palace': ['Desert Back Lobby', 'Desert Main Lobby', 'Desert West Lobby', 'Desert East Lobby'],
|
||||||
'Tower of Hera': ['Hera Lobby'],
|
'Tower of Hera': ['Hera Lobby'],
|
||||||
@@ -298,7 +306,7 @@ region_starts = {
|
|||||||
'Thieves Town': ['Thieves Lobby'],
|
'Thieves Town': ['Thieves Lobby'],
|
||||||
'Ice Palace': ['Ice Lobby'],
|
'Ice Palace': ['Ice Lobby'],
|
||||||
'Misery Mire': ['Mire Lobby'],
|
'Misery Mire': ['Mire Lobby'],
|
||||||
# ['TR Main Lobby', 'TR Eye Trap', 'TR Big Chest', 'TR Laser Bridge'],
|
'Turtle Rock': ['TR Main Lobby', 'TR Lazy Eyes', 'TR Big Chest Entrance', 'TR Eye Bridge'],
|
||||||
# ['GT Lobby']
|
# ['GT Lobby']
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,6 +322,10 @@ split_region_starts = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flexible_starts = {
|
||||||
|
'Skull Woods': ['Skull Left Drop', 'Skull Pinball']
|
||||||
|
}
|
||||||
|
|
||||||
dungeon_keys = {
|
dungeon_keys = {
|
||||||
'Hyrule Castle': 'Small Key (Escape)',
|
'Hyrule Castle': 'Small Key (Escape)',
|
||||||
'Eastern Palace': 'Small Key (Eastern Palace)',
|
'Eastern Palace': 'Small Key (Eastern Palace)',
|
||||||
@@ -326,6 +338,7 @@ dungeon_keys = {
|
|||||||
'Thieves Town': 'Small Key (Thieves Town)',
|
'Thieves Town': 'Small Key (Thieves Town)',
|
||||||
'Ice Palace': 'Small Key (Ice Palace)',
|
'Ice Palace': 'Small Key (Ice Palace)',
|
||||||
'Misery Mire': 'Small Key (Misery Mire)',
|
'Misery Mire': 'Small Key (Misery Mire)',
|
||||||
|
'Turtle Rock': 'Small Key (Turtle Rock)',
|
||||||
}
|
}
|
||||||
|
|
||||||
dungeon_bigs = {
|
dungeon_bigs = {
|
||||||
@@ -340,5 +353,6 @@ dungeon_bigs = {
|
|||||||
'Thieves Town': 'Big Key (Thieves Town)',
|
'Thieves Town': 'Big Key (Thieves Town)',
|
||||||
'Ice Palace': 'Big Key (Ice Palace)',
|
'Ice Palace': 'Big Key (Ice Palace)',
|
||||||
'Misery Mire': 'Big Key (Misery Mire)',
|
'Misery Mire': 'Big Key (Misery Mire)',
|
||||||
|
'Turtle Rock': 'Big Key (Turtle Rock)',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2860,7 +2860,7 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
|
|||||||
('Desert Ledge Return Rocks', 'Desert Ledge'),
|
('Desert Ledge Return Rocks', 'Desert Ledge'),
|
||||||
('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'),
|
('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'),
|
||||||
('Hyrule Castle Main Gate', 'Hyrule Castle Courtyard'),
|
('Hyrule Castle Main Gate', 'Hyrule Castle Courtyard'),
|
||||||
('Sewer Drop', 'Sewers Secret Room'),
|
('Sewer Drop', 'Sewers Rat Path'),
|
||||||
('Flute Spot 1', 'Death Mountain'),
|
('Flute Spot 1', 'Death Mountain'),
|
||||||
('Death Mountain Entrance Rock', 'Death Mountain Entrance'),
|
('Death Mountain Entrance Rock', 'Death Mountain Entrance'),
|
||||||
('Death Mountain Entrance Drop', 'Light World'),
|
('Death Mountain Entrance Drop', 'Light World'),
|
||||||
@@ -2947,20 +2947,6 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
|
|||||||
('Cave 45 Mirror Spot', 'Cave 45 Ledge'),
|
('Cave 45 Mirror Spot', 'Cave 45 Ledge'),
|
||||||
('Graveyard Ledge Mirror Spot', 'Graveyard Ledge'),
|
('Graveyard Ledge Mirror Spot', 'Graveyard Ledge'),
|
||||||
|
|
||||||
('Turtle Rock Entrance Gap', 'Turtle Rock (First Section)'),
|
|
||||||
('Turtle Rock Entrance Gap Reverse', 'Turtle Rock (Entrance)'),
|
|
||||||
('Turtle Rock Pokey Room', 'Turtle Rock (Chain Chomp Room)'),
|
|
||||||
('Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Second Section)'),
|
|
||||||
('Turtle Rock (Chain Chomp Room) (South)', 'Turtle Rock (First Section)'),
|
|
||||||
('Turtle Rock Chain Chomp Staircase', 'Turtle Rock (Chain Chomp Room)'),
|
|
||||||
('Turtle Rock (Big Chest) (North)', 'Turtle Rock (Second Section)'),
|
|
||||||
('Turtle Rock Big Key Door', 'Turtle Rock (Crystaroller Room)'),
|
|
||||||
('Turtle Rock Big Key Door Reverse', 'Turtle Rock (Second Section)'),
|
|
||||||
('Turtle Rock Dark Room Staircase', 'Turtle Rock (Dark Room)'),
|
|
||||||
('Turtle Rock (Dark Room) (North)', 'Turtle Rock (Crystaroller Room)'),
|
|
||||||
('Turtle Rock (Dark Room) (South)', 'Turtle Rock (Eye Bridge)'),
|
|
||||||
('Turtle Rock Dark Room (South)', 'Turtle Rock (Dark Room)'),
|
|
||||||
('Turtle Rock (Trinexx)', 'Turtle Rock (Trinexx)'),
|
|
||||||
('Ganons Tower (Tile Room)', 'Ganons Tower (Tile Room)'),
|
('Ganons Tower (Tile Room)', 'Ganons Tower (Tile Room)'),
|
||||||
('Ganons Tower (Tile Room) Key Door', 'Ganons Tower (Compass Room)'),
|
('Ganons Tower (Tile Room) Key Door', 'Ganons Tower (Compass Room)'),
|
||||||
('Ganons Tower (Bottom) (East)', 'Ganons Tower (Bottom)'),
|
('Ganons Tower (Bottom) (East)', 'Ganons Tower (Bottom)'),
|
||||||
@@ -3517,14 +3503,14 @@ default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Main L
|
|||||||
('Swamp Palace', 'Swamp Lobby'), # requires additional patch for flooding moat if moved
|
('Swamp Palace', 'Swamp Lobby'), # requires additional patch for flooding moat if moved
|
||||||
('Swamp Palace Exit', 'South Dark World'),
|
('Swamp Palace Exit', 'South Dark World'),
|
||||||
|
|
||||||
('Turtle Rock', 'Turtle Rock (Entrance)'),
|
('Turtle Rock', 'TR Main Lobby'),
|
||||||
('Turtle Rock Exit (Front)', 'Dark Death Mountain (Top)'),
|
('Turtle Rock Exit (Front)', 'Dark Death Mountain (Top)'),
|
||||||
('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'),
|
('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'),
|
||||||
('Turtle Rock Ledge Exit (East)', 'Dark Death Mountain Ledge'),
|
('Turtle Rock Ledge Exit (East)', 'Dark Death Mountain Ledge'),
|
||||||
('Dark Death Mountain Ledge (West)', 'Turtle Rock (Second Section)'),
|
('Dark Death Mountain Ledge (West)', 'TR Lazy Eyes'),
|
||||||
('Dark Death Mountain Ledge (East)', 'Turtle Rock (Big Chest)'),
|
('Dark Death Mountain Ledge (East)', 'TR Big Chest Entrance'),
|
||||||
('Turtle Rock Isolated Ledge Exit', 'Dark Death Mountain Isolated Ledge'),
|
('Turtle Rock Isolated Ledge Exit', 'Dark Death Mountain Isolated Ledge'),
|
||||||
('Turtle Rock Isolated Ledge Entrance', 'Turtle Rock (Eye Bridge)'),
|
('Turtle Rock Isolated Ledge Entrance', 'TR Eye Bridge'),
|
||||||
|
|
||||||
('Ganons Tower', 'Ganons Tower (Entrance)'),
|
('Ganons Tower', 'Ganons Tower (Entrance)'),
|
||||||
('Ganons Tower Exit', 'Dark Death Mountain (Top)')
|
('Ganons Tower Exit', 'Dark Death Mountain (Top)')
|
||||||
|
|||||||
6
Main.py
6
Main.py
@@ -24,7 +24,7 @@ from Utils import output_path
|
|||||||
__version__ = '0.0.1-pre'
|
__version__ = '0.0.1-pre'
|
||||||
|
|
||||||
def main(args, seed=None):
|
def main(args, seed=None):
|
||||||
start = time.clock()
|
start = time.process_time()
|
||||||
|
|
||||||
# initialize the world
|
# initialize the world
|
||||||
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)
|
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)
|
||||||
@@ -92,7 +92,7 @@ def main(args, seed=None):
|
|||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
all_state = world.get_all_state(keys=True)
|
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', 'Thieves Boss', 'Ice Boss']:
|
'Skull Boss', 'Thieves Boss', 'Ice Boss', 'Mire Boss', 'TR Boss']:
|
||||||
if world.get_region(bossregion, player) not in all_state.reachable_regions[player]:
|
if world.get_region(bossregion, player) not in all_state.reachable_regions[player]:
|
||||||
raise Exception(bossregion + ' missing from generation')
|
raise Exception(bossregion + ' missing from generation')
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ def main(args, seed=None):
|
|||||||
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
||||||
|
|
||||||
logger.info('Done. Enjoy.')
|
logger.info('Done. Enjoy.')
|
||||||
logger.debug('Total Time: %s', time.clock() - start)
|
logger.debug('Total Time: %s', time.process_time() - start)
|
||||||
|
|
||||||
return world
|
return world
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from Main import create_playthrough
|
|||||||
__version__ = '0.2-dev'
|
__version__ = '0.2-dev'
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
start_time = time.clock()
|
start_time = time.process_time()
|
||||||
|
|
||||||
# initialize the world
|
# initialize the world
|
||||||
world = World(1, 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, False, None, 'none', False)
|
world = World(1, 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, False, None, 'none', False)
|
||||||
@@ -89,7 +89,7 @@ def main(args):
|
|||||||
world.spoiler.to_file('%s_Spoiler.txt' % outfilebase)
|
world.spoiler.to_file('%s_Spoiler.txt' % outfilebase)
|
||||||
|
|
||||||
logger.info('Done. Enjoy.')
|
logger.info('Done. Enjoy.')
|
||||||
logger.debug('Total Time: %s', time.clock() - start_time)
|
logger.debug('Total Time: %s', time.process_time() - start_time)
|
||||||
|
|
||||||
return world
|
return world
|
||||||
|
|
||||||
|
|||||||
54
Regions.py
54
Regions.py
@@ -193,18 +193,7 @@ def create_regions(world, player):
|
|||||||
create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
|
create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
|
||||||
create_cave_region(player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
|
create_cave_region(player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
|
||||||
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
|
|
||||||
'Turtle Rock - Roller Room - Right'], ['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Second Section)', 'Turtle Rock', ['Turtle Rock - Big Key Chest'], ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
|
|
||||||
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
|
|
||||||
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']),
|
|
||||||
create_dungeon_region(player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
|
|
||||||
create_dungeon_region(player, 'Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'],
|
create_dungeon_region(player, 'Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'],
|
||||||
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']),
|
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']),
|
||||||
create_dungeon_region(player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
|
create_dungeon_region(player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
|
||||||
@@ -612,6 +601,43 @@ def create_regions(world, player):
|
|||||||
create_dungeon_region(player, 'Mire Boss', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize'], ['Mire Boss SW']),
|
create_dungeon_region(player, 'Mire Boss', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize'], ['Mire Boss SW']),
|
||||||
|
|
||||||
# tr
|
# tr
|
||||||
|
create_dungeon_region(player, 'TR Main Lobby', 'Turtle Rock', None, ['TR Main Lobby Gap', 'Turtle Rock Exit (Front)']),
|
||||||
|
create_dungeon_region(player, 'TR Lobby Ledge', 'Turtle Rock', None, ['TR Lobby Ledge NE', 'TR Lobby Ledge Gap']),
|
||||||
|
create_dungeon_region(player, 'TR Compass Room', 'Turtle Rock', ['Turtle Rock - Compass Chest'], ['TR Compass Room NW']),
|
||||||
|
create_dungeon_region(player, 'TR Hub', 'Turtle Rock', None, ['TR Hub SW', 'TR Hub SE', 'TR Hub ES', 'TR Hub EN', 'TR Hub NW', 'TR Hub NE']),
|
||||||
|
create_dungeon_region(player, 'TR Torches Ledge', 'Turtle Rock', None, ['TR Torches Ledge WS']),
|
||||||
|
create_dungeon_region(player, 'TR Torches', 'Turtle Rock', None, ['TR Torches WN', 'TR Torches NW']),
|
||||||
|
create_dungeon_region(player, 'TR Roller Room', 'Turtle Rock', ['Turtle Rock - Roller Room - Left', 'Turtle Rock - Roller Room - Right'], ['TR Roller Room SW']),
|
||||||
|
create_dungeon_region(player, 'TR Tile Room', 'Turtle Rock', None, ['TR Tile Room SE', 'TR Tile Room NE']),
|
||||||
|
create_dungeon_region(player, 'TR Refill', 'Turtle Rock', None, ['TR Refill SE']),
|
||||||
|
create_dungeon_region(player, 'TR Pokey 1', 'Turtle Rock', ['Turtle Rock - Pokey 1 Key Drop'], ['TR Pokey 1 SW', 'TR Pokey 1 NW']),
|
||||||
|
create_dungeon_region(player, 'TR Chain Chomps', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['TR Chain Chomps SW', 'TR Chain Chomps Down Stairs']),
|
||||||
|
create_dungeon_region(player, 'TR Pipe Pit', 'Turtle Rock', None, ['TR Pipe Pit Up Stairs', 'TR Pipe Pit WN']),
|
||||||
|
create_dungeon_region(player, 'TR Pipe Ledge', 'Turtle Rock', None, ['TR Pipe Ledge WS', 'TR Pipe Ledge Drop Down']),
|
||||||
|
create_dungeon_region(player, 'TR Lava Dual Pipes', 'Turtle Rock', None, ['TR Lava Dual Pipes EN', 'TR Lava Dual Pipes WN', 'TR Lava Dual Pipes SW']),
|
||||||
|
create_dungeon_region(player, 'TR Lava Island', 'Turtle Rock', ['Turtle Rock - Big Key Chest'], ['TR Lava Island WS', 'TR Lava Island ES']),
|
||||||
|
create_dungeon_region(player, 'TR Lava Escape', 'Turtle Rock', None, ['TR Lava Escape SE', 'TR Lava Escape NW']),
|
||||||
|
create_dungeon_region(player, 'TR Pokey 2', 'Turtle Rock', ['Turtle Rock - Pokey 2 Key Drop'], ['TR Pokey 2 EN', 'TR Pokey 2 ES']),
|
||||||
|
create_dungeon_region(player, 'TR Twin Pokeys', 'Turtle Rock', None, ['TR Twin Pokeys NW', 'TR Twin Pokeys EN', 'TR Twin Pokeys SW']),
|
||||||
|
create_dungeon_region(player, 'TR Hallway', 'Turtle Rock', None, ['TR Hallway NW', 'TR Hallway ES', 'TR Hallway WS']),
|
||||||
|
create_dungeon_region(player, 'TR Dodgers', 'Turtle Rock', None, ['TR Dodgers WN', 'TR Dodgers SE', 'TR Dodgers NE']),
|
||||||
|
create_dungeon_region(player, 'TR Big View', 'Turtle Rock', None, ['TR Big View WS']),
|
||||||
|
create_dungeon_region(player, 'TR Big Chest', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['TR Big Chest Gap', 'TR Big Chest NE']),
|
||||||
|
create_dungeon_region(player, 'TR Big Chest Entrance', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (East)', 'TR Big Chest Entrance Gap']),
|
||||||
|
create_dungeon_region(player, 'TR Lazy Eyes', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (West)', 'TR Lazy Eyes ES']),
|
||||||
|
create_dungeon_region(player, 'TR Dash Room', 'Turtle Rock', None, ['TR Dash Room SW', 'TR Dash Room ES', 'TR Dash Room NW']),
|
||||||
|
create_dungeon_region(player, 'TR Tongue Pull', 'Turtle Rock', None, ['TR Tongue Pull WS', 'TR Tongue Pull NE']),
|
||||||
|
create_dungeon_region(player, 'TR Rupees', 'Turtle Rock', None, ['TR Rupees SE']),
|
||||||
|
create_dungeon_region(player, 'TR Crystaroller', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['TR Crystaroller SW', 'TR Crystaroller Down Stairs']),
|
||||||
|
create_dungeon_region(player, 'TR Dark Ride', 'Turtle Rock', None, ['TR Dark Ride Up Stairs', 'TR Dark Ride SW']),
|
||||||
|
create_dungeon_region(player, 'TR Dash Bridge', 'Turtle Rock', None, ['TR Dash Bridge NW', 'TR Dash Bridge SW', 'TR Dash Bridge WS']),
|
||||||
|
create_dungeon_region(player, 'TR Eye Bridge', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
|
||||||
|
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
|
||||||
|
['Turtle Rock Isolated Ledge Exit', 'TR Eye Bridge NW']),
|
||||||
|
create_dungeon_region(player, 'TR Crystal Maze', 'Turtle Rock', None, ['TR Crystal Maze ES', 'TR Crystal Maze North Stairs']),
|
||||||
|
create_dungeon_region(player, 'TR Final Abyss', 'Turtle Rock', None, ['TR Final Abyss South Stairs', 'TR Final Abyss NW']),
|
||||||
|
create_dungeon_region(player, 'TR Boss', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize'], ['TR Boss SW']),
|
||||||
|
|
||||||
# gt
|
# gt
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -750,6 +776,8 @@ key_only_locations = {
|
|||||||
'Misery Mire - Spikes Pot Key': 'Small Key (Misery Mire)',
|
'Misery Mire - Spikes Pot Key': 'Small Key (Misery Mire)',
|
||||||
'Misery Mire - Fishbone Pot Key': 'Small Key (Misery Mire)',
|
'Misery Mire - Fishbone Pot Key': 'Small Key (Misery Mire)',
|
||||||
'Misery Mire - Conveyor Crystal Key Drop': 'Small Key (Misery Mire)',
|
'Misery Mire - Conveyor Crystal Key Drop': 'Small Key (Misery Mire)',
|
||||||
|
'Turtle Rock - Pokey 1 Key Drop': 'Small Key (Turtle Rock)',
|
||||||
|
'Turtle Rock - Pokey 2 Key Drop': 'Small Key (Turtle Rock)',
|
||||||
}
|
}
|
||||||
|
|
||||||
dungeon_events = [
|
dungeon_events = [
|
||||||
@@ -767,8 +795,6 @@ flooded_keys_reverse = {
|
|||||||
'Swamp Palace - Trench 2 Pot Key': 'Trench 2 Switch'
|
'Swamp Palace - Trench 2 Pot Key': 'Trench 2 Switch'
|
||||||
}
|
}
|
||||||
|
|
||||||
# todo: escape big key? - should be separate from above for dungeon key layout validation
|
|
||||||
|
|
||||||
location_table = {'Mushroom': (0x180013, False, 'in the woods'),
|
location_table = {'Mushroom': (0x180013, False, 'in the woods'),
|
||||||
'Bottle Merchant': (0x2eb18, False, 'with a merchant'),
|
'Bottle Merchant': (0x2eb18, False, 'with a merchant'),
|
||||||
'Flute Spot': (0x18014a, False, 'underground'),
|
'Flute Spot': (0x18014a, False, 'underground'),
|
||||||
|
|||||||
2
Rom.py
2
Rom.py
@@ -18,7 +18,7 @@ from EntranceShuffle import door_addresses
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = 'd5905bee86bf7c2c4964a61fdff057e9'
|
RANDOMIZERBASEHASH = '08d53b2249fc1598be1b94c537d0feb5'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ def create_rooms(world, player):
|
|||||||
Room(player, 0x60, 0x51309).door(Position.NorthE2, DoorKind.NormalLow2).door(Position.East2, DoorKind.NormalLow2).door(Position.East2, DoorKind.ToggleFlag).door(Position.EastN, DoorKind.Normal).door(Position.SouthE, DoorKind.Normal).door(Position.SouthE, DoorKind.IncognitoEntrance),
|
Room(player, 0x60, 0x51309).door(Position.NorthE2, DoorKind.NormalLow2).door(Position.East2, DoorKind.NormalLow2).door(Position.East2, DoorKind.ToggleFlag).door(Position.EastN, DoorKind.Normal).door(Position.SouthE, DoorKind.Normal).door(Position.SouthE, DoorKind.IncognitoEntrance),
|
||||||
Room(player, 0x61, 0x51454).door(Position.West2, DoorKind.NormalLow).door(Position.West2, DoorKind.ToggleFlag).door(Position.East2, DoorKind.NormalLow).door(Position.East2, DoorKind.ToggleFlag).door(Position.South2, DoorKind.NormalLow).door(Position.South2, DoorKind.IncognitoEntrance).door(Position.WestN, DoorKind.Normal),
|
Room(player, 0x61, 0x51454).door(Position.West2, DoorKind.NormalLow).door(Position.West2, DoorKind.ToggleFlag).door(Position.East2, DoorKind.NormalLow).door(Position.East2, DoorKind.ToggleFlag).door(Position.South2, DoorKind.NormalLow).door(Position.South2, DoorKind.IncognitoEntrance).door(Position.WestN, DoorKind.Normal),
|
||||||
Room(player, 0x62, 0x51577).door(Position.West2, DoorKind.NormalLow2).door(Position.West2, DoorKind.ToggleFlag).door(Position.NorthW2, DoorKind.NormalLow2).door(Position.North, DoorKind.Normal).door(Position.SouthW, DoorKind.Normal).door(Position.SouthW, DoorKind.IncognitoEntrance),
|
Room(player, 0x62, 0x51577).door(Position.West2, DoorKind.NormalLow2).door(Position.West2, DoorKind.ToggleFlag).door(Position.NorthW2, DoorKind.NormalLow2).door(Position.North, DoorKind.Normal).door(Position.SouthW, DoorKind.Normal).door(Position.SouthW, DoorKind.IncognitoEntrance),
|
||||||
Room(player, 0x63, 0xf88ed).door(Position.NorthE, DoorKind.StairKey).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.SouthW, DoorKind.DungeonEntrance), # todo: looks like a huge typo - I had to guess on StairKey
|
Room(player, 0x63, 0xf88ed).door(Position.NorthE, DoorKind.StairKey).door(Position.InteriorW, DoorKind.TrapTriggerable).door(Position.SouthW, DoorKind.DungeonEntrance), # looked like a huge typo - I had to guess on StairKey
|
||||||
Room(player, 0x64, 0xfda53).door(Position.InteriorS, DoorKind.Trap2),
|
Room(player, 0x64, 0xfda53).door(Position.InteriorS, DoorKind.Trap2),
|
||||||
Room(player, 0x65, 0xfdac5).door(Position.InteriorS, DoorKind.Normal),
|
Room(player, 0x65, 0xfdac5).door(Position.InteriorS, DoorKind.Normal),
|
||||||
Room(player, 0x66, 0xfa01b).door(Position.InteriorE2, DoorKind.Waterfall).door(Position.SouthW2, DoorKind.NormalLow2).door(Position.SouthW2, DoorKind.ToggleFlag).door(Position.InteriorW2, DoorKind.NormalLow2),
|
Room(player, 0x66, 0xfa01b).door(Position.InteriorE2, DoorKind.Waterfall).door(Position.SouthW2, DoorKind.NormalLow2).door(Position.SouthW2, DoorKind.ToggleFlag).door(Position.InteriorW2, DoorKind.NormalLow2),
|
||||||
@@ -195,7 +195,7 @@ def create_rooms(world, player):
|
|||||||
Room(player, 0xd1, 0xfb259).door(Position.InteriorS, DoorKind.Trap2).door(Position.NorthW, DoorKind.Normal).door(Position.NorthE, DoorKind.Normal).door(Position.InteriorE, DoorKind.Normal),
|
Room(player, 0xd1, 0xfb259).door(Position.InteriorS, DoorKind.Trap2).door(Position.NorthW, DoorKind.Normal).door(Position.NorthE, DoorKind.Normal).door(Position.InteriorE, DoorKind.Normal),
|
||||||
Room(player, 0xd2, 0xfafd6).door(Position.NorthE, DoorKind.Trap),
|
Room(player, 0xd2, 0xfafd6).door(Position.NorthE, DoorKind.Trap),
|
||||||
Room(player, 0xd5, 0xfee40).door(Position.SouthW, DoorKind.BombableEntrance).door(Position.NorthW, DoorKind.Normal),
|
Room(player, 0xd5, 0xfee40).door(Position.SouthW, DoorKind.BombableEntrance).door(Position.NorthW, DoorKind.Normal),
|
||||||
Room(player, 0xd6, 0xfe1cb).door(Position.NorthW, DoorKind.UnknownD7).door(Position.SouthE, DoorKind.DungeonEntrance).door(Position.NorthE, DoorKind.Normal),
|
Room(player, 0xd6, 0xfe1cb).door(Position.NorthW, DoorKind.UnknownD6).door(Position.SouthE, DoorKind.DungeonEntrance).door(Position.NorthE, DoorKind.Normal),
|
||||||
Room(player, 0xd8, 0x515ed).door(Position.NorthE, DoorKind.Trap).door(Position.InteriorE, DoorKind.TrapTriggerable).door(Position.EastS, DoorKind.Normal),
|
Room(player, 0xd8, 0x515ed).door(Position.NorthE, DoorKind.Trap).door(Position.InteriorE, DoorKind.TrapTriggerable).door(Position.EastS, DoorKind.Normal),
|
||||||
Room(player, 0xd9, 0x5166f).door(Position.WestS, DoorKind.Trap).door(Position.InteriorS, DoorKind.Trap).door(Position.EastS, DoorKind.Trap),
|
Room(player, 0xd9, 0x5166f).door(Position.WestS, DoorKind.Trap).door(Position.InteriorS, DoorKind.Trap).door(Position.EastS, DoorKind.Trap),
|
||||||
Room(player, 0xda, 0x5169d).door(Position.WestS, DoorKind.Trap),
|
Room(player, 0xda, 0x5169d).door(Position.WestS, DoorKind.Trap),
|
||||||
@@ -326,7 +326,7 @@ class DoorKind(Enum):
|
|||||||
DungeonChanger = 0x14
|
DungeonChanger = 0x14
|
||||||
ToggleFlag = 0x16
|
ToggleFlag = 0x16
|
||||||
Trap = 0x18
|
Trap = 0x18
|
||||||
UnknownD7 = 0x1A
|
UnknownD6 = 0x1A
|
||||||
SmallKey = 0x1C
|
SmallKey = 0x1C
|
||||||
BigKey = 0x1E
|
BigKey = 0x1E
|
||||||
StairKey = 0x20
|
StairKey = 0x20
|
||||||
|
|||||||
60
Rules.py
60
Rules.py
@@ -403,29 +403,37 @@ def global_rules(world, player):
|
|||||||
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player))
|
||||||
|
|
||||||
add_key_logic_rules(world, player) # todo - vanilla shuffle rules
|
set_rule(world.get_entrance('TR Main Lobby Gap', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
# End of door rando rules.
|
set_rule(world.get_entrance('TR Lobby Ledge Gap', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
|
set_rule(world.get_entrance('TR Hub SW', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
set_rule(world.get_entrance('TR Hub SE', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
|
set_rule(world.get_entrance('TR Hub ES', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_entrance('Turtle Rock Entrance Gap', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Hub EN', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_entrance('Turtle Rock Entrance Gap Reverse', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Hub NW', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Compass Chest', player), lambda state: state.has('Cane of Somaria', player)) # We could get here from the middle section without Cane as we don't cross the entrance gap!
|
set_rule(world.get_entrance('TR Hub NE', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Roller Room - Left', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
|
set_rule(world.get_entrance('TR Torches NW', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Roller Room - Right', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
|
set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player) and (state.has('Cane of Somaria', player) or state.has('Hookshot', player)))
|
if world.accessibility == 'locations':
|
||||||
set_rule(world.get_entrance('Turtle Rock (Big Chest) (North)', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
forbid_item(world.get_location('Turtle Rock - Big Chest', player), 'Big Key (Turtle Rock)', player)
|
||||||
set_rule(world.get_entrance('Turtle Rock Big Key Door', player), lambda state: state.has('Big Key (Turtle Rock)', player))
|
set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
||||||
set_rule(world.get_entrance('Turtle Rock (Dark Room) (North)', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
||||||
set_rule(world.get_entrance('Turtle Rock (Dark Room) (South)', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Dodgers NE', player), lambda state: state.has('Big Key (Turtle Rock)', player))
|
||||||
|
set_rule(world.get_entrance('TR Dark Ride Up Stairs', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
|
set_rule(world.get_entrance('TR Dark Ride SW', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
|
set_rule(world.get_entrance('TR Final Abyss South Stairs', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
|
set_rule(world.get_entrance('TR Final Abyss NW', player), lambda state: state.has('Cane of Somaria', player) and state.has('Big Key (Turtle Rock)', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||||
set_rule(world.get_entrance('Turtle Rock (Trinexx)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 4) and state.has('Big Key (Turtle Rock)', player) and state.has('Cane of Somaria', player))
|
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player))
|
||||||
|
|
||||||
|
add_key_logic_rules(world, player) # todo - vanilla shuffle rules
|
||||||
|
# End of door rando rules.
|
||||||
|
|
||||||
|
add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
||||||
|
|
||||||
# these key rules are conservative, you might be able to get away with more lenient rules
|
# these key rules are conservative, you might be able to get away with more lenient rules
|
||||||
randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right']
|
randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right']
|
||||||
compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right']
|
compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right']
|
||||||
@@ -478,13 +486,9 @@ def global_rules(world, player):
|
|||||||
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
||||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
||||||
|
|
||||||
set_rule(world.get_entrance('Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic.
|
|
||||||
|
|
||||||
if world.swords == 'swordless':
|
if world.swords == 'swordless':
|
||||||
swordless_rules(world, player)
|
swordless_rules(world, player)
|
||||||
|
|
||||||
set_trock_key_rules(world, player)
|
|
||||||
|
|
||||||
set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player))
|
set_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player))
|
||||||
|
|
||||||
def inverted_rules(world, player):
|
def inverted_rules(world, player):
|
||||||
@@ -920,9 +924,8 @@ def no_glitches_rules(world, player):
|
|||||||
if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))):
|
if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))):
|
||||||
add_lamp_requirement(spot, player)
|
add_lamp_requirement(spot, player)
|
||||||
|
|
||||||
add_conditional_lamp('Turtle Rock (Dark Room) (North)', 'Turtle Rock (Entrance)', 'Entrance')
|
add_conditional_lamp('TR Dark Ride Up Stairs', 'TR Dark Ride', 'Entrance')
|
||||||
add_conditional_lamp('Turtle Rock (Dark Room) (South)', 'Turtle Rock (Entrance)', 'Entrance')
|
add_conditional_lamp('TR Dark Ride SW', 'TR Dark Ride', 'Entrance')
|
||||||
|
|
||||||
add_conditional_lamp('Mire Dark Shooters Up Stairs', 'Mire Dark Shooters', 'Entrance')
|
add_conditional_lamp('Mire Dark Shooters Up Stairs', 'Mire Dark Shooters', 'Entrance')
|
||||||
add_conditional_lamp('Mire Dark Shooters SW', 'Mire Dark Shooters', 'Entrance')
|
add_conditional_lamp('Mire Dark Shooters SW', 'Mire Dark Shooters', 'Entrance')
|
||||||
add_conditional_lamp('Mire Dark Shooters SE', 'Mire Dark Shooters', 'Entrance')
|
add_conditional_lamp('Mire Dark Shooters SE', 'Mire Dark Shooters', 'Entrance')
|
||||||
@@ -1561,12 +1564,13 @@ def set_bunny_rules(world, player):
|
|||||||
|
|
||||||
# regions for the exits of multi-entrace caves/drops that bunny cannot pass
|
# 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.
|
# 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', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)',
|
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave',
|
||||||
'Turtle Rock (Eye Bridge)', 'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)']
|
'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)']
|
||||||
# todo: bunny impassable caves
|
# todo: bunny impassable caves
|
||||||
# sewers drop may or may not be - maybe just new terminology
|
# sewers drop may or may not be - maybe just new terminology
|
||||||
# desert pots are impassible by bunny - need rules for those transitions
|
# desert pots are impassible by bunny - need rules for those transitions
|
||||||
# skull woods drops tend to soft lock bunny
|
# skull woods drops tend to soft lock bunny
|
||||||
|
# tr too - dark ride, chest gap, entrance gap, pots in lazy eyes, etc
|
||||||
|
|
||||||
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']
|
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']
|
||||||
|
|
||||||
@@ -1714,7 +1718,13 @@ def add_key_logic_rules(world, player):
|
|||||||
for location in d_logic.bk_restricted:
|
for location in d_logic.bk_restricted:
|
||||||
if location.name not in key_only_locations.keys():
|
if location.name not in key_only_locations.keys():
|
||||||
forbid_item(location, d_logic.bk_name, player)
|
forbid_item(location, d_logic.bk_name, player)
|
||||||
|
for location in d_logic.sm_restricted:
|
||||||
|
forbid_item(location, d_logic.small_key_name, player)
|
||||||
|
|
||||||
|
|
||||||
def create_key_rule(small_key_name, player, keys):
|
def create_key_rule(small_key_name, player, keys):
|
||||||
return lambda state: state.has_key(small_key_name, player, keys)
|
return lambda state: state.has_key(small_key_name, player, keys)
|
||||||
|
|
||||||
|
|
||||||
|
def create_forced_small_rule(small_key_name, player):
|
||||||
|
return lambda item: item.name == small_key_name and item.player == player
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ normal_offset_table = {
|
|||||||
|
|
||||||
|
|
||||||
spiral_offset_table = {
|
spiral_offset_table = {
|
||||||
0x01: 0x01, 0x02: 0x02, 0x05: 0x03, 0x07: 0x04, 0x09: 0x05, 0x0a: 0x07, 0x0c: 0x08, 0x0e: 0x0b,
|
0x01: 0x01, 0x02: 0x02, 0x04: 0x03, 0x07: 0x04, 0x09: 0x05, 0x0a: 0x07, 0x0c: 0x08, 0x0e: 0x0b,
|
||||||
0x11: 0x0c, 0x15: 0x0d, 0x16: 0x0e, 0x17: 0x0f, 0x1a: 0x11, 0x1c: 0x13, 0x1d: 0x14, 0x1e: 0x15,
|
0x11: 0x0c, 0x15: 0x0d, 0x16: 0x0e, 0x17: 0x0f, 0x1a: 0x11, 0x1c: 0x13, 0x1d: 0x14, 0x1e: 0x15,
|
||||||
0x26: 0x16, 0x27: 0x19, 0x28: 0x1b, 0x31: 0x1c, 0x34: 0x1f, 0x38: 0x20, 0x3a: 0x21, 0x3f: 0x22,
|
0x26: 0x16, 0x27: 0x19, 0x28: 0x1b, 0x31: 0x1c, 0x34: 0x1f, 0x38: 0x20, 0x3a: 0x21, 0x3f: 0x22,
|
||||||
0x40: 0x23, 0x41: 0x24, 0x42: 0x25, 0x45: 0x26, 0x4a: 0x27, 0x4c: 0x29, 0x4d: 0x2a, 0x4e: 0x2b,
|
0x40: 0x23, 0x41: 0x24, 0x42: 0x25, 0x45: 0x26, 0x4a: 0x27, 0x4c: 0x29, 0x4d: 0x2a, 0x4e: 0x2b,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ RecordStairType: {
|
|||||||
|
|
||||||
SpiralWarp: {
|
SpiralWarp: {
|
||||||
lda $040c : cmp.b #$ff : beq .abort ; abort if not in dungeon
|
lda $040c : cmp.b #$ff : beq .abort ; abort if not in dungeon
|
||||||
cmp #$18 : bcs .abort ; abort if not supported yet -- todo: this needs to be altered/removed as more dungeons are implemented
|
cmp #$1a : bcs .abort ; abort if not supported yet -- todo: this needs to be altered/removed as more dungeons are implemented
|
||||||
.check
|
.check
|
||||||
lda $045e : cmp #$5e : beq .gtg ; abort if not spiral - intended room is in A!
|
lda $045e : cmp #$5e : beq .gtg ; abort if not spiral - intended room is in A!
|
||||||
cmp #$5f : beq .gtg
|
cmp #$5f : beq .gtg
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user