Large refactor underway.
Wrote new main method Implemented trap door shuffle to some degree Still needs the other types
This commit is contained in:
585
DoorShuffle.py
585
DoorShuffle.py
@@ -6,13 +6,14 @@ from enum import unique, Flag
|
|||||||
from typing import DefaultDict, Dict, List
|
from typing import DefaultDict, Dict, List
|
||||||
|
|
||||||
from BaseClasses import RegionType, Region, Door, DoorType, Direction, Sector, CrystalBarrier, DungeonInfo, dungeon_keys
|
from BaseClasses import RegionType, Region, Door, DoorType, Direction, Sector, CrystalBarrier, DungeonInfo, dungeon_keys
|
||||||
from BaseClasses import PotFlags, LocationType
|
from BaseClasses import PotFlags, LocationType, Direction
|
||||||
from Doors import reset_portals
|
from Doors import reset_portals
|
||||||
from Dungeons import dungeon_regions, region_starts, standard_starts, split_region_starts
|
from Dungeons import dungeon_regions, region_starts, standard_starts, split_region_starts
|
||||||
from Dungeons import dungeon_bigs, dungeon_hints
|
from Dungeons import dungeon_bigs, dungeon_hints
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
from RoomData import DoorKind, PairedDoor, reset_rooms
|
from RoomData import DoorKind, PairedDoor, reset_rooms
|
||||||
from source.dungeon.DungeonStitcher import GenerationException, generate_dungeon
|
from source.dungeon.DungeonStitcher import GenerationException, generate_dungeon
|
||||||
|
from source.dungeon.DungeonStitcher import ExplorationState as ExplorationState2
|
||||||
# from DungeonGenerator import generate_dungeon
|
# from DungeonGenerator import generate_dungeon
|
||||||
from DungeonGenerator import ExplorationState, convert_regions, pre_validate, determine_required_paths, drop_entrances
|
from DungeonGenerator import ExplorationState, convert_regions, pre_validate, determine_required_paths, drop_entrances
|
||||||
from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances
|
from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances
|
||||||
@@ -759,6 +760,131 @@ def find_entrance_region(portal):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# each dungeon_pool members is a pair of lists: dungeon names and regions in those dungeons
|
||||||
|
def main_dungeon_pool(dungeon_pool, world, player):
|
||||||
|
add_inaccessible_doors(world, player)
|
||||||
|
entrances_map, potentials, connections = determine_entrance_list(world, player)
|
||||||
|
connections_tuple = (entrances_map, potentials, connections)
|
||||||
|
entrances, splits = create_dungeon_entrances(world, player)
|
||||||
|
|
||||||
|
dungeon_builders = {}
|
||||||
|
door_type_pools = []
|
||||||
|
for pool, region_list in dungeon_pool:
|
||||||
|
if len(pool) == 1:
|
||||||
|
dungeon_key = next(pool)
|
||||||
|
sector_pool = convert_to_sectors(region_list, world, player)
|
||||||
|
merge_sectors(sector_pool, world, player)
|
||||||
|
dungeon_builders[dungeon_key] = simple_dungeon_builder(dungeon_key, sector_pool)
|
||||||
|
dungeon_builders[dungeon_key].entrance_list = list(entrances_map[dungeon_key])
|
||||||
|
else:
|
||||||
|
if 'Hyrule Castle' in pool:
|
||||||
|
hc = world.get_dungeon('Hyrule Castle', player)
|
||||||
|
hc.dungeon_items.append(ItemFactory('Compass (Escape)', player))
|
||||||
|
if 'Agahnims Tower' in pool:
|
||||||
|
at = world.get_dungeon('Agahnims Tower', player)
|
||||||
|
at.dungeon_items.append(ItemFactory('Compass (Agahnims Tower)', player))
|
||||||
|
at.dungeon_items.append(ItemFactory('Map (Agahnims Tower)', player))
|
||||||
|
sector_pool = convert_to_sectors(region_list, world, player)
|
||||||
|
merge_sectors(sector_pool, world, player)
|
||||||
|
# todo: which dungeon to create
|
||||||
|
dungeon_builders.update(create_dungeon_builders(sector_pool, connections_tuple,
|
||||||
|
world, player, pool, entrances, splits))
|
||||||
|
door_type_pools.append((pool, DoorTypePool(sector_pool, world, player)))
|
||||||
|
|
||||||
|
update_forced_keys(dungeon_builders, entrances_map, world, player)
|
||||||
|
recombinant_builders = {}
|
||||||
|
builder_info = entrances, splits, connections_tuple, world, player
|
||||||
|
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info)
|
||||||
|
|
||||||
|
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
||||||
|
|
||||||
|
setup_custom_door_types(world, player)
|
||||||
|
paths = determine_required_paths(world, player)
|
||||||
|
shuffle_door_types(door_type_pools, paths, world, player)
|
||||||
|
|
||||||
|
check_required_paths(paths, world, player)
|
||||||
|
|
||||||
|
all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items))
|
||||||
|
target_items = 34
|
||||||
|
if world.retro[player]:
|
||||||
|
target_items += 1 if world.dropshuffle[player] else 0 # the hc big key
|
||||||
|
else:
|
||||||
|
target_items += 29 # small keys in chests
|
||||||
|
if world.dropshuffle[player]:
|
||||||
|
target_items += 14 # 13 dropped smalls + 1 big
|
||||||
|
if world.pottery[player] not in ['none', 'cave']:
|
||||||
|
target_items += 19 # 19 pot keys
|
||||||
|
d_items = target_items - all_dungeon_items_cnt
|
||||||
|
world.pool_adjustment[player] = d_items
|
||||||
|
if not world.decoupledoors[player]:
|
||||||
|
smooth_door_pairs(world, player)
|
||||||
|
|
||||||
|
|
||||||
|
def update_forced_keys(dungeon_builders, entrances_map, world, player):
|
||||||
|
for builder in dungeon_builders.values():
|
||||||
|
builder.entrance_list = list(entrances_map[builder.name])
|
||||||
|
dungeon_obj = world.get_dungeon(builder.name, player)
|
||||||
|
for sector in builder.sectors:
|
||||||
|
for region in sector.regions:
|
||||||
|
region.dungeon = dungeon_obj
|
||||||
|
for loc in region.locations:
|
||||||
|
if loc.forced_item:
|
||||||
|
key_name = (dungeon_keys[builder.name] if loc.name != 'Hyrule Castle - Big Key Drop'
|
||||||
|
else dungeon_bigs[builder.name])
|
||||||
|
loc.forced_item = loc.item = ItemFactory(key_name, player)
|
||||||
|
|
||||||
|
|
||||||
|
def finish_up_work(world, player):
|
||||||
|
dungeon_builders = world.dungeon_layouts[player]
|
||||||
|
# Re-assign dungeon bosses
|
||||||
|
gt = world.get_dungeon('Ganons Tower', player)
|
||||||
|
for name, builder in dungeon_builders.items():
|
||||||
|
reassign_boss('GT Ice Armos', 'bottom', builder, gt, world, player)
|
||||||
|
reassign_boss('GT Lanmolas 2', 'middle', builder, gt, world, player)
|
||||||
|
reassign_boss('GT Moldorm', 'top', builder, gt, world, player)
|
||||||
|
|
||||||
|
sanctuary = world.get_region('Sanctuary', player)
|
||||||
|
d_name = sanctuary.dungeon.name
|
||||||
|
if d_name != 'Hyrule Castle':
|
||||||
|
possible_portals = []
|
||||||
|
for portal_name in dungeon_portals[d_name]:
|
||||||
|
portal = world.get_portal(portal_name, player)
|
||||||
|
if portal.door.name == 'Sanctuary S':
|
||||||
|
possible_portals.clear()
|
||||||
|
possible_portals.append(portal)
|
||||||
|
break
|
||||||
|
if not portal.destination and not portal.deadEnd:
|
||||||
|
possible_portals.append(portal)
|
||||||
|
if len(possible_portals) == 1:
|
||||||
|
world.sanc_portal[player] = possible_portals[0]
|
||||||
|
else:
|
||||||
|
reachable_portals = []
|
||||||
|
for portal in possible_portals:
|
||||||
|
start_area = portal.door.entrance.parent_region
|
||||||
|
state = ExplorationState(dungeon=d_name)
|
||||||
|
state.visit_region(start_area)
|
||||||
|
state.add_all_doors_check_unattached(start_area, world, player)
|
||||||
|
explore_state(state, world, player)
|
||||||
|
if state.visited_at_all(sanctuary):
|
||||||
|
reachable_portals.append(portal)
|
||||||
|
world.sanc_portal[player] = random.choice(reachable_portals)
|
||||||
|
if world.intensity[player] >= 3:
|
||||||
|
if player in world.sanc_portal:
|
||||||
|
portal = world.sanc_portal[player]
|
||||||
|
else:
|
||||||
|
portal = world.get_portal('Sanctuary', player)
|
||||||
|
target = portal.door.entrance.parent_region
|
||||||
|
connect_simple_door(world, 'Sanctuary Mirror Route', target, player)
|
||||||
|
|
||||||
|
check_entrance_fixes(world, player)
|
||||||
|
|
||||||
|
if world.standardize_palettes[player] == 'standardize' and world.doorShuffle[player] not in ['basic']:
|
||||||
|
palette_assignment(world, player)
|
||||||
|
|
||||||
|
refine_hints(dungeon_builders)
|
||||||
|
refine_boss_exits(world, player)
|
||||||
|
|
||||||
|
|
||||||
# def unpair_all_doors(world, player):
|
# def unpair_all_doors(world, player):
|
||||||
# for paired_door in world.paired_doors[player]:
|
# for paired_door in world.paired_doors[player]:
|
||||||
# paired_door.pair = False
|
# paired_door.pair = False
|
||||||
@@ -1522,7 +1648,9 @@ def setup_custom_door_types(world, player):
|
|||||||
custom_doors = custom_doors[player]
|
custom_doors = custom_doors[player]
|
||||||
if 'doors' not in custom_doors:
|
if 'doors' not in custom_doors:
|
||||||
return
|
return
|
||||||
world.custom_door_types[player] = type_map = {'Key Door': defaultdict(list), 'Dash Door': [], 'Bomb Door': []}
|
# todo: dash/bomb door pool specific
|
||||||
|
customizeable_types = ['Key Door', 'Dash Door', 'Bomb Door', 'Trap Door', 'Big Key Door']
|
||||||
|
world.custom_door_types[player] = type_map = {x: defaultdict(list) for x in customizeable_types}
|
||||||
for door, dest in custom_doors['doors'].items():
|
for door, dest in custom_doors['doors'].items():
|
||||||
if isinstance(dest, dict):
|
if isinstance(dest, dict):
|
||||||
if 'type' in dest:
|
if 'type' in dest:
|
||||||
@@ -1531,17 +1659,206 @@ def setup_custom_door_types(world, player):
|
|||||||
dungeon = d.entrance.parent_region.dungeon
|
dungeon = d.entrance.parent_region.dungeon
|
||||||
if d.type == DoorType.SpiralStairs:
|
if d.type == DoorType.SpiralStairs:
|
||||||
type_map[door_kind][dungeon.name].append(d)
|
type_map[door_kind][dungeon.name].append(d)
|
||||||
elif door_kind == 'Key Door':
|
else:
|
||||||
# check if the
|
# check if the
|
||||||
if d.dest.type in [DoorType.Interior, DoorType.Normal]:
|
if d.dest.type in [DoorType.Interior, DoorType.Normal]:
|
||||||
type_map[door_kind][dungeon.name].append((d, d.dest))
|
type_map[door_kind][dungeon.name].append((d, d.dest))
|
||||||
else:
|
else:
|
||||||
type_map[door_kind][dungeon.name].append(d)
|
type_map[door_kind][dungeon.name].append(d)
|
||||||
else:
|
|
||||||
if d.dest.type in [DoorType.Interior, DoorType.Normal]:
|
|
||||||
type_map[door_kind].append((d, d.dest))
|
class DoorTypePool:
|
||||||
else:
|
def __init__(self, sectors, world, player):
|
||||||
type_map[door_kind].append(d)
|
self.smalls = 0
|
||||||
|
self.bombable = 0
|
||||||
|
self.dashable = 0
|
||||||
|
self.bigs = 0
|
||||||
|
self.traps = 0
|
||||||
|
# self.tricky = 0
|
||||||
|
# self.hidden = 0
|
||||||
|
# todo: custom pools?
|
||||||
|
self.count_via_sectors(sectors, world, player)
|
||||||
|
|
||||||
|
def count_via_sectors(self, sectors, world, player):
|
||||||
|
skips = set()
|
||||||
|
for sector in sectors:
|
||||||
|
for region in sector.regions:
|
||||||
|
for ext in region.exits:
|
||||||
|
if ext.door:
|
||||||
|
d = ext.door
|
||||||
|
if d.name not in skips and d.type in [DoorType.Normal, DoorType.Interior]:
|
||||||
|
if d.smallKey:
|
||||||
|
self.smalls += 1
|
||||||
|
elif d.bigKey:
|
||||||
|
self.bigs += 1
|
||||||
|
elif d.blocked and d.trapFlag and 'Boss' not in d.name and 'Agahnim' not in d.name:
|
||||||
|
self.traps += 1
|
||||||
|
elif d.name == 'TR Compass Room NW':
|
||||||
|
self.tricky += 1
|
||||||
|
elif d.name in ['Skull Vines NW', 'Tower Altar NW']:
|
||||||
|
self.hidden += 1
|
||||||
|
else:
|
||||||
|
kind = world.get_room(d.roomIndex, player).kind(d)
|
||||||
|
if kind == DoorKind.Bombable:
|
||||||
|
self.bombable += 1
|
||||||
|
elif kind == DoorKind.Dashable:
|
||||||
|
self.dashable += 1
|
||||||
|
if d.type == DoorType.Interior:
|
||||||
|
skips.add(d.dest.name) ## lookup a different way for interior door shuffle
|
||||||
|
elif d.type == DoorType.Normal:
|
||||||
|
for dp in world.paired_doors[player]:
|
||||||
|
if d.name == dp.door_a or d.name == dp.door_b:
|
||||||
|
skips.add(dp.door_b if d.name == dp.door_a else dp.door_a)
|
||||||
|
break
|
||||||
|
|
||||||
|
def chaos_shuffle(self):
|
||||||
|
weights = [1, 2, 4, 3, 2, 1]
|
||||||
|
self.smalls = random.choices(self.get_choices(self.smalls), weights=weights)
|
||||||
|
self.bombable = random.choices(self.get_choices(self.bombable), weights=weights)
|
||||||
|
self.dashable = random.choices(self.get_choices(self.dashable), weights=weights)
|
||||||
|
self.bigs = random.choices(self.get_choices(self.bigs), weights=weights)
|
||||||
|
self.traps = random.choices(self.get_choices(self.traps), weights=weights)
|
||||||
|
# self.tricky = random.choices(self.get_choices(self.tricky), weights=weights)
|
||||||
|
# self.hidden = random.choices(self.get_choices(self.hidden), weights=weights)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_choices(number):
|
||||||
|
return [max(number-i, 0) for i in range(-1, 5)]
|
||||||
|
|
||||||
|
|
||||||
|
class BuilderDoorCandidates:
|
||||||
|
def __init__(self):
|
||||||
|
self.small = set()
|
||||||
|
self.big = set()
|
||||||
|
self.trap = set()
|
||||||
|
self.bombable = set()
|
||||||
|
self.dashable = set()
|
||||||
|
|
||||||
|
self.checked = set()
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle_door_types(door_type_pools, paths, world, player):
|
||||||
|
start_regions_map = {}
|
||||||
|
for name, builder in world.dungeon_layouts[player]:
|
||||||
|
start_regions = convert_regions(builder.path_entrances, world, player)
|
||||||
|
start_regions_map[name] = start_regions
|
||||||
|
used_doors = shuffle_door_traps(door_type_pools, paths, start_regions_map, world, player)
|
||||||
|
# big keys
|
||||||
|
# small keys
|
||||||
|
|
||||||
|
# bombable / dashable
|
||||||
|
|
||||||
|
# tricky / hidden
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle_door_traps(door_type_pools, paths, start_regions_map, world, player):
|
||||||
|
used_doors = set()
|
||||||
|
for pool, door_type_pool in door_type_pools:
|
||||||
|
ttl = 0
|
||||||
|
suggestion_map, trap_map, flex_map = {}, {}, {}
|
||||||
|
remaining = door_type_pool.traps
|
||||||
|
if player in world.custom_door_types:
|
||||||
|
custom_trap_doors = world.custom_door_types[player]['Trap Door']
|
||||||
|
else:
|
||||||
|
custom_trap_doors = defaultdict(list)
|
||||||
|
|
||||||
|
for dungeon in pool:
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
find_trappable_candidates(builder, world, player)
|
||||||
|
if custom_trap_doors[dungeon]:
|
||||||
|
builder.candidates.trap = filter_key_door_pool(builder.candidates.trap, custom_trap_doors[dungeon])
|
||||||
|
remaining -= len(custom_trap_doors[dungeon])
|
||||||
|
ttl += len(builder.candidates.trap)
|
||||||
|
for dungeon in pool:
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
proportion = len(builder.candidates.trap)
|
||||||
|
calc = int(round(proportion * door_type_pool.traps/ttl))
|
||||||
|
suggested = min(proportion, calc)
|
||||||
|
remaining -= suggested
|
||||||
|
suggestion_map[dungeon] = suggested
|
||||||
|
flex_map[dungeon] = (proportion - suggested) if suggested < proportion else 0
|
||||||
|
for dungeon in pool:
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
valid_traps, trap_number = find_valid_trap_combination(builder, suggestion_map[dungeon],
|
||||||
|
start_regions_map[dungeon], paths, world, player)
|
||||||
|
trap_map[dungeon] = valid_traps
|
||||||
|
if trap_number < suggestion_map[dungeon]:
|
||||||
|
flex_map[dungeon] = 0
|
||||||
|
remaining += suggestion_map[dungeon] - trap_number
|
||||||
|
suggestion_map[dungeon] = trap_number
|
||||||
|
builder_order = [x for x in pool if flex_map[x] > 0]
|
||||||
|
queue = deque(builder_order)
|
||||||
|
while len(queue) > 0 and remaining > 0:
|
||||||
|
dungeon = queue.popleft()
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
increased = suggestion_map[dungeon] + 1
|
||||||
|
valid_traps, trap_number = find_valid_trap_combination(builder, increased, start_regions_map[dungeon],
|
||||||
|
paths, world, player)
|
||||||
|
if valid_traps:
|
||||||
|
trap_map[dungeon] = valid_traps
|
||||||
|
remaining -= 1
|
||||||
|
suggestion_map[dungeon] = increased
|
||||||
|
flex_map[dungeon] -= 1
|
||||||
|
if flex_map[dungeon] > 0:
|
||||||
|
queue.append(dungeon)
|
||||||
|
# time to re-assign
|
||||||
|
reassign_trap_doors(trap_map, world, player)
|
||||||
|
for name, traps in trap_map.items():
|
||||||
|
used_doors.update(traps)
|
||||||
|
return used_doors
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, world, player):
|
||||||
|
for pool, door_type_pool in door_type_pools:
|
||||||
|
ttl = 0
|
||||||
|
suggestion_map, bk_map, flex_map = {}, {}, {}
|
||||||
|
remaining = door_type_pool.bigs
|
||||||
|
if player in world.custom_door_types:
|
||||||
|
custom_bk_doors = world.custom_door_types[player]['Big Key Door']
|
||||||
|
else:
|
||||||
|
custom_bk_doors = defaultdict(list)
|
||||||
|
|
||||||
|
for dungeon in pool:
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
find_big_key_candidates(builder, used_doors, world, player)
|
||||||
|
if custom_bk_doors[dungeon]:
|
||||||
|
builder.candidates.trap = filter_key_door_pool(builder.candidates.big, custom_bk_doors[dungeon])
|
||||||
|
remaining -= len(custom_bk_doors[dungeon])
|
||||||
|
ttl += len(builder.candidates.big)
|
||||||
|
for dungeon in pool:
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
proportion = len(builder.candidates.big)
|
||||||
|
calc = int(round(proportion * door_type_pool.big/ttl))
|
||||||
|
suggested = min(proportion, calc)
|
||||||
|
remaining -= suggested
|
||||||
|
suggestion_map[dungeon] = suggested
|
||||||
|
flex_map[dungeon] = (proportion - suggested) if suggested < proportion else 0
|
||||||
|
for dungeon in pool:
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
valid_doors, bk_number = find_valid_bk_combination(builder, suggestion_map[dungeon],
|
||||||
|
start_regions_map[dungeon], world, player)
|
||||||
|
bk_map[dungeon] = valid_doors
|
||||||
|
if bk_number < suggestion_map[dungeon]:
|
||||||
|
flex_map[dungeon] = 0
|
||||||
|
remaining += suggestion_map[dungeon] - bk_number
|
||||||
|
suggestion_map[dungeon] = bk_number
|
||||||
|
builder_order = [x for x in pool if flex_map[x] > 0]
|
||||||
|
queue = deque(builder_order)
|
||||||
|
while len(queue) > 0 and remaining > 0:
|
||||||
|
dungeon = queue.popleft()
|
||||||
|
builder = world.dungeon_layouts[player][dungeon]
|
||||||
|
increased = suggestion_map[dungeon] + 1
|
||||||
|
valid_doors, bk_number = find_valid_bk_combination(builder, increased, start_regions_map[dungeon],
|
||||||
|
paths, world, player)
|
||||||
|
if valid_doors:
|
||||||
|
bk_map[dungeon] = valid_doors
|
||||||
|
remaining -= 1
|
||||||
|
suggestion_map[dungeon] = increased
|
||||||
|
flex_map[dungeon] -= 1
|
||||||
|
if flex_map[dungeon] > 0:
|
||||||
|
queue.append(dungeon)
|
||||||
|
# time to re-assign
|
||||||
|
reassign_big_key_doors(bk_map, world, player)
|
||||||
|
|
||||||
|
|
||||||
def shuffle_key_doors(builder, world, player):
|
def shuffle_key_doors(builder, world, player):
|
||||||
@@ -1582,6 +1899,223 @@ def find_current_key_doors(builder):
|
|||||||
return current_doors
|
return current_doors
|
||||||
|
|
||||||
|
|
||||||
|
def find_trappable_candidates(builder, world, player):
|
||||||
|
if world.door_type_mode[player] != 'original': # all, chaos
|
||||||
|
r_set = builder.master_sector.region_set()
|
||||||
|
filtered_doors = [ext.door for r in r_set for ext in r.exits
|
||||||
|
if ext.door and ext.door.type in [DoorType.Interior, DoorType.Normal]]
|
||||||
|
for d in filtered_doors:
|
||||||
|
# I only support the first 3 due to the trapFlag right now
|
||||||
|
if 0 <= d.doorListPos < 3 and not d.entranceFlag:
|
||||||
|
room = world.get_room(d.roomIndex, player)
|
||||||
|
kind = room.kind(d)
|
||||||
|
if d.type == DoorType.Interior:
|
||||||
|
if (kind in [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind.Dashable,
|
||||||
|
DoorKind.BigKey]
|
||||||
|
or (d.blocked and d.trapFlag != 0 and 'Boss' not in d.name and 'Agahnim' not in d.name)
|
||||||
|
or (kind == DoorKind.TrapTriggerable and d.direction in [Direction.North, Direction.East])
|
||||||
|
or (kind == DoorKind.Trap2 and d.direction in [Direction.South, Direction.West])):
|
||||||
|
builder.candidates.trap.add(d)
|
||||||
|
elif d.type == DoorType.Normal:
|
||||||
|
if (kind in [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind.Dashable,
|
||||||
|
DoorKind.BigKey]
|
||||||
|
or (d.blocked and d.trapFlag != 0 and 'Boss' not in d.name and 'Agahnim' not in d.name)):
|
||||||
|
builder.candidates.trap.add(d)
|
||||||
|
else:
|
||||||
|
r_set = builder.master_sector.region_set()
|
||||||
|
for r in r_set:
|
||||||
|
for ext in r.exits:
|
||||||
|
if ext.door:
|
||||||
|
d = ext.door
|
||||||
|
if d.blocked and d.trapFlag != 0 and 'Boss' not in d.name and 'Agahnim' not in d.name:
|
||||||
|
builder.candidates.trap.add(d)
|
||||||
|
|
||||||
|
|
||||||
|
def find_valid_trap_combination(builder, suggested, start_regions, paths, world, player, drop=True):
|
||||||
|
trap_door_pool = builder.candidates.trap
|
||||||
|
trap_doors_needed = suggested
|
||||||
|
if player in world.custom_door_types:
|
||||||
|
custom_trap_doors = world.custom_door_types[player]['Trap Door'][builder.name]
|
||||||
|
else:
|
||||||
|
custom_trap_doors = []
|
||||||
|
if custom_trap_doors:
|
||||||
|
trap_door_pool = filter_key_door_pool(trap_door_pool, custom_trap_doors)
|
||||||
|
trap_doors_needed -= len(custom_trap_doors)
|
||||||
|
if len(trap_door_pool) < trap_doors_needed:
|
||||||
|
if not drop:
|
||||||
|
return None, 0
|
||||||
|
trap_doors_needed = len(trap_door_pool)
|
||||||
|
combinations = ncr(len(trap_door_pool), trap_doors_needed)
|
||||||
|
itr = 0
|
||||||
|
sample_list = build_sample_list(combinations, 1000)
|
||||||
|
proposal = kth_combination(sample_list[itr], trap_door_pool, trap_doors_needed)
|
||||||
|
proposal.extend(custom_trap_doors)
|
||||||
|
|
||||||
|
start_regions = filter_start_regions(builder, start_regions, world, player)
|
||||||
|
while not validate_trap_layout(proposal, builder, start_regions, paths, world, player):
|
||||||
|
itr += 1
|
||||||
|
if itr >= len(sample_list):
|
||||||
|
if not drop:
|
||||||
|
return None, 0
|
||||||
|
trap_doors_needed -= 1
|
||||||
|
if trap_doors_needed < 0:
|
||||||
|
raise Exception(f'Bad dungeon {builder.name} - maybe custom trap doors are bad')
|
||||||
|
combinations = ncr(len(trap_door_pool), trap_doors_needed)
|
||||||
|
sample_list = build_sample_list(combinations, 1000)
|
||||||
|
itr = 0
|
||||||
|
proposal = kth_combination(sample_list[itr], trap_door_pool, trap_doors_needed)
|
||||||
|
proposal.extend(custom_trap_doors)
|
||||||
|
return proposal, trap_doors_needed
|
||||||
|
|
||||||
|
|
||||||
|
# eliminate start region if portal marked as destination
|
||||||
|
def filter_start_regions(builder, start_regions, world, player):
|
||||||
|
std_flag = world.mode[player] == 'standard' and builder.name == 'Hyrule Castle'
|
||||||
|
excluded = {}
|
||||||
|
for region in start_regions:
|
||||||
|
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
||||||
|
if portal and portal.destination:
|
||||||
|
excluded[region] = None
|
||||||
|
if std_flag and (not portal or portal.find_portal_entrance().parent_region.name != 'Hyrule Castle Courtyard'):
|
||||||
|
excluded[region] = None
|
||||||
|
return [x for x in start_regions if x not in excluded.keys()]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_trap_layout(proposal, builder, start_regions, paths, world, player):
|
||||||
|
return check_required_paths_with_traps(paths, proposal, builder.name, start_regions, world, player)
|
||||||
|
|
||||||
|
|
||||||
|
def check_required_paths_with_traps(paths, proposal, dungeon_name, start_regions, world, player):
|
||||||
|
if len(paths[dungeon_name]) > 0:
|
||||||
|
states_to_explore = {}
|
||||||
|
common_starts = tuple(start_regions)
|
||||||
|
for path in paths[dungeon_name]:
|
||||||
|
if type(path) is tuple:
|
||||||
|
states_to_explore[tuple([path[0]])] = (path[1], 'any')
|
||||||
|
else:
|
||||||
|
if common_starts not in states_to_explore:
|
||||||
|
states_to_explore[common_starts] = ([], 'all')
|
||||||
|
states_to_explore[common_starts][0].append(path)
|
||||||
|
cached_initial_state = None
|
||||||
|
for start_regs, info in states_to_explore.items():
|
||||||
|
dest_regs, path_type = info
|
||||||
|
if type(dest_regs) is not list:
|
||||||
|
dest_regs = [dest_regs]
|
||||||
|
check_paths = convert_regions(dest_regs, world, player)
|
||||||
|
start_regions = convert_regions(start_regs, world, player)
|
||||||
|
initial = start_regs == common_starts
|
||||||
|
if not initial or cached_initial_state is None:
|
||||||
|
init = determine_init_crystal(initial, cached_initial_state, start_regions)
|
||||||
|
state = ExplorationState2(init, dungeon_name)
|
||||||
|
for region in start_regions:
|
||||||
|
state.visit_region(region)
|
||||||
|
state.add_all_doors_check_proposed_traps(region, proposal, world, player)
|
||||||
|
explore_state_proposed_traps(state, world, player)
|
||||||
|
if initial and cached_initial_state is None:
|
||||||
|
cached_initial_state = state
|
||||||
|
else:
|
||||||
|
state = cached_initial_state
|
||||||
|
if path_type == 'any':
|
||||||
|
valid, bad_region = check_if_any_regions_visited(state, check_paths)
|
||||||
|
else:
|
||||||
|
valid, bad_region = check_if_all_regions_visited(state, check_paths)
|
||||||
|
if not valid:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def reassign_trap_doors(trap_map, world, player):
|
||||||
|
logger = logging.getLogger('')
|
||||||
|
for name, traps in trap_map.items():
|
||||||
|
builder = world.dungeon_layouts[player][name]
|
||||||
|
queue = deque(find_current_trap_doors(builder))
|
||||||
|
while len(queue) > 0:
|
||||||
|
d = queue.pop()
|
||||||
|
if d.type is DoorType.Interior and d not in traps:
|
||||||
|
room = world.get_room(d.roomIndex, player)
|
||||||
|
kind = room.kind(d)
|
||||||
|
if kind == DoorKind.Trap:
|
||||||
|
new_type = (DoorKind.TrapTriggerable if d.direction in [Direction.South, Direction.East] else
|
||||||
|
DoorKind.Trap2)
|
||||||
|
room.change(d.doorListPos, new_type)
|
||||||
|
elif kind in [DoorKind.Trap2, DoorKind.TrapTriggerable]:
|
||||||
|
room.change(d.doorListPos, DoorKind.Normal)
|
||||||
|
d.blocked = False
|
||||||
|
elif d.type is DoorType.Normal and d not in traps:
|
||||||
|
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
|
||||||
|
d.blocked = False
|
||||||
|
for d in traps:
|
||||||
|
change_door_to_trap(d, world, player)
|
||||||
|
world.spoiler.set_door_type(d.name, 'Trap Door', player)
|
||||||
|
logger.debug('Key Door: %s', d.name)
|
||||||
|
|
||||||
|
|
||||||
|
def find_current_trap_doors(builder):
|
||||||
|
current_doors = []
|
||||||
|
for region in builder.master_sector.regions:
|
||||||
|
for ext in region.exits:
|
||||||
|
d = ext.door
|
||||||
|
if d and d.blocked and d.trapFlag != 0:
|
||||||
|
current_doors.append(d)
|
||||||
|
return current_doors
|
||||||
|
|
||||||
|
|
||||||
|
def change_door_to_trap(d, world, player):
|
||||||
|
room = world.get_room(d.roomIndex, player)
|
||||||
|
if d.type is DoorType.Interior:
|
||||||
|
kind = room.kind(d)
|
||||||
|
new_kind = None
|
||||||
|
if kind == DoorKind.TrapTriggerable and d.direction in [Direction.North, Direction.West]:
|
||||||
|
new_kind = DoorKind.Trap
|
||||||
|
elif kind == DoorKind.Trap2 and d.direction in [Direction.South, Direction.East]:
|
||||||
|
new_kind = DoorKind.Trap
|
||||||
|
elif d.direction in [Direction.North, Direction.West]:
|
||||||
|
new_kind = DoorKind.Trap2
|
||||||
|
elif d.direction in [Direction.South, Direction.East]:
|
||||||
|
new_kind = DoorKind.TrapTriggerable
|
||||||
|
if new_kind:
|
||||||
|
d.blocked = True
|
||||||
|
verify_door_list_pos(d, room, world, player, pos=3)
|
||||||
|
d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1}[d.doorListPos]
|
||||||
|
room.change(d.doorListPos, new_kind)
|
||||||
|
elif d.type is DoorType.Normal:
|
||||||
|
d.blocked = True
|
||||||
|
verify_door_list_pos(d, room, world, player, pos=3)
|
||||||
|
d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1}[d.doorListPos]
|
||||||
|
room.change(d.doorListPos, DoorKind.Trap)
|
||||||
|
|
||||||
|
|
||||||
|
def find_big_key_candidates(builder, used, world, player):
|
||||||
|
if world.door_type_mode[player] != 'original': # all, chaos
|
||||||
|
r_set = builder.master_sector.region_set()
|
||||||
|
filtered_doors = [ext.door for r in r_set for ext in r.exits
|
||||||
|
if ext.door and ext.door.type in [DoorType.Interior, DoorType.Normal]]
|
||||||
|
for d in filtered_doors:
|
||||||
|
if 0 <= d.doorListPos < 4 and not d.entranceFlag:
|
||||||
|
room = world.get_room(d.roomIndex, player)
|
||||||
|
kind = room.kind(d)
|
||||||
|
if d.type in [DoorType.Interior, DoorType.Normal]:
|
||||||
|
if (kind in [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind.Dashable,
|
||||||
|
DoorKind.BigKey] and d not in used)
|
||||||
|
or (d.blocked and d.trapFlag != 0 and 'Boss' not in d.name and 'Agahnim' not in d.name)
|
||||||
|
or (kind == DoorKind.TrapTriggerable and d.direction in [Direction.North, Direction.East])
|
||||||
|
or (kind == DoorKind.Trap2 and d.direction in [Direction.South, Direction.West])):
|
||||||
|
builder.candidates.big.add(d)
|
||||||
|
elif d.type == DoorType.Normal:
|
||||||
|
if (kind in [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind.Dashable,
|
||||||
|
DoorKind.BigKey]
|
||||||
|
or (d.blocked and d.trapFlag != 0 and 'Boss' not in d.name and 'Agahnim' not in d.name)):
|
||||||
|
builder.candidates.trap.add(d)
|
||||||
|
else:
|
||||||
|
r_set = builder.master_sector.region_set()
|
||||||
|
for r in r_set:
|
||||||
|
for ext in r.exits:
|
||||||
|
if ext.door:
|
||||||
|
d = ext.door
|
||||||
|
if d.bigKey and d.type in [DoorType.Normal, DoorType.Interior]:
|
||||||
|
builder.candidates.big.add(d)
|
||||||
|
|
||||||
|
|
||||||
def find_small_key_door_candidates(builder, start_regions, world, player):
|
def find_small_key_door_candidates(builder, start_regions, world, player):
|
||||||
# traverse dungeon and find candidates
|
# traverse dungeon and find candidates
|
||||||
candidates = []
|
candidates = []
|
||||||
@@ -1638,16 +2172,7 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True
|
|||||||
sample_list = build_sample_list(combinations)
|
sample_list = build_sample_list(combinations)
|
||||||
proposal = kth_combination(sample_list[itr], key_door_pool, key_doors_needed)
|
proposal = kth_combination(sample_list[itr], key_door_pool, key_doors_needed)
|
||||||
proposal.extend(custom_key_doors)
|
proposal.extend(custom_key_doors)
|
||||||
# eliminate start region if portal marked as destination
|
start_regions = filter_start_regions(builder, start_regions, world, player)
|
||||||
std_flag = world.mode[player] == 'standard' and builder.name == 'Hyrule Castle'
|
|
||||||
excluded = {}
|
|
||||||
for region in start_regions:
|
|
||||||
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
|
||||||
if portal and portal.destination:
|
|
||||||
excluded[region] = None
|
|
||||||
if std_flag and (not portal or portal.find_portal_entrance().parent_region.name != 'Hyrule Castle Courtyard'):
|
|
||||||
excluded[region] = None
|
|
||||||
start_regions = [x for x in start_regions if x not in excluded.keys()]
|
|
||||||
|
|
||||||
key_layout = build_key_layout(builder, start_regions, proposal, world, player)
|
key_layout = build_key_layout(builder, start_regions, proposal, world, player)
|
||||||
determine_prize_lock(key_layout, world, player)
|
determine_prize_lock(key_layout, world, player)
|
||||||
@@ -1682,13 +2207,12 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def build_sample_list(combinations):
|
def build_sample_list(combinations, max_combinations=1000000):
|
||||||
if combinations <= 1000000:
|
if combinations <= max_combinations:
|
||||||
sample_list = list(range(0, int(combinations)))
|
sample_list = list(range(0, int(combinations)))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
num_set = set()
|
num_set = set()
|
||||||
while len(num_set) < 1000000:
|
while len(num_set) < max_combinations:
|
||||||
num_set.add(random.randint(0, combinations))
|
num_set.add(random.randint(0, combinations))
|
||||||
sample_list = list(num_set)
|
sample_list = list(num_set)
|
||||||
sample_list.sort()
|
sample_list.sort()
|
||||||
@@ -1885,9 +2409,9 @@ def change_door_to_small_key(d, world, player):
|
|||||||
room.change(d.doorListPos, DoorKind.SmallKey)
|
room.change(d.doorListPos, DoorKind.SmallKey)
|
||||||
|
|
||||||
|
|
||||||
def verify_door_list_pos(d, room, world, player):
|
def verify_door_list_pos(d, room, world, player, pos=4):
|
||||||
if d.doorListPos >= 4:
|
if d.doorListPos >= pos:
|
||||||
new_index = room.next_free()
|
new_index = room.next_free(pos)
|
||||||
if new_index is not None:
|
if new_index is not None:
|
||||||
room.swap(new_index, d.doorListPos)
|
room.swap(new_index, d.doorListPos)
|
||||||
other = next(x for x in world.doors if x.player == player and x.roomIndex == d.roomIndex
|
other = next(x for x in world.doors if x.player == player and x.roomIndex == d.roomIndex
|
||||||
@@ -1895,7 +2419,7 @@ def verify_door_list_pos(d, room, world, player):
|
|||||||
other.doorListPos = d.doorListPos
|
other.doorListPos = d.doorListPos
|
||||||
d.doorListPos = new_index
|
d.doorListPos = new_index
|
||||||
else:
|
else:
|
||||||
raise Exception(f'Invalid stateful door: {d.name}. Only 4 stateful doors per supertile')
|
raise Exception(f'Invalid stateful door: {d.name}. Only {pos} stateful doors per supertile')
|
||||||
|
|
||||||
|
|
||||||
def smooth_door_pairs(world, player):
|
def smooth_door_pairs(world, player):
|
||||||
@@ -2261,6 +2785,15 @@ def explore_state(state, world, player):
|
|||||||
state.add_all_doors_check_unattached(connect_region, world, player)
|
state.add_all_doors_check_unattached(connect_region, world, player)
|
||||||
|
|
||||||
|
|
||||||
|
def explore_state_proposed_traps(state, proposed_traps, world, player):
|
||||||
|
while len(state.avail_doors) > 0:
|
||||||
|
door = state.next_avail_door().door
|
||||||
|
connect_region = world.get_entrance(door.name, player).connected_region
|
||||||
|
if not state.visited(connect_region) and valid_region_to_explore(connect_region, world, player):
|
||||||
|
state.visit_region(connect_region)
|
||||||
|
state.add_all_doors_check_proposed_traps(connect_region, proposed_traps, world, player)
|
||||||
|
|
||||||
|
|
||||||
def explore_state_not_inaccessible(state, world, player):
|
def explore_state_not_inaccessible(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
|
||||||
|
|||||||
26
Doors.py
26
Doors.py
@@ -348,7 +348,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Tower Catwalk North Stairs', StrS).dir(No, 0x40, Left, High),
|
create_door(player, 'Tower Catwalk North Stairs', StrS).dir(No, 0x40, Left, High),
|
||||||
create_door(player, 'Tower Antechamber South Stairs', StrS).dir(So, 0x30, Left, High),
|
create_door(player, 'Tower Antechamber South Stairs', StrS).dir(So, 0x30, Left, High),
|
||||||
create_door(player, 'Tower Antechamber NW', Intr).dir(No, 0x30, Left, High).pos(1),
|
create_door(player, 'Tower Antechamber NW', Intr).dir(No, 0x30, Left, High).pos(1),
|
||||||
create_door(player, 'Tower Altar SW', Intr).dir(So, 0x30, Left, High).no_exit().pos(1),
|
create_door(player, 'Tower Altar SW', Intr).dir(So, 0x30, Left, High).no_exit().trap(0x2).pos(1),
|
||||||
create_door(player, 'Tower Altar NW', Nrml).dir(No, 0x30, Left, High).pos(0),
|
create_door(player, 'Tower Altar NW', Nrml).dir(No, 0x30, Left, High).pos(0),
|
||||||
create_door(player, 'Tower Agahnim 1 SW', Nrml).dir(So, 0x20, Left, High).no_exit().trap(0x4).pos(0),
|
create_door(player, 'Tower Agahnim 1 SW', Nrml).dir(So, 0x20, Left, High).no_exit().trap(0x4).pos(0),
|
||||||
|
|
||||||
@@ -667,7 +667,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Thieves Conveyor Maze SW', Intr).dir(So, 0xbc, Left, High).pos(6),
|
create_door(player, 'Thieves Conveyor Maze SW', Intr).dir(So, 0xbc, Left, High).pos(6),
|
||||||
create_door(player, 'Thieves Pot Alcove Top NW', Intr).dir(No, 0xbc, Left, High).pos(6),
|
create_door(player, 'Thieves Pot Alcove Top NW', Intr).dir(No, 0xbc, Left, High).pos(6),
|
||||||
create_door(player, 'Thieves Conveyor Maze EN', Intr).dir(Ea, 0xbc, Top, High).pos(2),
|
create_door(player, 'Thieves Conveyor Maze EN', Intr).dir(Ea, 0xbc, Top, High).pos(2),
|
||||||
create_door(player, 'Thieves Hallway WN', Intr).dir(We, 0xbc, Top, High).no_exit().pos(2),
|
create_door(player, 'Thieves Hallway WN', Intr).dir(We, 0xbc, Top, High).no_exit().trap(0x1).pos(2),
|
||||||
create_door(player, 'Thieves Conveyor Maze Down Stairs', Sprl).dir(Dn, 0xbc, 0, HTH).ss(A, 0x11, 0x80, True, True),
|
create_door(player, 'Thieves Conveyor Maze Down Stairs', Sprl).dir(Dn, 0xbc, 0, HTH).ss(A, 0x11, 0x80, True, True),
|
||||||
create_door(player, 'Thieves Boss SE', Nrml).dir(So, 0xac, Right, High).no_exit().trap(0x4).pos(0),
|
create_door(player, 'Thieves Boss SE', Nrml).dir(So, 0xac, Right, High).no_exit().trap(0x4).pos(0),
|
||||||
create_door(player, 'Thieves Spike Track ES', Nrml).dir(Ea, 0xbb, Bot, High).pos(5),
|
create_door(player, 'Thieves Spike Track ES', Nrml).dir(Ea, 0xbb, Bot, High).pos(5),
|
||||||
@@ -742,7 +742,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Ice Big Key Push Block', Lgcl),
|
create_door(player, 'Ice Big Key Push Block', Lgcl),
|
||||||
create_door(player, 'Ice Big Key Down Ladder', Lddr).dir(So, 0x1f, 3, High),
|
create_door(player, 'Ice Big Key Down Ladder', Lddr).dir(So, 0x1f, 3, High),
|
||||||
create_door(player, 'Ice Stalfos Hint SE', Intr).dir(So, 0x3e, Right, High).pos(0),
|
create_door(player, 'Ice Stalfos Hint SE', Intr).dir(So, 0x3e, Right, High).pos(0),
|
||||||
create_door(player, 'Ice Conveyor NE', Intr).dir(No, 0x3e, Right, High).no_exit().pos(0),
|
create_door(player, 'Ice Conveyor NE', Intr).dir(No, 0x3e, Right, High).no_exit().trap(0x4).pos(0),
|
||||||
create_door(player, 'Ice Conveyor to Crystal', Lgcl),
|
create_door(player, 'Ice Conveyor to Crystal', Lgcl),
|
||||||
create_door(player, 'Ice Conveyor Crystal Exit', Lgcl),
|
create_door(player, 'Ice Conveyor Crystal Exit', Lgcl),
|
||||||
create_door(player, 'Ice Conveyor SW', Nrml).dir(So, 0x3e, Left, High).small_key().pos(1).portal(Z, 0x20),
|
create_door(player, 'Ice Conveyor SW', Nrml).dir(So, 0x3e, Left, High).small_key().pos(1).portal(Z, 0x20),
|
||||||
@@ -760,7 +760,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Ice Firebar ES', Intr).dir(Ea, 0x5e, Bot, High).pos(3),
|
create_door(player, 'Ice Firebar ES', Intr).dir(Ea, 0x5e, Bot, High).pos(3),
|
||||||
create_door(player, 'Ice Firebar Down Ladder', Lddr).dir(So, 0x5e, 5, High),
|
create_door(player, 'Ice Firebar Down Ladder', Lddr).dir(So, 0x5e, 5, High),
|
||||||
create_door(player, 'Ice Spike Cross NE', Intr).dir(No, 0x5e, Right, High).pos(1),
|
create_door(player, 'Ice Spike Cross NE', Intr).dir(No, 0x5e, Right, High).pos(1),
|
||||||
create_door(player, 'Ice Falling Square SE', Intr).dir(So, 0x5e, Right, High).no_exit().pos(1),
|
create_door(player, 'Ice Falling Square SE', Intr).dir(So, 0x5e, Right, High).no_exit().trap(0x1).pos(1),
|
||||||
create_door(player, 'Ice Falling Square Hole', Hole),
|
create_door(player, 'Ice Falling Square Hole', Hole),
|
||||||
create_door(player, 'Ice Spike Room WS', Nrml).dir(We, 0x5f, Bot, High).small_key().pos(0),
|
create_door(player, 'Ice Spike Room WS', Nrml).dir(We, 0x5f, Bot, High).small_key().pos(0),
|
||||||
create_door(player, 'Ice Spike Room Down Stairs', Sprl).dir(Dn, 0x5f, 3, HTH).ss(Z, 0x11, 0x48, True, True),
|
create_door(player, 'Ice Spike Room Down Stairs', Sprl).dir(Dn, 0x5f, 3, HTH).ss(Z, 0x11, 0x48, True, True),
|
||||||
@@ -840,12 +840,12 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Mire Hub Top NW', Nrml).dir(No, 0xc2, Left, High).pos(2),
|
create_door(player, 'Mire Hub Top NW', Nrml).dir(No, 0xc2, Left, High).pos(2),
|
||||||
create_door(player, 'Mire Lone Shooter WS', Nrml).dir(We, 0xc3, Bot, High).pos(6),
|
create_door(player, 'Mire Lone Shooter WS', Nrml).dir(We, 0xc3, Bot, High).pos(6),
|
||||||
create_door(player, 'Mire Lone Shooter ES', Intr).dir(Ea, 0xc3, Bot, High).pos(3),
|
create_door(player, 'Mire Lone Shooter ES', Intr).dir(Ea, 0xc3, Bot, High).pos(3),
|
||||||
create_door(player, 'Mire Falling Bridge WS', Intr).dir(We, 0xc3, Bot, High).no_exit().pos(3),
|
create_door(player, 'Mire Falling Bridge WS', Intr).dir(We, 0xc3, Bot, High).no_exit().trap(0x8).pos(3),
|
||||||
create_door(player, 'Mire Falling Bridge W', Intr).dir(We, 0xc3, Mid, High).pos(2),
|
create_door(player, 'Mire Falling Bridge W', Intr).dir(We, 0xc3, Mid, High).pos(2),
|
||||||
create_door(player, 'Mire Failure Bridge E', Intr).dir(Ea, 0xc3, Mid, High).no_exit().pos(2),
|
create_door(player, 'Mire Failure Bridge E', Intr).dir(Ea, 0xc3, Mid, High).no_exit().trap(0x1).pos(2),
|
||||||
create_door(player, 'Mire Failure Bridge W', Nrml).dir(We, 0xc3, Mid, High).pos(5),
|
create_door(player, 'Mire Failure Bridge W', Nrml).dir(We, 0xc3, Mid, High).pos(5),
|
||||||
create_door(player, 'Mire Falling Bridge WN', Intr).dir(We, 0xc3, Top, High).pos(1),
|
create_door(player, 'Mire Falling Bridge WN', Intr).dir(We, 0xc3, Top, High).pos(1),
|
||||||
create_door(player, 'Mire Map Spike Side EN', Intr).dir(Ea, 0xc3, Top, High).no_exit().pos(1),
|
create_door(player, 'Mire Map Spike Side EN', Intr).dir(Ea, 0xc3, Top, High).no_exit().trap(0x2).pos(1),
|
||||||
create_door(player, 'Mire Map Spot WN', Nrml).dir(We, 0xc3, Top, High).small_key().pos(0),
|
create_door(player, 'Mire Map Spot WN', Nrml).dir(We, 0xc3, Top, High).small_key().pos(0),
|
||||||
create_door(player, 'Mire Crystal Dead End NW', Nrml).dir(No, 0xc3, Left, High).pos(4),
|
create_door(player, 'Mire Crystal Dead End NW', Nrml).dir(No, 0xc3, Left, High).pos(4),
|
||||||
create_door(player, 'Mire Map Spike Side Drop Down', Lgcl),
|
create_door(player, 'Mire Map Spike Side Drop Down', Lgcl),
|
||||||
@@ -903,7 +903,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Mire Tile Room NW', Intr).dir(No, 0xc1, Left, High).pos(3),
|
create_door(player, 'Mire Tile Room NW', Intr).dir(No, 0xc1, Left, High).pos(3),
|
||||||
create_door(player, 'Mire Compass Room SW', Intr).dir(So, 0xc1, Left, High).pos(3),
|
create_door(player, 'Mire Compass Room SW', Intr).dir(So, 0xc1, Left, High).pos(3),
|
||||||
create_door(player, 'Mire Compass Room EN', Intr).dir(Ea, 0xc1, Top, High).pos(2),
|
create_door(player, 'Mire Compass Room EN', Intr).dir(Ea, 0xc1, Top, High).pos(2),
|
||||||
create_door(player, 'Mire Wizzrobe Bypass WN', Intr).dir(We, 0xc1, Top, High).no_exit().pos(2),
|
create_door(player, 'Mire Wizzrobe Bypass WN', Intr).dir(We, 0xc1, Top, High).no_exit().trap(0x1).pos(2),
|
||||||
create_door(player, 'Mire Compass Blue Barrier', Lgcl),
|
create_door(player, 'Mire Compass Blue Barrier', Lgcl),
|
||||||
create_door(player, 'Mire Compass Chest Exit', Lgcl),
|
create_door(player, 'Mire Compass Chest Exit', Lgcl),
|
||||||
create_door(player, 'Mire Neglected Room NE', Nrml).dir(No, 0xd1, Right, High).pos(2),
|
create_door(player, 'Mire Neglected Room NE', Nrml).dir(No, 0xd1, Right, High).pos(2),
|
||||||
@@ -912,7 +912,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Mire Neglected Room SE', Intr).dir(So, 0xd1, Right, High).pos(3),
|
create_door(player, 'Mire Neglected Room SE', Intr).dir(So, 0xd1, Right, High).pos(3),
|
||||||
create_door(player, 'Mire Chest View NE', Intr).dir(No, 0xd1, Right, High).pos(3),
|
create_door(player, 'Mire Chest View NE', Intr).dir(No, 0xd1, Right, High).pos(3),
|
||||||
create_door(player, 'Mire BK Chest Ledge WS', Intr).dir(We, 0xd1, Bot, High).pos(0),
|
create_door(player, 'Mire BK Chest Ledge WS', Intr).dir(We, 0xd1, Bot, High).pos(0),
|
||||||
create_door(player, 'Mire Warping Pool ES', Intr).dir(Ea, 0xd1, Bot, High).no_exit().pos(0),
|
create_door(player, 'Mire Warping Pool ES', Intr).dir(Ea, 0xd1, Bot, High).no_exit().trap(0x4).pos(0),
|
||||||
create_door(player, 'Mire Warping Pool Warp', Warp),
|
create_door(player, 'Mire Warping Pool Warp', Warp),
|
||||||
create_door(player, 'Mire Torches Top Down Stairs', Sprl).dir(Dn, 0x97, 0, HTH).ss(A, 0x11, 0xb0, True).kill(),
|
create_door(player, 'Mire Torches Top Down Stairs', Sprl).dir(Dn, 0x97, 0, HTH).ss(A, 0x11, 0xb0, True).kill(),
|
||||||
create_door(player, 'Mire Torches Top SW', Intr).dir(So, 0x97, Left, High).pos(1),
|
create_door(player, 'Mire Torches Top SW', Intr).dir(So, 0x97, Left, High).pos(1),
|
||||||
@@ -1011,7 +1011,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'TR Big Chest Entrance SE', Nrml).dir(So, 0x24, Right, High).pos(4).kill().portal(X, 0x00),
|
create_door(player, 'TR Big Chest Entrance SE', Nrml).dir(So, 0x24, Right, High).pos(4).kill().portal(X, 0x00),
|
||||||
create_door(player, 'TR Big Chest Entrance 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 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 SE', Intr).dir(So, 0x24, Right, High).no_exit().trap(0x8).pos(3),
|
||||||
create_door(player, 'TR Dodgers NE', Nrml).dir(No, 0x24, Right, High).big_key().pos(0),
|
create_door(player, 'TR Dodgers NE', Nrml).dir(No, 0x24, Right, High).big_key().pos(0),
|
||||||
create_door(player, 'TR Lazy Eyes SE', Nrml).dir(So, 0x23, Right, High).pos(0).portal(X, 0x00),
|
create_door(player, 'TR Lazy Eyes SE', Nrml).dir(So, 0x23, Right, High).pos(0).portal(X, 0x00),
|
||||||
create_door(player, 'TR Lazy Eyes ES', Nrml).dir(Ea, 0x23, Bot, High).pos(1),
|
create_door(player, 'TR Lazy Eyes ES', Nrml).dir(Ea, 0x23, Bot, High).pos(1),
|
||||||
@@ -1073,7 +1073,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'GT Hope Room EN', Nrml).dir(Ea, 0x8c, Top, High).trap(0x4).pos(0),
|
create_door(player, 'GT Hope Room EN', Nrml).dir(Ea, 0x8c, Top, High).trap(0x4).pos(0),
|
||||||
create_door(player, 'GT Torch EN', Intr).dir(Ea, 0x8c, Top, High).small_key().pos(2),
|
create_door(player, 'GT Torch EN', Intr).dir(Ea, 0x8c, Top, High).small_key().pos(2),
|
||||||
create_door(player, 'GT Hope Room WN', Intr).dir(We, 0x8c, Top, High).small_key().pos(2),
|
create_door(player, 'GT Hope Room WN', Intr).dir(We, 0x8c, Top, High).small_key().pos(2),
|
||||||
create_door(player, 'GT Torch SW', Intr).dir(So, 0x8c, Left, High).no_exit().pos(1),
|
create_door(player, 'GT Torch SW', Intr).dir(So, 0x8c, Left, High).no_exit().trap(0x2).pos(1),
|
||||||
create_door(player, 'GT Big Chest NW', Intr).dir(No, 0x8c, Left, High).pos(1),
|
create_door(player, 'GT Big Chest NW', Intr).dir(No, 0x8c, Left, High).pos(1),
|
||||||
create_door(player, 'GT Blocked Stairs Down Stairs', Sprl).dir(Dn, 0x8c, 3, HTH).ss(Z, 0x12, 0x40, True, True).kill(),
|
create_door(player, 'GT Blocked Stairs Down Stairs', Sprl).dir(Dn, 0x8c, 3, HTH).ss(Z, 0x12, 0x40, True, True).kill(),
|
||||||
create_door(player, 'GT Blocked Stairs Block Path', Lgcl),
|
create_door(player, 'GT Blocked Stairs Block Path', Lgcl),
|
||||||
@@ -1179,7 +1179,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'GT Ice Armos NE', Intr).dir(No, 0x1c, Right, High).pos(0),
|
create_door(player, 'GT Ice Armos NE', Intr).dir(No, 0x1c, Right, High).pos(0),
|
||||||
create_door(player, 'GT Big Key Room SE', Intr).dir(So, 0x1c, Right, High).pos(0),
|
create_door(player, 'GT Big Key Room SE', Intr).dir(So, 0x1c, Right, High).pos(0),
|
||||||
create_door(player, 'GT Ice Armos WS', Intr).dir(We, 0x1c, Bot, High).pos(1),
|
create_door(player, 'GT Ice Armos WS', Intr).dir(We, 0x1c, Bot, High).pos(1),
|
||||||
create_door(player, 'GT Four Torches ES', Intr).dir(Ea, 0x1c, Bot, High).no_exit().pos(1),
|
create_door(player, 'GT Four Torches ES', Intr).dir(Ea, 0x1c, Bot, High).no_exit().trap(0x2).pos(1),
|
||||||
create_door(player, 'GT Four Torches NW', Intr).dir(No, 0x1c, Left, High).pos(2),
|
create_door(player, 'GT Four Torches NW', Intr).dir(No, 0x1c, Left, High).pos(2),
|
||||||
create_door(player, 'GT Fairy Abyss SW', Intr).dir(So, 0x1c, Left, High).pos(2),
|
create_door(player, 'GT Fairy Abyss SW', Intr).dir(So, 0x1c, Left, High).pos(2),
|
||||||
create_door(player, 'GT Four Torches Up Stairs', Sprl).dir(Up, 0x1c, 0, HTH).ss(Z, 0x1b, 0x2c, True, True),
|
create_door(player, 'GT Four Torches Up Stairs', Sprl).dir(Up, 0x1c, 0, HTH).ss(Z, 0x1b, 0x2c, True, True),
|
||||||
@@ -1211,7 +1211,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'GT Beam Dash WS', Intr).dir(We, 0x6c, Bot, High).pos(0),
|
create_door(player, 'GT Beam Dash WS', Intr).dir(We, 0x6c, Bot, High).pos(0),
|
||||||
create_door(player, 'GT Lanmolas 2 ES', Intr).dir(Ea, 0x6c, Bot, High).pos(0),
|
create_door(player, 'GT Lanmolas 2 ES', Intr).dir(Ea, 0x6c, Bot, High).pos(0),
|
||||||
create_door(player, 'GT Lanmolas 2 NW', Intr).dir(No, 0x6c, Left, High).pos(1),
|
create_door(player, 'GT Lanmolas 2 NW', Intr).dir(No, 0x6c, Left, High).pos(1),
|
||||||
create_door(player, 'GT Quad Pot SW', Intr).dir(So, 0x6c, Left, High).no_exit().pos(1),
|
create_door(player, 'GT Quad Pot SW', Intr).dir(So, 0x6c, Left, High).no_exit().trap(0x2).pos(1),
|
||||||
create_door(player, 'GT Quad Pot Up Stairs', Sprl).dir(Up, 0x6c, 0, HTH).ss(A, 0x1b, 0x6c, True, True),
|
create_door(player, 'GT Quad Pot Up Stairs', Sprl).dir(Up, 0x6c, 0, HTH).ss(A, 0x1b, 0x6c, True, True),
|
||||||
create_door(player, 'GT Wizzrobes 1 Down Stairs', Sprl).dir(Dn, 0xa5, 0, HTH).ss(A, 0x12, 0x80, True, True),
|
create_door(player, 'GT Wizzrobes 1 Down Stairs', Sprl).dir(Dn, 0xa5, 0, HTH).ss(A, 0x12, 0x80, True, True),
|
||||||
create_door(player, 'GT Wizzrobes 1 SW', Intr).dir(So, 0xa5, Left, High).pos(2),
|
create_door(player, 'GT Wizzrobes 1 SW', Intr).dir(So, 0xa5, Left, High).pos(2),
|
||||||
|
|||||||
@@ -1262,7 +1262,7 @@ def simple_dungeon_builder(name, sector_list):
|
|||||||
return builder
|
return builder
|
||||||
|
|
||||||
|
|
||||||
def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
def create_dungeon_builders(all_sectors, connections_tuple, world, player, dungeon_pool,
|
||||||
dungeon_entrances=None, split_dungeon_entrances=None):
|
dungeon_entrances=None, split_dungeon_entrances=None):
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
logger.info('Shuffling Dungeon Sectors')
|
logger.info('Shuffling Dungeon Sectors')
|
||||||
@@ -1278,7 +1278,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
|||||||
global_pole = GlobalPolarity(candidate_sectors)
|
global_pole = GlobalPolarity(candidate_sectors)
|
||||||
|
|
||||||
dungeon_map = {}
|
dungeon_map = {}
|
||||||
for key in dungeon_regions.keys():
|
for key in dungeon_pool:
|
||||||
dungeon_map[key] = DungeonBuilder(key)
|
dungeon_map[key] = DungeonBuilder(key)
|
||||||
for key in dungeon_boss_sectors.keys():
|
for key in dungeon_boss_sectors.keys():
|
||||||
current_dungeon = dungeon_map[key]
|
current_dungeon = dungeon_map[key]
|
||||||
|
|||||||
@@ -316,9 +316,9 @@ class Room(object):
|
|||||||
byte_array.append(kind.value)
|
byte_array.append(kind.value)
|
||||||
return byte_array
|
return byte_array
|
||||||
|
|
||||||
def next_free(self):
|
def next_free(self, pos=4):
|
||||||
for i, door in enumerate(self.doorList):
|
for i, door in enumerate(self.doorList):
|
||||||
if i >= 4:
|
if i >= pos:
|
||||||
return None
|
return None
|
||||||
pos, kind = door
|
pos, kind = door
|
||||||
if kind not in [DoorKind.SmallKey, DoorKind.Dashable, DoorKind.Bombable, DoorKind.TrapTriggerable,
|
if kind not in [DoorKind.SmallKey, DoorKind.Dashable, DoorKind.Bombable, DoorKind.TrapTriggerable,
|
||||||
@@ -395,8 +395,8 @@ class DoorKind(Enum):
|
|||||||
Bombable = 0x2E
|
Bombable = 0x2E
|
||||||
BlastWall = 0x30
|
BlastWall = 0x30
|
||||||
Hidden = 0x32
|
Hidden = 0x32
|
||||||
TrapTriggerable = 0x36 # right side trap or south side trap
|
TrapTriggerable = 0x36 # right side trap or south side trap (West, South)
|
||||||
Trap2 = 0x38 # left side trap or north side trap
|
Trap2 = 0x38 # left side trap or north side trap (East, North)
|
||||||
NormalLow2 = 0x40
|
NormalLow2 = 0x40
|
||||||
TrapTriggerableLow = 0x44
|
TrapTriggerableLow = 0x44
|
||||||
Warp = 0x46
|
Warp = 0x46
|
||||||
|
|||||||
@@ -68,16 +68,11 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|||||||
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
|
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
|
||||||
doors_to_connect, idx = {}, 0
|
doors_to_connect, idx = {}, 0
|
||||||
all_regions = set()
|
all_regions = set()
|
||||||
bk_special = False
|
|
||||||
for sector in builder.sectors:
|
for sector in builder.sectors:
|
||||||
for door in sector.outstanding_doors:
|
for door in sector.outstanding_doors:
|
||||||
doors_to_connect[door.name] = door, idx
|
doors_to_connect[door.name] = door, idx
|
||||||
idx += 1
|
idx += 1
|
||||||
all_regions.update(sector.regions)
|
all_regions.update(sector.regions)
|
||||||
bk_special |= check_for_special(sector.regions)
|
|
||||||
bk_needed = False
|
|
||||||
for sector in builder.sectors:
|
|
||||||
bk_needed |= determine_if_bk_needed(sector, split_dungeon, bk_special, world, player)
|
|
||||||
finished = False
|
finished = False
|
||||||
# flag if standard and this is hyrule castle
|
# flag if standard and this is hyrule castle
|
||||||
paths = determine_paths_for_dungeon(world, player, all_regions, name)
|
paths = determine_paths_for_dungeon(world, player, all_regions, name)
|
||||||
@@ -96,9 +91,9 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|||||||
if hash_code not in hash_code_set:
|
if hash_code not in hash_code_set:
|
||||||
hash_code_set.add(hash_code)
|
hash_code_set.add(hash_code)
|
||||||
explored_state = explore_proposal(name, entrance_regions, all_regions, proposed_map, doors_to_connect,
|
explored_state = explore_proposal(name, entrance_regions, all_regions, proposed_map, doors_to_connect,
|
||||||
bk_needed, bk_special, world, player)
|
world, player)
|
||||||
if check_valid(name, explored_state, proposed_map, doors_to_connect, all_regions,
|
if check_valid(name, explored_state, proposed_map, doors_to_connect, all_regions,
|
||||||
bk_needed, bk_special, paths, entrance_regions, world, player):
|
paths, entrance_regions, world, player):
|
||||||
finished = True
|
finished = True
|
||||||
else:
|
else:
|
||||||
proposed_map, hash_code = modify_proposal(proposed_map, explored_state, doors_to_connect,
|
proposed_map, hash_code = modify_proposal(proposed_map, explored_state, doors_to_connect,
|
||||||
@@ -217,29 +212,21 @@ def modify_proposal(proposed_map, explored_state, doors_to_connect, hash_code_se
|
|||||||
return proposed_map, hash_code
|
return proposed_map, hash_code
|
||||||
|
|
||||||
|
|
||||||
def explore_proposal(name, entrance_regions, all_regions, proposed_map, valid_doors,
|
def explore_proposal(name, entrance_regions, all_regions, proposed_map, valid_doors, world, player):
|
||||||
bk_needed, bk_special, world, player):
|
|
||||||
start = ExplorationState(dungeon=name)
|
start = ExplorationState(dungeon=name)
|
||||||
start.big_key_special = bk_special
|
original_state = extend_reachable_state_lenient(entrance_regions, start, proposed_map,
|
||||||
|
all_regions, valid_doors, world, player)
|
||||||
bk_flag = False if world.bigkeyshuffle[player] and not bk_special else bk_needed
|
|
||||||
|
|
||||||
def exception(d):
|
|
||||||
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
|
||||||
original_state = extend_reachable_state_improved(entrance_regions, start, proposed_map, all_regions,
|
|
||||||
valid_doors, bk_flag, world, player, exception)
|
|
||||||
return original_state
|
return original_state
|
||||||
|
|
||||||
|
|
||||||
def check_valid(name, exploration_state, proposed_map, doors_to_connect, all_regions,
|
def check_valid(name, exploration_state, proposed_map, doors_to_connect, all_regions,
|
||||||
bk_needed, bk_special, paths, entrance_regions, world, player):
|
paths, entrance_regions, world, player):
|
||||||
all_visited = set()
|
all_visited = set()
|
||||||
all_visited.update(exploration_state.visited_blue)
|
all_visited.update(exploration_state.visited_blue)
|
||||||
all_visited.update(exploration_state.visited_orange)
|
all_visited.update(exploration_state.visited_orange)
|
||||||
if len(all_regions.difference(all_visited)) > 0:
|
if len(all_regions.difference(all_visited)) > 0:
|
||||||
return False
|
return False
|
||||||
if not valid_paths(name, paths, entrance_regions, doors_to_connect, all_regions, proposed_map,
|
if not valid_paths(name, paths, entrance_regions, doors_to_connect, all_regions, proposed_map, world, player):
|
||||||
bk_needed, bk_special, world, player):
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -262,8 +249,7 @@ def check_for_special(regions):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def valid_paths(name, paths, entrance_regions, valid_doors, all_regions, proposed_map,
|
def valid_paths(name, paths, entrance_regions, valid_doors, all_regions, proposed_map, world, player):
|
||||||
bk_needed, bk_special, world, player):
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if type(path) is tuple:
|
if type(path) is tuple:
|
||||||
target = path[1]
|
target = path[1]
|
||||||
@@ -275,14 +261,12 @@ def valid_paths(name, paths, entrance_regions, valid_doors, all_regions, propose
|
|||||||
else:
|
else:
|
||||||
target = path
|
target = path
|
||||||
start_regions = entrance_regions
|
start_regions = entrance_regions
|
||||||
if not valid_path(name, start_regions, target, valid_doors, proposed_map, all_regions,
|
if not valid_path(name, start_regions, target, valid_doors, proposed_map, all_regions, world, player):
|
||||||
bk_needed, bk_special, world, player):
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_regions,
|
def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_regions, world, player):
|
||||||
bk_needed, bk_special, world, player):
|
|
||||||
target_regions = set()
|
target_regions = set()
|
||||||
if type(target) is not list:
|
if type(target) is not list:
|
||||||
for region in all_regions:
|
for region in all_regions:
|
||||||
@@ -295,16 +279,11 @@ def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_re
|
|||||||
target_regions.add(region)
|
target_regions.add(region)
|
||||||
|
|
||||||
start = ExplorationState(dungeon=name)
|
start = ExplorationState(dungeon=name)
|
||||||
start.big_key_special = bk_special
|
original_state = extend_reachable_state_lenient(starting_regions, start, proposed_map, all_regions,
|
||||||
bk_flag = False if world.bigkeyshuffle[player] and not bk_special else bk_needed
|
valid_doors, world, player)
|
||||||
|
|
||||||
def exception(d):
|
|
||||||
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
|
||||||
original_state = extend_reachable_state_improved(starting_regions, start, proposed_map, all_regions,
|
|
||||||
valid_doors, bk_flag, world, player, exception)
|
|
||||||
|
|
||||||
for exp_door in original_state.unattached_doors:
|
for exp_door in original_state.unattached_doors:
|
||||||
if not exp_door.door.blocked:
|
if not exp_door.door.blocked or exp_door.door.trapFlag != 0:
|
||||||
return True # outstanding connection possible
|
return True # outstanding connection possible
|
||||||
for target in target_regions:
|
for target in target_regions:
|
||||||
if original_state.visited_at_all(target):
|
if original_state.visited_at_all(target):
|
||||||
@@ -637,6 +616,37 @@ class ExplorationState(object):
|
|||||||
elif not self.in_door_list(door, self.avail_doors):
|
elif not self.in_door_list(door, self.avail_doors):
|
||||||
self.append_door_to_list(door, self.avail_doors, flag)
|
self.append_door_to_list(door, self.avail_doors, flag)
|
||||||
|
|
||||||
|
def add_all_doors_check_proposed_2(self, region, proposed_map, valid_doors, flag, world, player):
|
||||||
|
for door in get_doors(world, region, player):
|
||||||
|
if door in proposed_map and door.name in valid_doors:
|
||||||
|
self.visited_doors.add(door)
|
||||||
|
if self.can_traverse_ignore_traps(door):
|
||||||
|
if door.controller is not None:
|
||||||
|
door = door.controller
|
||||||
|
if door.dest is None and door not in proposed_map.keys() and door.name in valid_doors:
|
||||||
|
if not self.in_door_list_ic(door, self.unattached_doors):
|
||||||
|
self.append_door_to_list(door, self.unattached_doors, flag)
|
||||||
|
else:
|
||||||
|
other = self.find_door_in_list(door, self.unattached_doors)
|
||||||
|
if self.crystal != other.crystal:
|
||||||
|
other.crystal = CrystalBarrier.Either
|
||||||
|
elif door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door,
|
||||||
|
self.event_doors):
|
||||||
|
self.append_door_to_list(door, self.event_doors, flag)
|
||||||
|
elif not self.in_door_list(door, self.avail_doors):
|
||||||
|
self.append_door_to_list(door, self.avail_doors, flag)
|
||||||
|
|
||||||
|
def add_all_doors_check_proposed_traps(self, region, proposed_traps, world, player):
|
||||||
|
for door in get_doors(world, region, player):
|
||||||
|
if self.can_traverse_ignore_traps(door) and door not in proposed_traps:
|
||||||
|
if door.controller is not None:
|
||||||
|
door = door.controller
|
||||||
|
if door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door,
|
||||||
|
self.event_doors):
|
||||||
|
self.append_door_to_list(door, self.event_doors, False)
|
||||||
|
elif not self.in_door_list(door, self.avail_doors):
|
||||||
|
self.append_door_to_list(door, self.avail_doors, False)
|
||||||
|
|
||||||
def add_all_doors_check_key_region(self, region, key_region, world, player):
|
def add_all_doors_check_key_region(self, region, key_region, world, player):
|
||||||
for door in get_doors(world, region, player):
|
for door in get_doors(world, region, player):
|
||||||
if self.can_traverse(door):
|
if self.can_traverse(door):
|
||||||
@@ -693,6 +703,13 @@ 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_ignore_traps(self, door):
|
||||||
|
if door.blocked and door.trapFlag == 0:
|
||||||
|
return False
|
||||||
|
if door.crystal not in [CrystalBarrier.Null, CrystalBarrier.Either]:
|
||||||
|
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
||||||
|
return True
|
||||||
|
|
||||||
def count_locations_exclude_specials(self, world, player):
|
def count_locations_exclude_specials(self, world, player):
|
||||||
return count_locations_exclude_big_chest(self.found_locations, world, player)
|
return count_locations_exclude_big_chest(self.found_locations, world, player)
|
||||||
|
|
||||||
@@ -801,6 +818,25 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, all_reg
|
|||||||
return local_state
|
return local_state
|
||||||
|
|
||||||
|
|
||||||
|
def extend_reachable_state_lenient(search_regions, state, proposed_map, all_regions, valid_doors, world, player):
|
||||||
|
local_state = state.copy()
|
||||||
|
for region in search_regions:
|
||||||
|
local_state.visit_region(region)
|
||||||
|
local_state.add_all_doors_check_proposed_2(region, proposed_map, valid_doors, world, player)
|
||||||
|
while len(local_state.avail_doors) > 0:
|
||||||
|
explorable_door = local_state.next_avail_door()
|
||||||
|
if explorable_door.door in proposed_map:
|
||||||
|
connect_region = world.get_entrance(proposed_map[explorable_door.door].name, player).parent_region
|
||||||
|
else:
|
||||||
|
connect_region = world.get_entrance(explorable_door.door.name, player).connected_region
|
||||||
|
if connect_region is not None:
|
||||||
|
if (valid_region_to_explore_in_regions(connect_region, all_regions, world, player)
|
||||||
|
and not local_state.visited(connect_region)):
|
||||||
|
local_state.visit_region(connect_region)
|
||||||
|
local_state.add_all_doors_check_proposed_2(connect_region, proposed_map, valid_doors, world, player)
|
||||||
|
return local_state
|
||||||
|
|
||||||
|
|
||||||
def special_big_key_found(state):
|
def special_big_key_found(state):
|
||||||
for location in state.found_locations:
|
for location in state.found_locations:
|
||||||
if location.forced_item and location.forced_item.bigkey:
|
if location.forced_item and location.forced_item.bigkey:
|
||||||
|
|||||||
Reference in New Issue
Block a user