Generation improvements
Bomb/dash doors removed from experimental
This commit is contained in:
125
DoorShuffle.py
125
DoorShuffle.py
@@ -60,9 +60,12 @@ def link_doors(world, player):
|
|||||||
if world.mode[player] == 'standard':
|
if world.mode[player] == 'standard':
|
||||||
world.get_portal('Sanctuary', player).destination = True
|
world.get_portal('Sanctuary', player).destination = True
|
||||||
world.get_portal('Desert East', player).destination = True
|
world.get_portal('Desert East', player).destination = True
|
||||||
world.get_portal('Skull 2 West', player).destination = True
|
if world.mode[player] == 'inverted':
|
||||||
world.get_portal('Turtle Rock Lazy Eyes', player).destination = True
|
world.get_portal('Desert West', player).destination = True
|
||||||
world.get_portal('Turtle Rock Eye Bridge', player).destination = True
|
if world.mode[player] == 'open':
|
||||||
|
world.get_portal('Skull 2 West', player).destination = True
|
||||||
|
world.get_portal('Turtle Rock Lazy Eyes', player).destination = True
|
||||||
|
world.get_portal('Turtle Rock Eye Bridge', player).destination = True
|
||||||
else:
|
else:
|
||||||
analyze_portals(world, player)
|
analyze_portals(world, player)
|
||||||
for portal in world.dungeon_portals[player]:
|
for portal in world.dungeon_portals[player]:
|
||||||
@@ -638,7 +641,7 @@ def within_dungeon(world, player):
|
|||||||
dungeon_builders[key].entrance_list = list(entrances_map[key])
|
dungeon_builders[key].entrance_list = list(entrances_map[key])
|
||||||
recombinant_builders = {}
|
recombinant_builders = {}
|
||||||
entrances, splits = create_dungeon_entrances(world, player)
|
entrances, splits = create_dungeon_entrances(world, player)
|
||||||
builder_info = entrances, splits, world, player
|
builder_info = entrances, splits, connections_tuple, world, player
|
||||||
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info)
|
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info)
|
||||||
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
||||||
|
|
||||||
@@ -662,12 +665,12 @@ def within_dungeon(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info):
|
def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info):
|
||||||
dungeon_entrances, split_dungeon_entrances, world, player = builder_info
|
dungeon_entrances, split_dungeon_entrances, c_tuple, world, player = builder_info
|
||||||
if dungeon_entrances is None:
|
if dungeon_entrances is None:
|
||||||
dungeon_entrances = default_dungeon_entrances
|
dungeon_entrances = default_dungeon_entrances
|
||||||
if split_dungeon_entrances is None:
|
if split_dungeon_entrances is None:
|
||||||
split_dungeon_entrances = split_region_starts
|
split_dungeon_entrances = split_region_starts
|
||||||
builder_info = dungeon_entrances, split_dungeon_entrances, world, player
|
builder_info = dungeon_entrances, split_dungeon_entrances, c_tuple, world, player
|
||||||
|
|
||||||
for name, split_list in split_dungeon_entrances.items():
|
for name, split_list in split_dungeon_entrances.items():
|
||||||
builder = dungeon_builders.pop(name)
|
builder = dungeon_builders.pop(name)
|
||||||
@@ -886,7 +889,7 @@ def cross_dungeon(world, player):
|
|||||||
key_name = dungeon_keys[builder.name] if loc.name != 'Hyrule Castle - Big Key Drop' else dungeon_bigs[builder.name]
|
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)
|
loc.forced_item = loc.item = ItemFactory(key_name, player)
|
||||||
recombinant_builders = {}
|
recombinant_builders = {}
|
||||||
builder_info = entrances, splits, world, player
|
builder_info = entrances, splits, connections_tuple, world, player
|
||||||
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info)
|
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info)
|
||||||
|
|
||||||
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
||||||
@@ -1609,6 +1612,7 @@ def change_door_to_small_key(d, world, player):
|
|||||||
def smooth_door_pairs(world, player):
|
def smooth_door_pairs(world, player):
|
||||||
all_doors = [x for x in world.doors if x.player == player]
|
all_doors = [x for x in world.doors if x.player == player]
|
||||||
skip = set()
|
skip = set()
|
||||||
|
bd_candidates, dashable_counts, bombable_counts = defaultdict(list), defaultdict(int), defaultdict(int)
|
||||||
for door in all_doors:
|
for door in all_doors:
|
||||||
if door.type in [DoorType.Normal, DoorType.Interior] and door not in skip and not door.entranceFlag:
|
if door.type in [DoorType.Normal, DoorType.Interior] and door not in skip and not door.entranceFlag:
|
||||||
partner = door.dest
|
partner = door.dest
|
||||||
@@ -1636,19 +1640,18 @@ def smooth_door_pairs(world, player):
|
|||||||
remove_pair(door, world, player)
|
remove_pair(door, world, player)
|
||||||
elif type_a in [DoorKind.Bombable, DoorKind.Dashable] or type_b in [DoorKind.Bombable, DoorKind.Dashable]:
|
elif type_a in [DoorKind.Bombable, DoorKind.Dashable] or type_b in [DoorKind.Bombable, DoorKind.Dashable]:
|
||||||
if valid_pair:
|
if valid_pair:
|
||||||
if type_a == type_b:
|
new_type = type_a
|
||||||
add_pair(door, partner, world, player)
|
if type_a != type_b:
|
||||||
spoiler_type = 'Bomb Door' if type_a == DoorKind.Bombable else 'Dash Door'
|
|
||||||
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
|
|
||||||
else:
|
|
||||||
new_type = DoorKind.Dashable if type_a == DoorKind.Dashable or type_b == DoorKind.Dashable else DoorKind.Bombable
|
new_type = DoorKind.Dashable if type_a == DoorKind.Dashable or type_b == DoorKind.Dashable else DoorKind.Bombable
|
||||||
if type_a != new_type:
|
if type_a != new_type:
|
||||||
room_a.change(door.doorListPos, new_type)
|
room_a.change(door.doorListPos, new_type)
|
||||||
if type_b != new_type:
|
if type_b != new_type:
|
||||||
room_b.change(partner.doorListPos, new_type)
|
room_b.change(partner.doorListPos, new_type)
|
||||||
add_pair(door, partner, world, player)
|
add_pair(door, partner, world, player)
|
||||||
spoiler_type = 'Bomb Door' if new_type == DoorKind.Bombable else 'Dash Door'
|
spoiler_type = 'Bomb Door' if new_type == DoorKind.Bombable else 'Dash Door'
|
||||||
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
|
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
|
||||||
|
counter = bombable_counts if new_type == DoorKind.Bombable else dashable_counts
|
||||||
|
counter[door.entrance.parent_region.dungeon] += 1
|
||||||
else:
|
else:
|
||||||
if type_a in [DoorKind.Bombable, DoorKind.Dashable]:
|
if type_a in [DoorKind.Bombable, DoorKind.Dashable]:
|
||||||
room_a.change(door.doorListPos, DoorKind.Normal)
|
room_a.change(door.doorListPos, DoorKind.Normal)
|
||||||
@@ -1656,8 +1659,9 @@ def smooth_door_pairs(world, player):
|
|||||||
elif type_b in [DoorKind.Bombable, DoorKind.Dashable]:
|
elif type_b in [DoorKind.Bombable, DoorKind.Dashable]:
|
||||||
room_b.change(partner.doorListPos, DoorKind.Normal)
|
room_b.change(partner.doorListPos, DoorKind.Normal)
|
||||||
remove_pair(partner, world, player)
|
remove_pair(partner, world, player)
|
||||||
elif world.experimental[player] and valid_pair and type_a != DoorKind.SmallKey and type_b != DoorKind.SmallKey:
|
elif valid_pair and type_a != DoorKind.SmallKey and type_b != DoorKind.SmallKey:
|
||||||
random_door_type(door, partner, world, player, type_a, type_b, room_a, room_b)
|
bd_candidates[door.entrance.parent_region.dungeon].append(door)
|
||||||
|
shuffle_bombable_dashable(bd_candidates, bombable_counts, dashable_counts, world, player)
|
||||||
world.paired_doors[player] = [x for x in world.paired_doors[player] if x.pair or x.original]
|
world.paired_doors[player] = [x for x in world.paired_doors[player] if x.pair or x.original]
|
||||||
|
|
||||||
|
|
||||||
@@ -1694,17 +1698,61 @@ def stateful_door(door, kind):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def random_door_type(door, partner, world, player, type_a, type_b, room_a, room_b):
|
def shuffle_bombable_dashable(bd_candidates, bombable_counts, dashable_counts, world, player):
|
||||||
r_kind = random.choices([DoorKind.Normal, DoorKind.Bombable, DoorKind.Dashable], [15, 4, 6], k=1)[0]
|
if world.doorShuffle[player] == 'basic':
|
||||||
if r_kind != DoorKind.Normal:
|
for dungeon, candidates in bd_candidates.items():
|
||||||
if door.type == DoorType.Normal:
|
diff = bomb_dash_counts[dungeon.name][1] - dashable_counts[dungeon]
|
||||||
add_pair(door, partner, world, player)
|
if diff > 0:
|
||||||
if type_a != r_kind:
|
for chosen in random.sample(candidates, min(diff, len(candidates))):
|
||||||
room_a.change(door.doorListPos, r_kind)
|
change_pair_type(chosen, DoorKind.Dashable, world, player)
|
||||||
if type_b != r_kind:
|
candidates.remove(chosen)
|
||||||
room_b.change(partner.doorListPos, r_kind)
|
diff = bomb_dash_counts[dungeon.name][0] - bombable_counts[dungeon]
|
||||||
spoiler_type = 'Bomb Door' if r_kind == DoorKind.Bombable else 'Dash Door'
|
if diff > 0:
|
||||||
world.spoiler.set_door_type(door.name + ' <-> ' + partner.name, spoiler_type, player)
|
for chosen in random.sample(candidates, min(diff, len(candidates))):
|
||||||
|
change_pair_type(chosen, DoorKind.Bombable, world, player)
|
||||||
|
candidates.remove(chosen)
|
||||||
|
for excluded in candidates:
|
||||||
|
remove_pair_type_if_present(excluded, world, player)
|
||||||
|
elif world.doorShuffle[player] == 'crossed':
|
||||||
|
all_candidates = sum(bd_candidates.values(), [])
|
||||||
|
all_bomb_counts = sum(bombable_counts.values())
|
||||||
|
all_dash_counts = sum(dashable_counts.values())
|
||||||
|
if all_dash_counts < 8:
|
||||||
|
for chosen in random.sample(all_candidates, min(8 - all_dash_counts, len(all_candidates))):
|
||||||
|
change_pair_type(chosen, DoorKind.Dashable, world, player)
|
||||||
|
world.spoiler.set_door_type(chosen.name + ' <-> ' + chosen.dest.name, DoorKind.Dashable, player)
|
||||||
|
all_candidates.remove(chosen)
|
||||||
|
if all_bomb_counts < 12:
|
||||||
|
for chosen in random.sample(all_candidates, min(12 - all_bomb_counts, len(all_candidates))):
|
||||||
|
change_pair_type(chosen, DoorKind.Bombable, world, player)
|
||||||
|
world.spoiler.set_door_type(chosen.name + ' <-> ' + chosen.dest.name, DoorKind.Bombable, player)
|
||||||
|
all_candidates.remove(chosen)
|
||||||
|
for excluded in all_candidates:
|
||||||
|
remove_pair_type_if_present(excluded, world, player)
|
||||||
|
|
||||||
|
|
||||||
|
def change_pair_type(door, new_type, world, player):
|
||||||
|
room_a = world.get_room(door.roomIndex, player)
|
||||||
|
room_a.change(door.doorListPos, new_type)
|
||||||
|
if door.type != DoorType.Interior:
|
||||||
|
room_b = world.get_room(door.dest.roomIndex, player)
|
||||||
|
room_b.change(door.dest.doorListPos, new_type)
|
||||||
|
add_pair(door, door.dest, world, player)
|
||||||
|
spoiler_type = 'Bomb Door' if new_type == DoorKind.Bombable else 'Dash Door'
|
||||||
|
world.spoiler.set_door_type(door.name + ' <-> ' + door.dest.name, spoiler_type, player)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_pair_type_if_present(door, world, player):
|
||||||
|
room_a = world.get_room(door.roomIndex, player)
|
||||||
|
if room_a.kind(door) in [DoorKind.Bombable, DoorKind.Dashable]:
|
||||||
|
room_a.change(door.doorListPos, DoorKind.Normal)
|
||||||
|
if door.type != DoorType.Interior:
|
||||||
|
remove_pair(door, world, player)
|
||||||
|
if door.type != DoorType.Interior:
|
||||||
|
room_b = world.get_room(door.dest.roomIndex, player)
|
||||||
|
if room_b.kind(door.dest) in [DoorKind.Bombable, DoorKind.Dashable]:
|
||||||
|
room_b.change(door.dest.doorListPos, DoorKind.Normal)
|
||||||
|
remove_pair(door.dest, world, player)
|
||||||
|
|
||||||
|
|
||||||
def find_inaccessible_regions(world, player):
|
def find_inaccessible_regions(world, player):
|
||||||
@@ -1727,8 +1775,9 @@ def find_inaccessible_regions(world, player):
|
|||||||
queue.append(parent)
|
queue.append(parent)
|
||||||
for ext in next_region.exits:
|
for ext in next_region.exits:
|
||||||
connect = ext.connected_region
|
connect = ext.connected_region
|
||||||
if connect and connect.type is not RegionType.Dungeon and connect not in queue and connect not in visited_regions:
|
if connect and connect not in queue and connect not in visited_regions:
|
||||||
queue.append(connect)
|
if connect.type is not RegionType.Dungeon or connect.name.endswith(' Portal'):
|
||||||
|
queue.append(connect)
|
||||||
world.inaccessible_regions[player].extend([r.name for r in all_regions.difference(visited_regions) if valid_inaccessible_region(r)])
|
world.inaccessible_regions[player].extend([r.name for r in all_regions.difference(visited_regions) if valid_inaccessible_region(r)])
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
logger.debug('Inaccessible Regions:')
|
logger.debug('Inaccessible Regions:')
|
||||||
@@ -2798,4 +2847,20 @@ split_portal_defaults = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bomb_dash_counts = {
|
||||||
|
'Hyrule Castle': (0, 2),
|
||||||
|
'Eastern Palace': (0, 0),
|
||||||
|
'Desert Palace': (0, 0),
|
||||||
|
'Agahnims Tower': (0, 0),
|
||||||
|
'Swamp Palace': (2, 0),
|
||||||
|
'Palace of Darkness': (3, 2),
|
||||||
|
'Misery Mire': (2, 0),
|
||||||
|
'Skull Woods': (2, 0),
|
||||||
|
'Ice Palace': (0, 0),
|
||||||
|
'Tower of Hera': (0, 0),
|
||||||
|
'Thieves Town': (1, 1),
|
||||||
|
'Turtle Rock': (0, 2), # 2 bombs kind of for entrances
|
||||||
|
'Ganons Tower': (2, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, all_regions, pro
|
|||||||
o_state_cache = {}
|
o_state_cache = {}
|
||||||
for sector in available_sectors:
|
for sector in available_sectors:
|
||||||
for door in sector.outstanding_doors:
|
for door in sector.outstanding_doors:
|
||||||
if not door.stonewall and door not in proposed_map.keys():
|
if door not in proposed_map.keys():
|
||||||
hanger_set.add(door)
|
hanger_set.add(door)
|
||||||
bk_flag = group_flags[door_map[door]]
|
bk_flag = group_flags[door_map[door]]
|
||||||
parent = door.entrance.parent_region
|
parent = door.entrance.parent_region
|
||||||
@@ -1271,7 +1271,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
|||||||
identify_destination_sectors(accessible_sectors, reverse_d_map, dungeon_map, connections,
|
identify_destination_sectors(accessible_sectors, reverse_d_map, dungeon_map, connections,
|
||||||
dungeon_entrances, split_dungeon_entrances)
|
dungeon_entrances, split_dungeon_entrances)
|
||||||
for name, builder in dungeon_map.items():
|
for name, builder in dungeon_map.items():
|
||||||
calc_allowance_and_dead_ends(builder, connections_tuple, world.dungeon_portals[player])
|
calc_allowance_and_dead_ends(builder, connections_tuple, world, player)
|
||||||
|
|
||||||
if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']:
|
if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']:
|
||||||
sanc = find_sector('Sanctuary', candidate_sectors)
|
sanc = find_sector('Sanctuary', candidate_sectors)
|
||||||
@@ -1318,7 +1318,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
|||||||
# restart
|
# restart
|
||||||
raise NeutralizingException('Either free location/crystal assignment is already globally invalid')
|
raise NeutralizingException('Either free location/crystal assignment is already globally invalid')
|
||||||
logger.info(world.fish.translate("cli", "cli", "balance.doors"))
|
logger.info(world.fish.translate("cli", "cli", "balance.doors"))
|
||||||
builder_info = dungeon_entrances, split_dungeon_entrances, world, player
|
builder_info = dungeon_entrances, split_dungeon_entrances, connections_tuple, world, player
|
||||||
assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, builder_info)
|
assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, builder_info)
|
||||||
# the rest
|
# the rest
|
||||||
assign_the_rest(dungeon_map, neutral_sectors, global_pole, builder_info)
|
assign_the_rest(dungeon_map, neutral_sectors, global_pole, builder_info)
|
||||||
@@ -1376,26 +1376,31 @@ def identify_destination_sectors(accessible_sectors, reverse_d_map, dungeon_map,
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def calc_allowance_and_dead_ends(builder, connections_tuple, portals):
|
# todo: split version that adds allowance for potential entrances
|
||||||
|
def calc_allowance_and_dead_ends(builder, connections_tuple, world, player):
|
||||||
|
portals = world.dungeon_portals[player]
|
||||||
entrances_map, potentials, connections = connections_tuple
|
entrances_map, potentials, connections = connections_tuple
|
||||||
needed_connections = [x for x in builder.all_entrances if x not in entrances_map[builder.name]]
|
name = builder.name if not builder.split_flag else builder.name.rsplit(' ', 1)[0]
|
||||||
|
needed_connections = [x for x in builder.all_entrances if x not in entrances_map[name]]
|
||||||
starting_allowance = 0
|
starting_allowance = 0
|
||||||
used_sectors = set()
|
used_sectors = set()
|
||||||
destination_entrances = [x.door.entrance.parent_region.name for x in portals if x.destination]
|
destination_entrances = [x.door.entrance.parent_region.name for x in portals if x.destination]
|
||||||
for entrance in entrances_map[builder.name]:
|
dead_ends = [x.door.entrance.parent_region.name for x in portals if x.deadEnd]
|
||||||
|
for entrance in entrances_map[name]:
|
||||||
sector = find_sector(entrance, builder.sectors)
|
sector = find_sector(entrance, builder.sectors)
|
||||||
outflow_target = 0 if entrance not in drop_entrances_allowance else 1
|
if sector:
|
||||||
if sector not in used_sectors and sector.adj_outflow() > outflow_target:
|
outflow_target = 0 if entrance not in drop_entrances_allowance else 1
|
||||||
if entrance not in destination_entrances:
|
if sector not in used_sectors and (sector.adj_outflow() > outflow_target or entrance in dead_ends):
|
||||||
starting_allowance += 1
|
if entrance not in destination_entrances:
|
||||||
else:
|
starting_allowance += 1
|
||||||
builder.branches -= 1
|
else:
|
||||||
used_sectors.add(sector)
|
builder.branches -= 1
|
||||||
elif sector not in used_sectors:
|
used_sectors.add(sector)
|
||||||
if entrance in destination_entrances and sector.branches() > 0:
|
elif sector not in used_sectors:
|
||||||
builder.branches -= 1
|
if entrance in destination_entrances and sector.branches() > 0:
|
||||||
if entrance not in drop_entrances_allowance:
|
builder.branches -= 1
|
||||||
needed_connections.append(entrance)
|
if entrance not in drop_entrances_allowance:
|
||||||
|
needed_connections.append(entrance)
|
||||||
builder.allowance = starting_allowance
|
builder.allowance = starting_allowance
|
||||||
for entrance in needed_connections:
|
for entrance in needed_connections:
|
||||||
sector = find_sector(entrance, builder.sectors)
|
sector = find_sector(entrance, builder.sectors)
|
||||||
@@ -1404,7 +1409,11 @@ def calc_allowance_and_dead_ends(builder, connections_tuple, portals):
|
|||||||
connect_able = False
|
connect_able = False
|
||||||
if entrance in connections.keys():
|
if entrance in connections.keys():
|
||||||
enabling_region = connections[entrance]
|
enabling_region = connections[entrance]
|
||||||
connecting_entrances = [x for x in potentials[enabling_region] if x != entrance and x not in dead_entrances and x not in drop_entrances_allowance]
|
check_list = list(potentials[enabling_region])
|
||||||
|
if enabling_region.name in ['Desert Ledge', 'Desert Palace Entrance (North) Spot']:
|
||||||
|
alternate = 'Desert Palace Entrance (North) Spot' if enabling_region.name == 'Desert Ledge' else 'Desert Ledge'
|
||||||
|
check_list.extend(potentials[world.get_region(alternate, player)])
|
||||||
|
connecting_entrances = [x for x in check_list if x != entrance and x not in dead_entrances and x not in drop_entrances_allowance]
|
||||||
connect_able = len(connecting_entrances) > 0
|
connect_able = len(connecting_entrances) > 0
|
||||||
if is_destination and sector.branches() == 0: #
|
if is_destination and sector.branches() == 0: #
|
||||||
builder.dead_ends += 1
|
builder.dead_ends += 1
|
||||||
@@ -2633,7 +2642,7 @@ def valid_entrance(builder, sector_list, builder_info):
|
|||||||
if len(builder.sectors) == 0:
|
if len(builder.sectors) == 0:
|
||||||
is_dead_end = True
|
is_dead_end = True
|
||||||
else:
|
else:
|
||||||
entrances, splits, world, player = builder_info
|
entrances, splits, c_tuple, world, player = builder_info
|
||||||
if builder.name not in entrances.keys():
|
if builder.name not in entrances.keys():
|
||||||
name_parts = builder.name.rsplit(' ', 1)
|
name_parts = builder.name.rsplit(' ', 1)
|
||||||
entrance_list = splits[name_parts[0]][name_parts[1]]
|
entrance_list = splits[name_parts[0]][name_parts[1]]
|
||||||
@@ -2765,7 +2774,7 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
continue
|
continue
|
||||||
elif len(split_entrances) <= 0:
|
elif len(split_entrances) <= 0:
|
||||||
continue
|
continue
|
||||||
x, y, world, player = builder_info
|
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)
|
p = next(x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == r_name)
|
||||||
if not p.deadEnd:
|
if not p.deadEnd:
|
||||||
@@ -2782,6 +2791,7 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
sub_builder.all_entrances.extend(split_entrances)
|
sub_builder.all_entrances.extend(split_entrances)
|
||||||
if key not in dungeon_map:
|
if key not in dungeon_map:
|
||||||
dungeon_map[key] = sub_builder = DungeonBuilder(key)
|
dungeon_map[key] = sub_builder = DungeonBuilder(key)
|
||||||
|
sub_builder.split_flag = True
|
||||||
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)
|
||||||
@@ -2799,6 +2809,9 @@ def split_dungeon_builder(builder, split_list, builder_info):
|
|||||||
|
|
||||||
|
|
||||||
def balance_split(candidate_sectors, dungeon_map, global_pole, builder_info):
|
def balance_split(candidate_sectors, dungeon_map, global_pole, builder_info):
|
||||||
|
dungeon_entrances, split_dungeon_entrances, connections_tuple, world, player = builder_info
|
||||||
|
for name, builder in dungeon_map.items():
|
||||||
|
calc_allowance_and_dead_ends(builder, connections_tuple, world, player)
|
||||||
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
comb_w_replace = len(dungeon_map) ** len(candidate_sectors)
|
||||||
if comb_w_replace <= 10000:
|
if comb_w_replace <= 10000:
|
||||||
combinations = list(itertools.product(dungeon_map.keys(), repeat=len(candidate_sectors)))
|
combinations = list(itertools.product(dungeon_map.keys(), repeat=len(candidate_sectors)))
|
||||||
@@ -3217,7 +3230,7 @@ def identify_branching_issues(dungeon_map, builder_info):
|
|||||||
|
|
||||||
|
|
||||||
def check_for_valid_layout(builder, sector_list, builder_info):
|
def check_for_valid_layout(builder, sector_list, builder_info):
|
||||||
dungeon_entrances, split_dungeon_entrances, world, player = builder_info
|
dungeon_entrances, split_dungeon_entrances, c_tuple, world, player = builder_info
|
||||||
if builder.name in split_dungeon_entrances.keys():
|
if builder.name in split_dungeon_entrances.keys():
|
||||||
try:
|
try:
|
||||||
temp_builder = DungeonBuilder(builder.name)
|
temp_builder = DungeonBuilder(builder.name)
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -25,7 +25,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
|
|||||||
from ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
|
from ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
__version__ = '0.2.0.17u'
|
__version__ = '0.2.0.18u'
|
||||||
|
|
||||||
class EnemizerError(RuntimeError):
|
class EnemizerError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -83,15 +83,19 @@ testing to verify logic is all good.
|
|||||||
|
|
||||||
### Experimental features
|
### Experimental features
|
||||||
|
|
||||||
* Only the random bomb doors and the item counter are currently experimental
|
* Only the item counter is currently experimental
|
||||||
* Item counter is suppressed in Triforce Hunt
|
* Item counter is suppressed in Triforce Hunt
|
||||||
|
|
||||||
|
|
||||||
#### Temporary debug features
|
#### Temporary debug features
|
||||||
|
|
||||||
* Removed the red square in the upper right corner of the hud if the castle gate is closed
|
* Removed the red square in the upper right corner of the hud if the castle gate is closed
|
||||||
|
|
||||||
# Bug Fixes
|
# Bug Fixes
|
||||||
|
|
||||||
|
* 2.0.18u
|
||||||
|
* Generation improvements
|
||||||
|
* Bombs/Dash doors more consistent with the amount in vanilla.
|
||||||
* 2.0.17u
|
* 2.0.17u
|
||||||
* Generation improvements
|
* Generation improvements
|
||||||
* 2.0.16u
|
* 2.0.16u
|
||||||
|
|||||||
46
Rom.py
46
Rom.py
@@ -1375,38 +1375,42 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
||||||
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
||||||
|
|
||||||
rom.write_byte(0x18004E, 0) # Escape Fill (nothing)
|
rom.write_byte(0x18004E, 0) # Escape Fill (nothing)
|
||||||
write_int16(rom, 0x180183, 300) # Escape fill rupee bow
|
write_int16(rom, 0x180183, 300) # Escape fill rupee bow
|
||||||
rom.write_bytes(0x180185, [0,0,0]) # Uncle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180185, [0, 0, 0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x180188, [0,0,0]) # Zelda respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180188, [0, 0, 0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x18018B, [0,0,0]) # Mantle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x18018B, [0, 0, 0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||||
bow_max, bomb_max, magic_max = 0, 0, 0
|
bow_max, bomb_max, magic_max = 0, 0, 0
|
||||||
|
bow_small, magic_small = 0, 0
|
||||||
if world.mode[player] == 'standard':
|
if world.mode[player] == 'standard':
|
||||||
if uncle_location.item is not None and uncle_location.item.name in ['Bow', 'Progressive Bow']:
|
if uncle_location.item is not None and uncle_location.item.name in ['Bow', 'Progressive Bow']:
|
||||||
rom.write_byte(0x18004E, 1) # Escape Fill (arrows)
|
rom.write_byte(0x18004E, 1) # Escape Fill (arrows)
|
||||||
write_int16(rom, 0x180183, 300) # Escape fill rupee bow
|
write_int16(rom, 0x180183, 300) # Escape fill rupee bow
|
||||||
rom.write_bytes(0x180185, [0,0,70]) # Uncle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180185, [0, 0, 70]) # Uncle respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x180188, [0,0,10]) # Zelda respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180188, [0, 0, 10]) # Zelda respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x18018B, [0,0,10]) # Mantle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x18018B, [0, 0, 10]) # Mantle respawn refills (magic, bombs, arrows)
|
||||||
bow_max = 70
|
bow_max, bow_small = 70, 10
|
||||||
elif uncle_location.item is not None and uncle_location.item.name in ['Bombs (10)']:
|
elif uncle_location.item is not None and uncle_location.item.name in ['Bombs (10)']:
|
||||||
rom.write_byte(0x18004E, 2) # Escape Fill (bombs)
|
rom.write_byte(0x18004E, 2) # Escape Fill (bombs)
|
||||||
rom.write_bytes(0x180185, [0,50,0]) # Uncle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180185, [0, 50, 0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x180188, [0,3,0]) # Zelda respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180188, [0, 3, 0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x18018B, [0,3,0]) # Mantle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x18018B, [0, 3, 0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||||
bomb_max = 50
|
bomb_max = 50
|
||||||
elif uncle_location.item is not None and uncle_location.item.name in ['Cane of Somaria', 'Cane of Byrna', 'Fire Rod']:
|
elif uncle_location.item is not None and uncle_location.item.name in ['Cane of Somaria', 'Cane of Byrna', 'Fire Rod']:
|
||||||
rom.write_byte(0x18004E, 4) # Escape Fill (magic)
|
rom.write_byte(0x18004E, 4) # Escape Fill (magic)
|
||||||
rom.write_bytes(0x180185, [0x80,0,0]) # Uncle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180185, [0x80, 0, 0]) # Uncle respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x180188, [0x20,0,0]) # Zelda respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180188, [0x20, 0, 0]) # Zelda respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x18018B, [0x20,0,0]) # Mantle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x18018B, [0x20, 0, 0]) # Mantle respawn refills (magic, bombs, arrows)
|
||||||
magic_max = 0x80
|
magic_max, magic_small = 0x80, 0x20
|
||||||
if world.doorShuffle[player] == 'crossed':
|
if world.doorShuffle[player] == 'crossed':
|
||||||
# Uncle respawn refills (magic, bombs, arrows)
|
# Uncle respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x180185, [max(0x20, magic_max), max(3, bomb_max), max(10, bow_max)])
|
rom.write_bytes(0x180185, [max(0x20, magic_max), max(3, bomb_max), max(10, bow_max)])
|
||||||
rom.write_bytes(0x180188, [0x20, 3, 10]) # Zelda respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x180188, [0x20, 3, 10]) # Zelda respawn refills (magic, bombs, arrows)
|
||||||
rom.write_bytes(0x18018B, [0x20, 3, 10]) # Mantle respawn refills (magic, bombs, arrows)
|
rom.write_bytes(0x18018B, [0x20, 3, 10]) # Mantle respawn refills (magic, bombs, arrows)
|
||||||
|
elif world.doorShuffle[player] == 'basic': # just in case a bomb is needed to get to a chest
|
||||||
|
rom.write_bytes(0x180185, [max(0x00, magic_max), max(3, bomb_max), max(0, bow_max)])
|
||||||
|
rom.write_bytes(0x180188, [magic_small, 3, bow_small]) # Zelda respawn refills (magic, bombs, arrows)
|
||||||
|
rom.write_bytes(0x18018B, [magic_small, 3, bow_small]) # Mantle respawn refills (magic, bombs, arrows)
|
||||||
|
|
||||||
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
|
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
|
||||||
rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required[player] else 0x00)
|
rom.write_byte(0x18003D, 0x01 if world.swamp_patch_required[player] else 0x00)
|
||||||
|
|||||||
Reference in New Issue
Block a user