Standard throne room changes
This commit is contained in:
@@ -14,7 +14,7 @@ from Items import ItemFactory
|
|||||||
from RoomData import DoorKind, PairedDoor, reset_rooms
|
from RoomData import DoorKind, PairedDoor, reset_rooms
|
||||||
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths, drop_entrances
|
from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, 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
|
||||||
from DungeonGenerator import dungeon_portals, dungeon_drops, GenerationException
|
from DungeonGenerator import dungeon_portals, dungeon_drops, GenerationException, connect_doors
|
||||||
from DungeonGenerator import valid_region_to_explore as valid_region_to_explore_lim
|
from DungeonGenerator import valid_region_to_explore as valid_region_to_explore_lim
|
||||||
from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock
|
from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock
|
||||||
from Utils import ncr, kth_combination
|
from Utils import ncr, kth_combination
|
||||||
@@ -685,6 +685,13 @@ def create_dungeon_entrances(world, player):
|
|||||||
choice = random.choice(filtered_choices)
|
choice = random.choice(filtered_choices)
|
||||||
r_name = portal.door.entrance.parent_region.name
|
r_name = portal.door.entrance.parent_region.name
|
||||||
split_map[key][choice].append(r_name)
|
split_map[key][choice].append(r_name)
|
||||||
|
elif key == 'Hyrule Castle' and world.mode[player] == 'standard':
|
||||||
|
for portal_name in portal_list:
|
||||||
|
portal = world.get_portal(portal_name, player)
|
||||||
|
choice = 'Sewers' if portal_name == 'Sanctuary' else 'Dungeon'
|
||||||
|
r_name = portal.door.entrance.parent_region.name
|
||||||
|
split_map[key][choice].append(r_name)
|
||||||
|
entrance_map[key].append(r_name)
|
||||||
else:
|
else:
|
||||||
for portal_name in portal_list:
|
for portal_name in portal_list:
|
||||||
portal = world.get_portal(portal_name, player)
|
portal = world.get_portal(portal_name, player)
|
||||||
@@ -774,7 +781,8 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
|
|||||||
logging.getLogger('').info(world.fish.translate("cli", "cli", "generating.dungeon"))
|
logging.getLogger('').info(world.fish.translate("cli", "cli", "generating.dungeon"))
|
||||||
while len(sector_queue) > 0:
|
while len(sector_queue) > 0:
|
||||||
builder = sector_queue.popleft()
|
builder = sector_queue.popleft()
|
||||||
split_dungeon = builder.name.startswith('Desert Palace') or builder.name.startswith('Skull Woods')
|
split_dungeon = (builder.name.startswith('Desert Palace') or builder.name.startswith('Skull Woods')
|
||||||
|
or (builder.name.startswith('Hyrule Castle') and world.mode[player] == 'standard'))
|
||||||
name = builder.name
|
name = builder.name
|
||||||
if split_dungeon:
|
if split_dungeon:
|
||||||
name = ' '.join(builder.name.split(' ')[:-1])
|
name = ' '.join(builder.name.split(' ')[:-1])
|
||||||
@@ -782,6 +790,7 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
|
|||||||
del dungeon_builders[builder.name]
|
del dungeon_builders[builder.name]
|
||||||
continue
|
continue
|
||||||
origin_list = list(builder.entrance_list)
|
origin_list = list(builder.entrance_list)
|
||||||
|
find_standard_origins(builder, recombinant_builders, origin_list)
|
||||||
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, name)
|
find_enabled_origins(builder.sectors, enabled_entrances, origin_list, entrances_map, name)
|
||||||
split_dungeon = treat_split_as_whole_dungeon(split_dungeon, name, origin_list, world, player)
|
split_dungeon = treat_split_as_whole_dungeon(split_dungeon, name, origin_list, world, player)
|
||||||
if len(origin_list) <= 0 or not pre_validate(builder, origin_list, split_dungeon, world, player):
|
if len(origin_list) <= 0 or not pre_validate(builder, origin_list, split_dungeon, world, player):
|
||||||
@@ -798,7 +807,7 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
|
|||||||
builder.master_sector = ds
|
builder.master_sector = ds
|
||||||
builder.layout_starts = origin_list if len(builder.entrance_list) <= 0 else builder.entrance_list
|
builder.layout_starts = origin_list if len(builder.entrance_list) <= 0 else builder.entrance_list
|
||||||
last_key = None
|
last_key = None
|
||||||
combine_layouts(recombinant_builders, dungeon_builders, entrances_map)
|
combine_layouts(recombinant_builders, dungeon_builders, entrances_map, world, player)
|
||||||
world.dungeon_layouts[player] = {}
|
world.dungeon_layouts[player] = {}
|
||||||
for builder in dungeon_builders.values():
|
for builder in dungeon_builders.values():
|
||||||
builder.entrance_list = builder.layout_starts = builder.path_entrances = find_accessible_entrances(world, player, builder)
|
builder.entrance_list = builder.layout_starts = builder.path_entrances = find_accessible_entrances(world, player, builder)
|
||||||
@@ -872,6 +881,14 @@ def add_shuffled_entrances(sectors, region_list, entrance_list):
|
|||||||
entrance_list.append(region.name)
|
entrance_list.append(region.name)
|
||||||
|
|
||||||
|
|
||||||
|
def find_standard_origins(builder, recomb_builders, origin_list):
|
||||||
|
if builder.name == 'Hyrule Castle Sewers':
|
||||||
|
throne_door = recomb_builders['Hyrule Castle'].throne_door
|
||||||
|
sewer_entrance = throne_door.entrance.parent_region.name
|
||||||
|
if sewer_entrance not in origin_list:
|
||||||
|
origin_list.append(sewer_entrance)
|
||||||
|
|
||||||
|
|
||||||
def find_enabled_origins(sectors, enabled, entrance_list, entrance_map, key):
|
def find_enabled_origins(sectors, enabled, entrance_list, entrance_map, key):
|
||||||
for sector in sectors:
|
for sector in sectors:
|
||||||
for region in sector.regions:
|
for region in sector.regions:
|
||||||
@@ -1378,7 +1395,7 @@ def merge_sectors(all_sectors, 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(recombinant_builders, dungeon_builders, entrances_map):
|
def combine_layouts(recombinant_builders, dungeon_builders, entrances_map, world, player):
|
||||||
for recombine in recombinant_builders.values():
|
for recombine in recombinant_builders.values():
|
||||||
queue = deque(dungeon_builders.values())
|
queue = deque(dungeon_builders.values())
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
@@ -1390,6 +1407,10 @@ def combine_layouts(recombinant_builders, dungeon_builders, entrances_map):
|
|||||||
recombine.master_sector.name = recombine.name
|
recombine.master_sector.name = recombine.name
|
||||||
else:
|
else:
|
||||||
recombine.master_sector.regions.extend(builder.master_sector.regions)
|
recombine.master_sector.regions.extend(builder.master_sector.regions)
|
||||||
|
if recombine.name == 'Hyrule Castle':
|
||||||
|
recombine.master_sector.regions.extend(recombine.throne_sector.regions)
|
||||||
|
throne_n = world.get_door('Hyrule Castle Throne Room N', player)
|
||||||
|
connect_doors(throne_n, recombine.throne_door)
|
||||||
recombine.layout_starts = list(entrances_map[recombine.name])
|
recombine.layout_starts = list(entrances_map[recombine.name])
|
||||||
dungeon_builders[recombine.name] = recombine
|
dungeon_builders[recombine.name] = recombine
|
||||||
|
|
||||||
@@ -1487,11 +1508,14 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True
|
|||||||
proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num)
|
proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num)
|
||||||
|
|
||||||
# eliminate start region if portal marked as destination
|
# eliminate start region if portal marked as destination
|
||||||
|
std_flag = world.mode[player] == 'standard' and builder.name == 'Hyrule Castle'
|
||||||
excluded = {}
|
excluded = {}
|
||||||
for region in start_regions:
|
for region in start_regions:
|
||||||
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
|
||||||
if portal and portal.destination:
|
if portal and portal.destination:
|
||||||
excluded[region] = None
|
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()]
|
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)
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
|
|||||||
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
|
doors_to_connect[door.name] = door
|
||||||
all_regions.update(sector.regions)
|
if world.mode[player] == 'standard' and builder.name == 'Hyrule Castle Dungeon':
|
||||||
|
all_regions.update([x for x in sector.regions if x.name != 'Hyrule Castle Behind Tapestry'])
|
||||||
|
else:
|
||||||
|
all_regions.update(sector.regions)
|
||||||
bk_special |= check_for_special(sector.regions)
|
bk_special |= check_for_special(sector.regions)
|
||||||
bk_needed = False
|
bk_needed = False
|
||||||
for sector in builder.sectors:
|
for sector in builder.sectors:
|
||||||
@@ -91,18 +94,27 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|||||||
p_region = portal.door.entrance.connected_region
|
p_region = portal.door.entrance.connected_region
|
||||||
access_region = next(x.parent_region for x in p_region.entrances
|
access_region = next(x.parent_region for x in p_region.entrances
|
||||||
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld])
|
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld])
|
||||||
|
if ((access_region.name in world.inaccessible_regions[player] and
|
||||||
|
region.name not in world.enabled_entrances[player])
|
||||||
|
or (world.mode[player] == 'standard' and access_region.name != 'Hyrule Castle Courtyard'
|
||||||
|
and 'Hyrule Castle' in builder.name)):
|
||||||
|
excluded[region] = None
|
||||||
|
else: # for non-portals, holes and sewers in std
|
||||||
|
access_region = next((x.parent_region for x in region.entrances
|
||||||
|
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]
|
||||||
|
or x.parent_region.name == 'Sewer Drop'), None)
|
||||||
|
if access_region is None:
|
||||||
|
if builder.sewers_access is None:
|
||||||
|
excluded[region] = None
|
||||||
|
else:
|
||||||
|
if access_region.name == 'Sewer Drop':
|
||||||
|
if world.mode[player] == 'standard' and (builder.sewers_access is None
|
||||||
|
or builder.sewers_access.entrance.parent_region != region):
|
||||||
|
excluded[region] = None
|
||||||
|
access_region = next(x.parent_region for x in access_region.entrances)
|
||||||
if (access_region.name in world.inaccessible_regions[player] and
|
if (access_region.name in world.inaccessible_regions[player] and
|
||||||
region.name not in world.enabled_entrances[player]):
|
region.name not in world.enabled_entrances[player]):
|
||||||
excluded[region] = None
|
excluded[region] = None
|
||||||
elif len(region.entrances) == 1: # for holes
|
|
||||||
access_region = next(x.parent_region for x in region.entrances
|
|
||||||
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]
|
|
||||||
or x.parent_region.name == 'Sewer Drop')
|
|
||||||
if access_region.name == 'Sewer Drop':
|
|
||||||
access_region = next(x.parent_region for x in access_region.entrances)
|
|
||||||
if (access_region.name in world.inaccessible_regions[player] and
|
|
||||||
region.name not in world.enabled_entrances[player]):
|
|
||||||
excluded[region] = None
|
|
||||||
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 = {}
|
doors_to_connect = {}
|
||||||
all_regions = set()
|
all_regions = set()
|
||||||
@@ -143,7 +155,11 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|||||||
dungeon, hangers, hooks = gen_dungeon_info(name, builder.sectors, entrance_regions, all_regions, proposed_map,
|
dungeon, hangers, hooks = gen_dungeon_info(name, builder.sectors, entrance_regions, all_regions, proposed_map,
|
||||||
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(name, dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions,
|
if len(proposed_map) != len(doors_to_connect) and builder.name == 'Hyrule Castle Dungeon':
|
||||||
|
check_regions = all_regions.difference({world.get_region('Hyrule Castle Behind Tapestry', player)})
|
||||||
|
else:
|
||||||
|
check_regions = all_regions
|
||||||
|
valid = check_valid(name, dungeon, hangers, hooks, proposed_map, doors_to_connect, check_regions,
|
||||||
bk_needed, bk_special, paths, entrance_regions, world, player)
|
bk_needed, bk_special, paths, entrance_regions, world, player)
|
||||||
else:
|
else:
|
||||||
dungeon, hangers, hooks = dungeon_cache[depth]
|
dungeon, hangers, hooks = dungeon_cache[depth]
|
||||||
@@ -565,9 +581,15 @@ def determine_paths_for_dungeon(world, player, all_regions, name):
|
|||||||
non_hole_portals.append(portal.door.entrance.parent_region.name)
|
non_hole_portals.append(portal.door.entrance.parent_region.name)
|
||||||
if portal.destination:
|
if portal.destination:
|
||||||
paths.append(portal.door.entrance.parent_region.name)
|
paths.append(portal.door.entrance.parent_region.name)
|
||||||
if world.mode[player] == 'standard' and name == 'Hyrule Castle':
|
if world.mode[player] == 'standard':
|
||||||
paths.append('Hyrule Dungeon Cellblock')
|
if name == 'Hyrule Castle':
|
||||||
paths.append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
paths.append('Hyrule Dungeon Cellblock')
|
||||||
|
paths.append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
||||||
|
if name == 'Hyrule Castle Sewers':
|
||||||
|
paths.append('Sanctuary')
|
||||||
|
if name == 'Hyrule Castle Dungeon':
|
||||||
|
paths.append('Hyrule Dungeon Cellblock')
|
||||||
|
paths.append(('Hyrule Dungeon Cellblock', 'Hyrule Castle Throne Room'))
|
||||||
if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town':
|
if world.doorShuffle[player] in ['basic'] and name == 'Thieves Town':
|
||||||
paths.append('Thieves Attic Window')
|
paths.append('Thieves Attic Window')
|
||||||
elif 'Thieves Attic Window' in all_r_names:
|
elif 'Thieves Attic Window' in all_r_names:
|
||||||
@@ -1206,6 +1228,11 @@ class DungeonBuilder(object):
|
|||||||
self.split_dungeon_map = None
|
self.split_dungeon_map = None
|
||||||
self.exception_list = []
|
self.exception_list = []
|
||||||
|
|
||||||
|
self.throne_door = None
|
||||||
|
self.throne_sector = None
|
||||||
|
self.chosen_lobby = None
|
||||||
|
self.sewers_access = None
|
||||||
|
|
||||||
def polarity_complement(self):
|
def polarity_complement(self):
|
||||||
pol = Polarity()
|
pol = Polarity()
|
||||||
for sector in self.sectors:
|
for sector in self.sectors:
|
||||||
@@ -1258,7 +1285,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
|||||||
for r_name in dungeon_boss_sectors[key]:
|
for r_name in dungeon_boss_sectors[key]:
|
||||||
assign_sector(find_sector(r_name, candidate_sectors), current_dungeon, candidate_sectors, global_pole)
|
assign_sector(find_sector(r_name, candidate_sectors), current_dungeon, candidate_sectors, global_pole)
|
||||||
if key == 'Hyrule Castle' and world.mode[player] == 'standard':
|
if key == 'Hyrule Castle' and world.mode[player] == 'standard':
|
||||||
for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary']: # need to deliver zelda
|
for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary', 'Hyrule Castle Throne Room']: # need to deliver zelda
|
||||||
assign_sector(find_sector(r_name, candidate_sectors), current_dungeon,
|
assign_sector(find_sector(r_name, candidate_sectors), current_dungeon,
|
||||||
candidate_sectors, global_pole)
|
candidate_sectors, global_pole)
|
||||||
if key == 'Thieves Town' and world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind':
|
if key == 'Thieves Town' and world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind':
|
||||||
@@ -2077,16 +2104,18 @@ def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, builde
|
|||||||
while len(problem_builders) > 0:
|
while len(problem_builders) > 0:
|
||||||
for name, builder in problem_builders.items():
|
for name, builder in problem_builders.items():
|
||||||
candidates = find_branching_candidates(builder, neutral_choices, builder_info)
|
candidates = find_branching_candidates(builder, neutral_choices, builder_info)
|
||||||
valid, choice = False, None
|
valid, choice, package = False, None, None
|
||||||
while not valid:
|
while not valid:
|
||||||
if len(candidates) <= 0:
|
if len(candidates) <= 0:
|
||||||
raise GenerationException('Cross Dungeon Builder: Complex branch problems: %s' % name)
|
raise GenerationException('Cross Dungeon Builder: Complex branch problems: %s' % name)
|
||||||
choice = random.choice(candidates)
|
choice, package = random.choice(candidates)
|
||||||
candidates.remove(choice)
|
candidates.remove((choice, package))
|
||||||
valid = global_pole.is_valid_choice(dungeon_map, builder, choice) and valid_polarized_assignment(builder, choice)
|
valid = global_pole.is_valid_choice(dungeon_map, builder, choice) and valid_polarized_assignment(builder, choice)
|
||||||
neutral_choices.remove(choice)
|
neutral_choices.remove(choice)
|
||||||
for sector in choice:
|
for sector in choice:
|
||||||
assign_sector(sector, builder, polarized_sectors, global_pole)
|
assign_sector(sector, builder, polarized_sectors, global_pole)
|
||||||
|
if package:
|
||||||
|
builder.throne_door, builder.throne_sector, builder.chosen_lobby = package
|
||||||
builder.unfulfilled.clear()
|
builder.unfulfilled.clear()
|
||||||
problem_builders = identify_branching_issues(problem_builders, builder_info)
|
problem_builders = identify_branching_issues(problem_builders, builder_info)
|
||||||
|
|
||||||
@@ -2107,16 +2136,21 @@ def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, builde
|
|||||||
chosen_sectors = defaultdict(list)
|
chosen_sectors = defaultdict(list)
|
||||||
for i, choice in enumerate(choices):
|
for i, choice in enumerate(choices):
|
||||||
chosen_sectors[choice].extend(neutral_choices[i])
|
chosen_sectors[choice].extend(neutral_choices[i])
|
||||||
all_valid = True
|
all_valid, package_map = True, {}
|
||||||
for name, sector_list in chosen_sectors.items():
|
for name, sector_list in chosen_sectors.items():
|
||||||
if not valid_assignment(dungeon_map[name], sector_list, builder_info):
|
flag, package = valid_assignment(dungeon_map[name], sector_list, builder_info)
|
||||||
|
if not flag:
|
||||||
all_valid = False
|
all_valid = False
|
||||||
break
|
break
|
||||||
|
if package:
|
||||||
|
package_map[dungeon_map[name]] = package
|
||||||
if all_valid:
|
if all_valid:
|
||||||
for i, choice in enumerate(choices):
|
for i, choice in enumerate(choices):
|
||||||
builder = dungeon_map[choice]
|
builder = dungeon_map[choice]
|
||||||
for sector in neutral_choices[i]:
|
for sector in neutral_choices[i]:
|
||||||
assign_sector(sector, builder, polarized_sectors, global_pole)
|
assign_sector(sector, builder, polarized_sectors, global_pole)
|
||||||
|
if builder in package_map:
|
||||||
|
builder.throne_door, builder.throne_sector, builder.chosen_lobby = package_map[builder]
|
||||||
tries += 1
|
tries += 1
|
||||||
|
|
||||||
|
|
||||||
@@ -2629,9 +2663,9 @@ def weed_candidates(builder, candidates, best_charge):
|
|||||||
def find_branching_candidates(builder, neutral_choices, builder_info):
|
def find_branching_candidates(builder, neutral_choices, builder_info):
|
||||||
candidates = []
|
candidates = []
|
||||||
for choice in neutral_choices:
|
for choice in neutral_choices:
|
||||||
resolved, problem_list = check_for_valid_layout(builder, choice, builder_info)
|
resolved, problem_list, package = check_for_valid_layout(builder, choice, builder_info)
|
||||||
if resolved:
|
if resolved:
|
||||||
candidates.append(choice)
|
candidates.append((choice, package))
|
||||||
return candidates
|
return candidates
|
||||||
|
|
||||||
|
|
||||||
@@ -2786,13 +2820,13 @@ def categorize_groupings(sectors):
|
|||||||
|
|
||||||
def valid_assignment(builder, sector_list, builder_info):
|
def valid_assignment(builder, sector_list, builder_info):
|
||||||
if not valid_entrance(builder, sector_list, builder_info):
|
if not valid_entrance(builder, sector_list, builder_info):
|
||||||
return False
|
return False, None
|
||||||
if not valid_c_switch(builder, sector_list):
|
if not valid_c_switch(builder, sector_list):
|
||||||
return False
|
return False, None
|
||||||
if not valid_polarized_assignment(builder, sector_list):
|
if not valid_polarized_assignment(builder, sector_list):
|
||||||
return False
|
return False, None
|
||||||
resolved, problems = check_for_valid_layout(builder, sector_list, builder_info)
|
resolved, problems, package = check_for_valid_layout(builder, sector_list, builder_info)
|
||||||
return resolved
|
return resolved, package
|
||||||
|
|
||||||
|
|
||||||
def valid_entrance(builder, sector_list, builder_info):
|
def valid_entrance(builder, sector_list, builder_info):
|
||||||
@@ -2898,31 +2932,56 @@ def assign_the_rest(dungeon_map, neutral_sectors, global_pole, builder_info):
|
|||||||
chosen_sectors = defaultdict(list)
|
chosen_sectors = defaultdict(list)
|
||||||
for i, choice in enumerate(choices):
|
for i, choice in enumerate(choices):
|
||||||
chosen_sectors[choice].append(neutral_sector_list[i])
|
chosen_sectors[choice].append(neutral_sector_list[i])
|
||||||
all_valid = True
|
all_valid, package_map = True, {}
|
||||||
for name, sector_list in chosen_sectors.items():
|
for name, sector_list in chosen_sectors.items():
|
||||||
if not valid_assignment(dungeon_map[name], sector_list, builder_info):
|
flag, package = valid_assignment(dungeon_map[name], sector_list, builder_info)
|
||||||
|
if not flag:
|
||||||
all_valid = False
|
all_valid = False
|
||||||
break
|
break
|
||||||
|
if package:
|
||||||
|
package_map[dungeon_map[name]] = package
|
||||||
if all_valid:
|
if all_valid:
|
||||||
for name, sector_list in chosen_sectors.items():
|
for name, sector_list in chosen_sectors.items():
|
||||||
builder = dungeon_map[name]
|
builder = dungeon_map[name]
|
||||||
for sector in sector_list:
|
for sector in sector_list:
|
||||||
assign_sector(sector, builder, neutral_sectors, global_pole)
|
assign_sector(sector, builder, neutral_sectors, global_pole)
|
||||||
|
if builder in package_map:
|
||||||
|
builder.throne_door, builder.throne_sector, builder.chosen_lobby = package_map[builder]
|
||||||
tries += 1
|
tries += 1
|
||||||
|
|
||||||
|
|
||||||
def split_dungeon_builder(builder, split_list, builder_info):
|
def split_dungeon_builder(builder, split_list, builder_info):
|
||||||
|
ents, splits, c_tuple, world, player = builder_info
|
||||||
if builder.split_dungeon_map and len(builder.exception_list) == 0:
|
if builder.split_dungeon_map and len(builder.exception_list) == 0:
|
||||||
for name, proposal in builder.valid_proposal.items():
|
for name, proposal in builder.valid_proposal.items():
|
||||||
builder.split_dungeon_map[name].valid_proposal = proposal
|
builder.split_dungeon_map[name].valid_proposal = proposal
|
||||||
|
if builder.name == 'Hyrule Castle':
|
||||||
|
builder.chosen_lobby.outstanding_doors.remove(builder.throne_door)
|
||||||
|
builder.throne_sector.outstanding_doors.remove(world.get_door('Hyrule Castle Throne Room N', player))
|
||||||
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, comb_w_replace, merge_attempt, merge_limit = 0, None, 0, len(split_list) - 1
|
attempts, comb_w_replace, merge_attempt, merge_limit = 0, None, 0, len(split_list) - 1
|
||||||
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)
|
||||||
global_pole = GlobalPolarity(candidate_sectors)
|
if builder.name == 'Hyrule Castle':
|
||||||
|
throne_sector = find_sector('Hyrule Castle Throne Room', candidate_sectors)
|
||||||
|
chosen_lobbies = {r_name for x in split_list.values() for r_name in x}
|
||||||
|
choices = {}
|
||||||
|
for sector in candidate_sectors:
|
||||||
|
if sector.adj_outflow() > 1 and sector != throne_sector:
|
||||||
|
for door in sector.outstanding_doors:
|
||||||
|
if door.direction == Direction.South and door.entrance.parent_region not in chosen_lobbies:
|
||||||
|
choices[door] = sector
|
||||||
|
chosen_door = random.choice(list(choices.keys()))
|
||||||
|
split_list['Sewers'].append(chosen_door.entrance.parent_region.name)
|
||||||
|
choices[chosen_door].outstanding_doors.remove(chosen_door)
|
||||||
|
builder.throne_door = chosen_door
|
||||||
|
builder.throne_sector = throne_sector
|
||||||
|
builder.chosen_lobby = choices[chosen_door]
|
||||||
|
throne_sector.outstanding_doors.remove(world.get_door('Hyrule Castle Throne Room N', player))
|
||||||
|
|
||||||
|
global_pole = GlobalPolarity(candidate_sectors)
|
||||||
dungeon_map, sub_builder, merge_keys = {}, None, []
|
dungeon_map, sub_builder, merge_keys = {}, None, []
|
||||||
if merge_attempt > 0:
|
if merge_attempt > 0:
|
||||||
candidates = []
|
candidates = []
|
||||||
@@ -2932,7 +2991,6 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
continue
|
continue
|
||||||
elif len(split_entrances) <= 0:
|
elif len(split_entrances) <= 0:
|
||||||
continue
|
continue
|
||||||
ents, splits, c_tuple, world, player = builder_info
|
|
||||||
r_name = split_entrances[0]
|
r_name = split_entrances[0]
|
||||||
p = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == r_name), None)
|
p = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == r_name), None)
|
||||||
if p and not p.deadEnd:
|
if p and not p.deadEnd:
|
||||||
@@ -2953,6 +3011,13 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
sub_builder.all_entrances = list(split_entrances)
|
sub_builder.all_entrances = list(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)
|
||||||
|
if builder.name == 'Hyrule Castle':
|
||||||
|
assign_sector(find_sector('Hyrule Castle Throne Room', candidate_sectors),
|
||||||
|
dungeon_map['Hyrule Castle Dungeon'], candidate_sectors, global_pole)
|
||||||
|
assign_sector(find_sector('Hyrule Dungeon Cellblock', candidate_sectors),
|
||||||
|
dungeon_map['Hyrule Castle Dungeon'], candidate_sectors, global_pole)
|
||||||
|
dungeon_map['Hyrule Castle Dungeon'].throne_door = world.get_door('Hyrule Castle Throne Room N', player)
|
||||||
|
dungeon_map['Hyrule Castle Sewers'].sewers_access = builder.throne_door
|
||||||
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
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):
|
||||||
@@ -2960,7 +3025,13 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
attempts += 5 # all the combinations were tried already, no use repeating
|
attempts += 5 # all the combinations were tried already, no use repeating
|
||||||
else:
|
else:
|
||||||
attempts += 1
|
attempts += 1
|
||||||
if attempts >= 5 and merge_attempt < merge_limit:
|
if builder.throne_door:
|
||||||
|
previous = find_sector(builder.throne_door.entrance.parent_region.name, builder.sectors)
|
||||||
|
previous.outstanding_doors.append(builder.throne_door)
|
||||||
|
builder.throne_sector.outstanding_doors.append(world.get_door('Hyrule Castle Throne Room N', player))
|
||||||
|
split_list['Sewers'].remove(builder.throne_door.entrance.parent_region.name)
|
||||||
|
builder.throne_door = None
|
||||||
|
if attempts >= 5 and merge_attempt < merge_limit and builder.name != 'Hyrule Castle':
|
||||||
merge_attempt, attempts = merge_attempt + 1, 0
|
merge_attempt, attempts = merge_attempt + 1, 0
|
||||||
|
|
||||||
raise GenerationException('Unable to resolve in 5 attempts')
|
raise GenerationException('Unable to resolve in 5 attempts')
|
||||||
@@ -2981,16 +3052,21 @@ def balance_split(candidate_sectors, dungeon_map, global_pole, builder_info):
|
|||||||
chosen_sectors = defaultdict(list)
|
chosen_sectors = defaultdict(list)
|
||||||
for i, choice in enumerate(choices):
|
for i, choice in enumerate(choices):
|
||||||
chosen_sectors[choice].append(main_sector_list[i])
|
chosen_sectors[choice].append(main_sector_list[i])
|
||||||
all_valid = True
|
all_valid, package_map = True, {}
|
||||||
for name, builder in dungeon_map.items():
|
for name, builder in dungeon_map.items():
|
||||||
if not valid_assignment(builder, chosen_sectors[name], builder_info):
|
flag, package = valid_assignment(builder, chosen_sectors[name], builder_info)
|
||||||
|
if not flag:
|
||||||
all_valid = False
|
all_valid = False
|
||||||
break
|
break
|
||||||
|
if package:
|
||||||
|
package_map[builder] = package
|
||||||
if all_valid:
|
if all_valid:
|
||||||
for name, sector_list in chosen_sectors.items():
|
for name, sector_list in chosen_sectors.items():
|
||||||
builder = dungeon_map[name]
|
builder = dungeon_map[name]
|
||||||
for sector in sector_list:
|
for sector in sector_list:
|
||||||
assign_sector(sector, builder, candidate_sectors, global_pole)
|
assign_sector(sector, builder, candidate_sectors, global_pole)
|
||||||
|
if builder in package_map:
|
||||||
|
builder.throne_door, builder.throne_sector, builder.chosen_lobby = package_map[builder]
|
||||||
return dungeon_map
|
return dungeon_map
|
||||||
tries += 1
|
tries += 1
|
||||||
raise GenerationException('Split Dungeon Builder: Impossible dungeon. Ref %s' % next(iter(dungeon_map.keys())))
|
raise GenerationException('Split Dungeon Builder: Impossible dungeon. Ref %s' % next(iter(dungeon_map.keys())))
|
||||||
@@ -3380,7 +3456,7 @@ class DungeonAccess:
|
|||||||
def identify_branching_issues(dungeon_map, builder_info):
|
def identify_branching_issues(dungeon_map, builder_info):
|
||||||
unconnected_builders = {}
|
unconnected_builders = {}
|
||||||
for name, builder in dungeon_map.items():
|
for name, builder in dungeon_map.items():
|
||||||
resolved, unreached_doors = check_for_valid_layout(builder, [], builder_info)
|
resolved, unreached_doors, package = check_for_valid_layout(builder, [], builder_info)
|
||||||
if not resolved:
|
if not resolved:
|
||||||
unconnected_builders[name] = builder
|
unconnected_builders[name] = builder
|
||||||
for hook, door_list in unreached_doors.items():
|
for hook, door_list in unreached_doors.items():
|
||||||
@@ -3421,16 +3497,27 @@ def check_for_valid_layout(builder, sector_list, builder_info):
|
|||||||
proposal = generate_dungeon_find_proposal(split_build, entrance_regions, split, world, player)
|
proposal = generate_dungeon_find_proposal(split_build, entrance_regions, split, world, player)
|
||||||
# record split proposals
|
# record split proposals
|
||||||
builder.valid_proposal[name] = proposal
|
builder.valid_proposal[name] = proposal
|
||||||
|
package = None
|
||||||
|
if temp_builder.name == 'Hyrule Castle':
|
||||||
|
temp_builder.chosen_lobby.outstanding_doors.append(temp_builder.throne_door)
|
||||||
|
temp_builder.throne_sector.outstanding_doors.append(world.get_door('Hyrule Castle Throne Room N', player))
|
||||||
|
package = temp_builder.throne_door, temp_builder.throne_sector, temp_builder.chosen_lobby
|
||||||
|
split_list['Sewers'].remove(temp_builder.throne_door.entrance.parent_region.name)
|
||||||
builder.exception_list = list(sector_list)
|
builder.exception_list = list(sector_list)
|
||||||
return True, {}
|
return True, {}, package
|
||||||
except (GenerationException, NeutralizingException):
|
except (GenerationException, NeutralizingException):
|
||||||
builder.split_dungeon_map = None
|
builder.split_dungeon_map = None
|
||||||
builder.valid_proposal = None
|
builder.valid_proposal = None
|
||||||
|
if temp_builder.name == 'Hyrule Castle' and temp_builder.throne_door:
|
||||||
|
temp_builder.chosen_lobby.outstanding_doors.append(temp_builder.throne_door)
|
||||||
|
temp_builder.throne_sector.outstanding_doors.append(world.get_door('Hyrule Castle Throne Room N', player))
|
||||||
|
old_entrance = temp_builder.throne_door.entrance.parent_region.name
|
||||||
|
split_dungeon_entrances[builder.name]['Sewers'].remove(old_entrance)
|
||||||
unreached_doors = resolve_equations(builder, sector_list)
|
unreached_doors = resolve_equations(builder, sector_list)
|
||||||
return False, unreached_doors
|
return False, unreached_doors, None
|
||||||
else:
|
else:
|
||||||
unreached_doors = resolve_equations(builder, sector_list)
|
unreached_doors = resolve_equations(builder, sector_list)
|
||||||
return len(unreached_doors) == 0, unreached_doors
|
return len(unreached_doors) == 0, unreached_doors, None
|
||||||
|
|
||||||
|
|
||||||
def find_independent_entrances(entrance_regions, world, player):
|
def find_independent_entrances(entrance_regions, world, player):
|
||||||
@@ -3831,9 +3918,7 @@ def find_free_equation(equations):
|
|||||||
def copy_door_equations(builder, sector_list):
|
def copy_door_equations(builder, sector_list):
|
||||||
equations = {}
|
equations = {}
|
||||||
for sector in builder.sectors + sector_list:
|
for sector in builder.sectors + sector_list:
|
||||||
if sector.equations is None:
|
sector.equations = calc_sector_equations(sector)
|
||||||
# todo: sort equations?
|
|
||||||
sector.equations = calc_sector_equations(sector)
|
|
||||||
curr_list = equations[sector] = []
|
curr_list = equations[sector] = []
|
||||||
for equation in sector.equations:
|
for equation in sector.equations:
|
||||||
curr_list.append(equation.copy())
|
curr_list.append(equation.copy())
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -31,7 +31,7 @@ from Utils import output_path, parse_player_names
|
|||||||
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config
|
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config
|
||||||
from source.tools.BPS import create_bps_from_data
|
from source.tools.BPS import create_bps_from_data
|
||||||
|
|
||||||
__version__ = '1.0.2.4-v'
|
__version__ = '1.0.2.4-x'
|
||||||
|
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
|
|
||||||
|
|||||||
@@ -155,8 +155,16 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
|
|||||||
|
|
||||||
## Notes and Bug Fixes
|
## Notes and Bug Fixes
|
||||||
|
|
||||||
|
#### StandardThrone
|
||||||
|
|
||||||
|
* Changed standard dungeon generation to always have Throne Room in hyrule castle and always have sanctuary behind it
|
||||||
|
* S&Q/death in standard after moving the tapestry but before delivering Zelda will result in spawning at the tapestry
|
||||||
|
* Mirror scroll will return you to Zelda's cell instead of last entrance. This reverts to normal behavior once the tapestry open trigger is reach
|
||||||
|
|
||||||
#### Volatile
|
#### Volatile
|
||||||
|
|
||||||
|
* 1.0.2.5
|
||||||
|
* Some textual changes for hints
|
||||||
* 1.0.2.4
|
* 1.0.2.4
|
||||||
* Updated tourney winners (included Doors Async League winners)
|
* Updated tourney winners (included Doors Async League winners)
|
||||||
* Fixed a couple issues with dungeon counters and the DungeonCompletion field for autotracking
|
* Fixed a couple issues with dungeon counters and the DungeonCompletion field for autotracking
|
||||||
|
|||||||
2
Rom.py
2
Rom.py
@@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '01166fb16b38b49ef79acc9993dc4f02'
|
RANDOMIZERBASEHASH = '4941faf4496875a373a51119f2bedf4c'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user