Crossed Dungeon generation work
-Added more path checking to dungeon gen -Found and squashed a pair of infinite loops
This commit is contained in:
@@ -12,7 +12,7 @@ from Dungeons import dungeon_regions, region_starts, standard_starts, split_regi
|
|||||||
from Dungeons import dungeon_bigs, dungeon_keys, dungeon_hints
|
from Dungeons import dungeon_bigs, dungeon_keys, dungeon_hints
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
from RoomData import DoorKind, PairedDoor
|
from RoomData import DoorKind, PairedDoor
|
||||||
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate
|
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths
|
||||||
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
|
||||||
from KeyDoorShuffle import analyze_dungeon, validate_vanilla_key_logic, build_key_layout, validate_key_layout
|
from KeyDoorShuffle import analyze_dungeon, validate_vanilla_key_logic, build_key_layout, validate_key_layout
|
||||||
|
|
||||||
@@ -1134,31 +1134,6 @@ def random_door_type(door, partner, world, player, type_a, type_b, room_a, room_
|
|||||||
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
|
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
|
||||||
|
|
||||||
|
|
||||||
def determine_required_paths(world, player):
|
|
||||||
paths = {
|
|
||||||
'Hyrule Castle': ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby'],
|
|
||||||
'Eastern Palace': ['Eastern Boss'],
|
|
||||||
'Desert Palace': ['Desert Main Lobby', 'Desert East Lobby', 'Desert West Lobby', 'Desert Boss'],
|
|
||||||
'Tower of Hera': ['Hera Boss'],
|
|
||||||
'Agahnims Tower': ['Tower Agahnim 1'],
|
|
||||||
'Palace of Darkness': ['PoD Boss'],
|
|
||||||
'Swamp Palace': ['Swamp Boss'],
|
|
||||||
'Skull Woods': ['Skull 1 Lobby', 'Skull 2 East Lobby', 'Skull 2 West Lobby', 'Skull Boss'],
|
|
||||||
'Thieves Town': ['Thieves Boss', ('Thieves Blind\'s Cell', 'Thieves Boss')],
|
|
||||||
'Ice Palace': ['Ice Boss'],
|
|
||||||
'Misery Mire': ['Mire Boss'],
|
|
||||||
'Turtle Rock': ['TR Main Lobby', 'TR Lazy Eyes', 'TR Big Chest Entrance', 'TR Eye Bridge', 'TR Boss'],
|
|
||||||
'Ganons Tower': ['GT Agahnim 2']
|
|
||||||
}
|
|
||||||
if world.mode[player] == 'standard':
|
|
||||||
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
|
||||||
# noinspection PyTypeChecker
|
|
||||||
paths['Hyrule Castle'].append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
|
||||||
if world.doorShuffle[player] in ['basic']:
|
|
||||||
paths['Thieves Town'].append('Thieves Attic Window')
|
|
||||||
return paths
|
|
||||||
|
|
||||||
|
|
||||||
def overworld_prep(world, player):
|
def overworld_prep(world, player):
|
||||||
find_inaccessible_regions(world, player)
|
find_inaccessible_regions(world, player)
|
||||||
add_inaccessible_doors(world, player)
|
add_inaccessible_doors(world, player)
|
||||||
@@ -1220,37 +1195,38 @@ def create_door(world, player, entName, region_name):
|
|||||||
|
|
||||||
def check_required_paths(paths, world, player):
|
def check_required_paths(paths, world, player):
|
||||||
for dungeon_name in paths.keys():
|
for dungeon_name in paths.keys():
|
||||||
builder = world.dungeon_layouts[player][dungeon_name]
|
if dungeon_name in world.dungeon_layouts[player].keys():
|
||||||
if len(paths[dungeon_name]) > 0:
|
builder = world.dungeon_layouts[player][dungeon_name]
|
||||||
states_to_explore = defaultdict(list)
|
if len(paths[dungeon_name]) > 0:
|
||||||
for path in paths[dungeon_name]:
|
states_to_explore = defaultdict(list)
|
||||||
if type(path) is tuple:
|
for path in paths[dungeon_name]:
|
||||||
states_to_explore[tuple([path[0]])].append(path[1])
|
if type(path) is tuple:
|
||||||
else:
|
states_to_explore[tuple([path[0]])].append(path[1])
|
||||||
states_to_explore[tuple(builder.path_entrances)].append(path)
|
else:
|
||||||
cached_initial_state = None
|
states_to_explore[tuple(builder.path_entrances)].append(path)
|
||||||
for start_regs, dest_regs in states_to_explore.items():
|
cached_initial_state = None
|
||||||
check_paths = convert_regions(dest_regs, world, player)
|
for start_regs, dest_regs in states_to_explore.items():
|
||||||
start_regions = convert_regions(start_regs, world, player)
|
check_paths = convert_regions(dest_regs, world, player)
|
||||||
initial = start_regs == tuple(builder.path_entrances)
|
start_regions = convert_regions(start_regs, world, player)
|
||||||
if not initial or cached_initial_state is None:
|
initial = start_regs == tuple(builder.path_entrances)
|
||||||
init = determine_init_crystal(initial, cached_initial_state, start_regions)
|
if not initial or cached_initial_state is None:
|
||||||
state = ExplorationState(init, dungeon_name)
|
init = determine_init_crystal(initial, cached_initial_state, start_regions)
|
||||||
for region in start_regions:
|
state = ExplorationState(init, dungeon_name)
|
||||||
state.visit_region(region)
|
for region in start_regions:
|
||||||
state.add_all_doors_check_unattached(region, world, player)
|
state.visit_region(region)
|
||||||
explore_state(state, world, player)
|
state.add_all_doors_check_unattached(region, 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)
|
|
||||||
if not valid:
|
|
||||||
if check_for_pinball_fix(state, bad_region, world, player):
|
|
||||||
explore_state(state, world, player)
|
explore_state(state, world, player)
|
||||||
valid, bad_region = check_if_regions_visited(state, check_paths)
|
if initial and cached_initial_state is None:
|
||||||
if not valid:
|
cached_initial_state = state
|
||||||
raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name))
|
else:
|
||||||
|
state = cached_initial_state
|
||||||
|
valid, bad_region = check_if_regions_visited(state, check_paths)
|
||||||
|
if not valid:
|
||||||
|
if check_for_pinball_fix(state, bad_region, world, player):
|
||||||
|
explore_state(state, world, player)
|
||||||
|
valid, bad_region = check_if_regions_visited(state, check_paths)
|
||||||
|
if not valid:
|
||||||
|
raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name))
|
||||||
|
|
||||||
|
|
||||||
def determine_init_crystal(initial, state, start_regions):
|
def determine_init_crystal(initial, state, start_regions):
|
||||||
|
|||||||
@@ -41,9 +41,11 @@ def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
|
|||||||
all_regions.update(sector.regions)
|
all_regions.update(sector.regions)
|
||||||
bk_needed = bk_needed or determine_if_bk_needed(sector, split_dungeon, world, player)
|
bk_needed = bk_needed or determine_if_bk_needed(sector, split_dungeon, world, player)
|
||||||
bk_special = bk_special or check_for_special(sector)
|
bk_special = bk_special or check_for_special(sector)
|
||||||
|
paths = determine_required_paths(world, player, split_dungeon, all_regions, builder.name)[builder.name]
|
||||||
dungeon, hangers, hooks = gen_dungeon_info(builder.name, builder.sectors, entrance_regions, all_regions,
|
dungeon, hangers, hooks = gen_dungeon_info(builder.name, builder.sectors, entrance_regions, all_regions,
|
||||||
proposed_map, doors_to_connect, bk_needed, bk_special, world, player)
|
proposed_map, doors_to_connect, bk_needed, bk_special, world, player)
|
||||||
return check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions, bk_needed, False, False)
|
return check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||||
|
bk_needed, paths, entrance_regions)
|
||||||
|
|
||||||
|
|
||||||
def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player):
|
def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player):
|
||||||
@@ -104,8 +106,7 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|||||||
attempt = 1
|
attempt = 1
|
||||||
finished = False
|
finished = False
|
||||||
# flag if standard and this is hyrule castle
|
# flag if standard and this is hyrule castle
|
||||||
std_flag = world.mode[player] == 'standard' and bk_special
|
paths = determine_required_paths(world, player, split_dungeon, all_regions, name)[name]
|
||||||
maiden_flag = name == 'Thieves Town'
|
|
||||||
while not finished:
|
while not finished:
|
||||||
# what are my choices?
|
# what are my choices?
|
||||||
itr += 1
|
itr += 1
|
||||||
@@ -125,7 +126,7 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|||||||
doors_to_connect, bk_needed, bk_special, world, player)
|
doors_to_connect, bk_needed, bk_special, world, player)
|
||||||
dungeon_cache[depth] = dungeon, hangers, hooks
|
dungeon_cache[depth] = dungeon, hangers, hooks
|
||||||
valid = check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
valid = check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||||
bk_needed, std_flag, maiden_flag)
|
bk_needed, paths, entrance_regions)
|
||||||
else:
|
else:
|
||||||
dungeon, hangers, hooks = dungeon_cache[depth]
|
dungeon, hangers, hooks = dungeon_cache[depth]
|
||||||
valid = True
|
valid = True
|
||||||
@@ -385,7 +386,7 @@ def filter_choices(next_hanger, door, orig_hang, prev_choices, hook_candidates):
|
|||||||
|
|
||||||
|
|
||||||
def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
||||||
bk_needed, std_flag, maiden_flag):
|
bk_needed, paths, entrance_regions):
|
||||||
# evaluate if everything is still plausible
|
# evaluate if everything is still plausible
|
||||||
|
|
||||||
# only origin is left in the dungeon and not everything is connected
|
# only origin is left in the dungeon and not everything is connected
|
||||||
@@ -433,9 +434,7 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
|||||||
return False
|
return False
|
||||||
if not bk_possible:
|
if not bk_possible:
|
||||||
return False
|
return False
|
||||||
if maiden_flag and not maiden_valid(doors_to_connect, all_regions, proposed_map):
|
if not valid_paths(paths, entrance_regions, doors_to_connect, all_regions, proposed_map):
|
||||||
return False
|
|
||||||
if std_flag and not cellblock_valid(doors_to_connect, all_regions, proposed_map):
|
|
||||||
return False
|
return False
|
||||||
new_hangers_found = True
|
new_hangers_found = True
|
||||||
accessible_hook_types = []
|
accessible_hook_types = []
|
||||||
@@ -464,18 +463,29 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
|||||||
return len(all_hangers.difference(hanger_matching)) == 0
|
return len(all_hangers.difference(hanger_matching)) == 0
|
||||||
|
|
||||||
|
|
||||||
# todo: combine these two search methods
|
def valid_paths(paths, entrance_regions, valid_doors, all_regions, proposed_map):
|
||||||
def maiden_valid(valid_doors, all_regions, proposed_map):
|
for path in paths:
|
||||||
cellblock = None
|
if type(path) is tuple:
|
||||||
for region in all_regions:
|
target = path[1]
|
||||||
if "Thieves Blind's Cell" == region.name:
|
start_regions = []
|
||||||
cellblock = region
|
for region in all_regions:
|
||||||
break
|
if path[0] == region.name:
|
||||||
queue = deque([cellblock])
|
start_regions.append(region)
|
||||||
visited = {cellblock}
|
break
|
||||||
|
else:
|
||||||
|
target = path
|
||||||
|
start_regions = entrance_regions
|
||||||
|
if not valid_path(start_regions, target, valid_doors, proposed_map):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def valid_path(starting_regions, target, valid_doors, proposed_map):
|
||||||
|
queue = deque(starting_regions)
|
||||||
|
visited = set(starting_regions)
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
region = queue.popleft()
|
region = queue.popleft()
|
||||||
if region.name == 'Thieves Boss':
|
if region.name == target:
|
||||||
return True
|
return True
|
||||||
for ext in region.exits:
|
for ext in region.exits:
|
||||||
connect = ext.connected_region
|
connect = ext.connected_region
|
||||||
@@ -494,39 +504,47 @@ def maiden_valid(valid_doors, all_regions, proposed_map):
|
|||||||
if door is not None and not door.blocked and connect not in visited:
|
if door is not None and not door.blocked and connect not in visited:
|
||||||
visited.add(connect)
|
visited.add(connect)
|
||||||
queue.append(connect)
|
queue.append(connect)
|
||||||
return False # couldn't find an outstanding door or Blind
|
return False # couldn't find an outstanding door or the target
|
||||||
|
|
||||||
|
|
||||||
|
def determine_required_paths(world, player, split_dungeon=False, all_regions=None, name=None):
|
||||||
|
if all_regions is None:
|
||||||
|
all_regions = set()
|
||||||
|
paths = {
|
||||||
|
'Hyrule Castle': ['Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby'],
|
||||||
|
'Eastern Palace': ['Eastern Boss'],
|
||||||
|
'Desert Palace': ['Desert Main Lobby', 'Desert East Lobby', 'Desert West Lobby', 'Desert Boss'],
|
||||||
|
'Tower of Hera': ['Hera Boss'],
|
||||||
|
'Agahnims Tower': ['Tower Agahnim 1'],
|
||||||
|
'Palace of Darkness': ['PoD Boss'],
|
||||||
|
'Swamp Palace': ['Swamp Boss'],
|
||||||
|
'Skull Woods': ['Skull 1 Lobby', 'Skull 2 East Lobby', 'Skull 2 West Lobby', 'Skull Boss'],
|
||||||
|
'Thieves Town': ['Thieves Boss', ('Thieves Blind\'s Cell', 'Thieves Boss')],
|
||||||
|
'Ice Palace': ['Ice Boss'],
|
||||||
|
'Misery Mire': ['Mire Boss'],
|
||||||
|
'Turtle Rock': ['TR Main Lobby', 'TR Lazy Eyes', 'TR Big Chest Entrance', 'TR Eye Bridge', 'TR Boss'],
|
||||||
|
'Ganons Tower': ['GT Agahnim 2'],
|
||||||
|
'Skull Woods 1': ['Skull 1 Lobby'],
|
||||||
|
'Skull Woods 2': ['Skull 2 East Lobby', 'Skull 2 West Lobby'],
|
||||||
|
'Skull Woods 3': [],
|
||||||
|
'Desert Palace Main': ['Desert Main Lobby', 'Desert East Lobby', 'Desert West Lobby'],
|
||||||
|
'Desert Palace Back': []
|
||||||
|
}
|
||||||
|
if world.mode[player] == 'standard':
|
||||||
|
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
||||||
|
# noinspection PyTypeChecker
|
||||||
|
paths['Hyrule Castle'].append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
||||||
|
if world.doorShuffle[player] in ['basic']:
|
||||||
|
paths['Thieves Town'].append('Thieves Attic Window')
|
||||||
|
if split_dungeon:
|
||||||
|
if world.get_region('Desert Boss', player) in all_regions:
|
||||||
|
paths[name].append('Desert Boss')
|
||||||
|
if world.get_region('Skull Boss', player) in all_regions:
|
||||||
|
paths[name].append('Skull Boss')
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cellblock_valid(valid_doors, all_regions, proposed_map):
|
|
||||||
cellblock = None
|
|
||||||
for region in all_regions:
|
|
||||||
if 'Hyrule Dungeon Cellblock' == region.name:
|
|
||||||
cellblock = region
|
|
||||||
break
|
|
||||||
queue = deque([cellblock])
|
|
||||||
visited = {cellblock}
|
|
||||||
while len(queue) > 0:
|
|
||||||
region = queue.popleft()
|
|
||||||
if region.name == 'Sanctuary':
|
|
||||||
return True
|
|
||||||
for ext in region.exits:
|
|
||||||
connect = ext.connected_region
|
|
||||||
if connect is None and ext.name in valid_doors:
|
|
||||||
door = valid_doors[ext.name]
|
|
||||||
if not door.blocked:
|
|
||||||
if door in proposed_map:
|
|
||||||
new_region = proposed_map[door].entrance.parent_region
|
|
||||||
if new_region not in visited:
|
|
||||||
visited.add(new_region)
|
|
||||||
queue.append(new_region)
|
|
||||||
else:
|
|
||||||
return True # outstanding connection possible
|
|
||||||
elif connect is not None:
|
|
||||||
door = ext.door
|
|
||||||
if door is not None and not door.blocked and connect not in visited:
|
|
||||||
visited.add(connect)
|
|
||||||
queue.append(connect)
|
|
||||||
return False # couldn't find an outstanding door or the sanctuary
|
|
||||||
|
|
||||||
|
|
||||||
def winnow_hangers(hangers, hooks):
|
def winnow_hangers(hangers, hooks):
|
||||||
@@ -864,6 +882,8 @@ class ExplorationState(object):
|
|||||||
def add_all_doors_check_unattached(self, region, world, player):
|
def add_all_doors_check_unattached(self, 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):
|
||||||
|
if door.controller is not None:
|
||||||
|
door = door.controller
|
||||||
if door.dest is None and not self.in_door_list_ic(door, self.unattached_doors):
|
if door.dest is None and not self.in_door_list_ic(door, self.unattached_doors):
|
||||||
self.append_door_to_list(door, self.unattached_doors)
|
self.append_door_to_list(door, self.unattached_doors)
|
||||||
elif door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door,
|
elif door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door,
|
||||||
@@ -1170,7 +1190,8 @@ def simple_dungeon_builder(name, sector_list):
|
|||||||
return builder
|
return builder
|
||||||
|
|
||||||
|
|
||||||
def create_dungeon_builders(all_sectors, connections_tuple, world, player, dungeon_entrances=None, split_dungeon_entrances=None):
|
def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
||||||
|
dungeon_entrances=None, split_dungeon_entrances=None):
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
|
|
||||||
if dungeon_entrances is None:
|
if dungeon_entrances is None:
|
||||||
@@ -1960,7 +1981,7 @@ def parallel_full_neutralization(dungeon_map, polarized_sectors, global_pole):
|
|||||||
candidates, last_depth = find_exact_neutralizing_candidates_parallel_db(builders_to_check, solution_list,
|
candidates, last_depth = find_exact_neutralizing_candidates_parallel_db(builders_to_check, solution_list,
|
||||||
avail_sectors, current_depth)
|
avail_sectors, current_depth)
|
||||||
increment_depth = True
|
increment_depth = True
|
||||||
|
any_valid = False
|
||||||
for builder, candidate_list in candidates.items():
|
for builder, candidate_list in candidates.items():
|
||||||
valid, sectors = False, None
|
valid, sectors = False, None
|
||||||
while not valid:
|
while not valid:
|
||||||
@@ -1974,6 +1995,7 @@ def parallel_full_neutralization(dungeon_map, polarized_sectors, global_pole):
|
|||||||
proposal[builder].extend(sectors)
|
proposal[builder].extend(sectors)
|
||||||
valid = global_pole.is_valid_multi_choice_2(dungeon_map, builders, proposal)
|
valid = global_pole.is_valid_multi_choice_2(dungeon_map, builders, proposal)
|
||||||
if valid:
|
if valid:
|
||||||
|
any_valid = True
|
||||||
solution_list[builder].extend(sectors)
|
solution_list[builder].extend(sectors)
|
||||||
for sector in sectors:
|
for sector in sectors:
|
||||||
avail_sectors.remove(sector)
|
avail_sectors.remove(sector)
|
||||||
@@ -1988,6 +2010,8 @@ def parallel_full_neutralization(dungeon_map, polarized_sectors, global_pole):
|
|||||||
break
|
break
|
||||||
other_cand_list[:] = [x for x in other_cand_list if x not in candidates_to_remove]
|
other_cand_list[:] = [x for x in other_cand_list if x not in candidates_to_remove]
|
||||||
# remove sectors from other candidate lists
|
# remove sectors from other candidate lists
|
||||||
|
if not any_valid:
|
||||||
|
increment_depth = True
|
||||||
current_depth = last_depth + 1 if increment_depth else last_depth
|
current_depth = last_depth + 1 if increment_depth else last_depth
|
||||||
finished = all([(x.polarity()+sum_polarity(solution_list[x])).is_neutral() for x in builders])
|
finished = all([(x.polarity()+sum_polarity(solution_list[x])).is_neutral() for x in builders])
|
||||||
logging.getLogger('').info(f'-Balanced solution found in {time.process_time()-start}')
|
logging.getLogger('').info(f'-Balanced solution found in {time.process_time()-start}')
|
||||||
@@ -2654,7 +2678,7 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
builder.split_dungeon_map[name].valid_proposal = proposal
|
builder.split_dungeon_map[name].valid_proposal = proposal
|
||||||
return builder.split_dungeon_map # we made this earlier in gen, just use it
|
return builder.split_dungeon_map # we made this earlier in gen, just use it
|
||||||
|
|
||||||
attempts = 0
|
attempts, comb_w_replace = 0, None
|
||||||
while attempts < 5: # does not solve coin flips 3% of the time
|
while attempts < 5: # does not solve coin flips 3% of the time
|
||||||
try:
|
try:
|
||||||
candidate_sectors = dict.fromkeys(builder.sectors)
|
candidate_sectors = dict.fromkeys(builder.sectors)
|
||||||
@@ -2667,9 +2691,13 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
sub_builder.all_entrances = split_entrances
|
sub_builder.all_entrances = split_entrances
|
||||||
for r_name in split_entrances:
|
for r_name in split_entrances:
|
||||||
assign_sector(find_sector(r_name, candidate_sectors), sub_builder, candidate_sectors, global_pole)
|
assign_sector(find_sector(r_name, candidate_sectors), sub_builder, candidate_sectors, global_pole)
|
||||||
|
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
||||||
return balance_split(candidate_sectors, dungeon_map, global_pole, builder_info)
|
return balance_split(candidate_sectors, dungeon_map, global_pole, builder_info)
|
||||||
except (GenerationException, NeutralizingException):
|
except (GenerationException, NeutralizingException):
|
||||||
attempts += 1
|
if comb_w_replace and comb_w_replace <= 10000:
|
||||||
|
attempts += 5 # all the combinations were tried already, no use repeating
|
||||||
|
else:
|
||||||
|
attempts += 1
|
||||||
raise GenerationException('Unable to resolve in 5 attempts')
|
raise GenerationException('Unable to resolve in 5 attempts')
|
||||||
|
|
||||||
|
|
||||||
@@ -3018,7 +3046,8 @@ class DungeonAccess:
|
|||||||
self.door_access[partner_door] = crystal_state
|
self.door_access[partner_door] = crystal_state
|
||||||
if partner_door in self.outstanding_doors.keys():
|
if partner_door in self.outstanding_doors.keys():
|
||||||
self.outstanding_doors[partner_door] = crystal_state
|
self.outstanding_doors[partner_door] = crystal_state
|
||||||
queue.append(partner_door)
|
if partner_door not in visited:
|
||||||
|
queue.append(partner_door)
|
||||||
else:
|
else:
|
||||||
for key, door_list in next_eq.benefit.items():
|
for key, door_list in next_eq.benefit.items():
|
||||||
for cand_door in door_list:
|
for cand_door in door_list:
|
||||||
@@ -3102,7 +3131,8 @@ def check_for_valid_layout(builder, sector_list, builder_info):
|
|||||||
name_bits = name.split(" ")
|
name_bits = name.split(" ")
|
||||||
orig_name = " ".join(name_bits[:-1])
|
orig_name = " ".join(name_bits[:-1])
|
||||||
entrance_regions = split_dungeon_entrances[orig_name][name_bits[-1]]
|
entrance_regions = split_dungeon_entrances[orig_name][name_bits[-1]]
|
||||||
# todo: destination regions?
|
# todo: this is hardcoded information for random entrances
|
||||||
|
entrance_regions = [x for x in entrance_regions if x not in split_check_entrance_invalid]
|
||||||
proposal = generate_dungeon_find_proposal(split_build, entrance_regions, True, world, player)
|
proposal = generate_dungeon_find_proposal(split_build, entrance_regions, True, world, player)
|
||||||
# record split proposals
|
# record split proposals
|
||||||
builder.valid_proposal[name] = proposal
|
builder.valid_proposal[name] = proposal
|
||||||
@@ -3700,4 +3730,8 @@ destination_entrances = [
|
|||||||
'Skull Back Drop', 'TR Big Chest Entrance', 'TR Eye Bridge', 'TR Lazy Eyes'
|
'Skull Back Drop', 'TR Big Chest Entrance', 'TR Eye Bridge', 'TR Lazy Eyes'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
split_check_entrance_invalid = [
|
||||||
|
'Desert East Lobby', 'Skull 2 West Lobby'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user