Adding new shared/utility functions

This commit is contained in:
codemann8
2021-10-14 22:01:56 -05:00
parent 6aeabc5e66
commit 9d7663c648

View File

@@ -1437,6 +1437,215 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player):
connect_caves(world, lw_entrances, dw_entrances, dungeon_exits, player) connect_caves(world, lw_entrances, dw_entrances, dungeon_exits, player)
def place_links_house(world, sectors, player):
invFlag = world.mode[player] == 'inverted'
if world.mode[player] == 'standard' or not world.shufflelinks[player]:
links_house = 'Links House' if not invFlag else 'Big Bomb Shop'
else:
def get_link_candidates():
# find largest walkable sector
sector = None
invalid_sectors = list()
while (sector is None):
sector = max(sectors, key=lambda x: len(x) - (0 if x not in invalid_sectors else 1000))
if not ((world.owCrossed[player] == 'polar' and world.owMixed[player]) or world.owCrossed[player] not in ['none', 'polar']) \
and world.get_region(next(iter(next(iter(sector)))), player).type != (RegionType.LightWorld if not invFlag else RegionType.DarkWorld):
invalid_sectors.append(sector)
sector = None
regions = max(sector, key=lambda x: len(x))
# get entrances from list of regions
entrances = list()
for region_name in [r for r in regions if r ]:
if world.shuffle[player] == 'simple' and region_name in OWTileRegions and OWTileRegions[region_name] in [0x03, 0x05, 0x07]:
continue
region = world.get_region(region_name, player)
if region.type == RegionType.LightWorld if not invFlag else RegionType.DarkWorld:
for exit in region.exits:
if not exit.connected_region and exit.spot_type == 'Entrance':
entrances.append(exit.name)
return entrances
links_house_doors = [i for i in get_link_candidates() if i in entrance_pool]
links_house = random.choice(links_house_doors)
connect_two_way(world, links_house, 'Links House Exit', player)
return links_house
def place_dark_sanc(world, links_house, sectors, player):
# get walkable sector in which links house was placed
links_region = world.get_entrance(links_house, player).parent_region.name
regions = next(s for s in sectors if any(links_region in w for w in s))
regions = next(w for w in regions if links_region in w)
# eliminate regions surrounding links until less than half of the candidate regions remain
explored_regions = list({links_region})
was_progress = True
while was_progress and len(explored_regions) < len(regions) / 2:
was_progress = False
new_regions = list()
for region_name in explored_regions:
region = world.get_region(region_name, player)
for exit in region.exits:
if exit.connected_region and region.type == exit.connected_region.type and exit.connected_region.name in regions and exit.connected_region.name not in explored_regions + new_regions:
new_regions.append(exit.connected_region.name)
was_progress = True
explored_regions.extend(new_regions)
# get entrances from remaining regions
sanc_doors = list()
for region_name in [r for r in regions if r not in explored_regions]:
if OWTileRegions[region_name] in [0x03, 0x05, 0x07]:
continue
region = world.get_region(region_name, player)
for exit in region.exits:
if not exit.connected_region and exit.spot_type == 'Entrance':
sanc_doors.append(exit.name)
sanc_doors = [i for i in sanc_doors if i in entrance_pool]
sanc_door = random.choice(sanc_doors)
connect_entrance(world, sanc_door, 'Dark Sanctuary Hint', player)
world.get_entrance('Dark Sanctuary Hint Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
return sanc_door
def place_blacksmith(world, links_house, player):
invFlag = world.mode[player] == 'inverted'
assumed_inventory = list()
region = world.get_region('Frog Prison', player)
if world.logic[player] in ['noglitches', 'minorglitches'] and region.type == (RegionType.DarkWorld if not invFlag else RegionType.LightWorld):
assumed_inventory.append('Titans Mitts')
links_region = world.get_entrance(links_house, player).parent_region.name
blacksmith_doors = list(build_accessible_entrance_list(world, links_region, player, assumed_inventory, False, True, True))
if invFlag:
dark_sanc = world.get_entrance('Dark Sanctuary Hint Exit', player).connected_region.name
blacksmith_doors = list(set(blacksmith_doors + list(build_accessible_entrance_list(world, dark_sanc, player, assumed_inventory, False, True, True))))
elif world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3:
sanc_region = world.get_entrance('Sanctuary Exit', player).connected_region.name
blacksmith_doors = list(set(blacksmith_doors + list(build_accessible_entrance_list(world, sanc_region, player, assumed_inventory, False, True, True))))
random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
return blacksmith_hut
def place_old_man(world, pool, player):
# exit has to come from specific set of doors, the entrance is free to move about
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
region_name = 'West Death Mountain (Top)'
else:
region_name = 'West Dark Death Mountain (Top)'
old_man_entrances = list(build_accessible_entrance_list(world, region_name, player, [], False, True, True))
random.shuffle(old_man_entrances)
old_man_exit = None
while not old_man_exit:
old_man_exit = old_man_entrances.pop()
if 'West Death Mountain (Bottom)' not in build_accessible_region_list(world, world.get_entrance(old_man_exit, player).parent_region.name, player, True):
old_man_exit = None
old_man_entrances = [e for e in pool if e in entrance_pool and e not in entrance_exits]
random.shuffle(old_man_entrances)
old_man_entrance = old_man_entrances.pop()
if world.shuffle[player] != 'insanity':
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
else:
# skip assigning connections to West Entrance/Exit
connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player, False)
connect_entrance(world, old_man_entrance, 'Old Man Cave Exit (East)', player, False)
def junk_fill_inaccessible(world, player):
from DoorShuffle import find_inaccessible_regions
find_inaccessible_regions(world, player)
# remove regions that have a dungeon entrance
accessible_regions = list()
for region_name in world.inaccessible_regions[player]:
region = world.get_region(region_name, player)
for exit in region.exits:
if exit.connected_region and exit.connected_region.type == RegionType.Dungeon:
accessible_regions.append(region_name)
break
for region_name in accessible_regions.copy():
accessible_regions = list(set(accessible_regions + list(build_accessible_region_list(world, region_name, player, True, False, False))))
world.inaccessible_regions[player] = [r for r in world.inaccessible_regions[player] if r not in accessible_regions]
# get inaccessible entrances
inaccessible_entrances = list()
for region_name in world.inaccessible_regions[player]:
region = world.get_region(region_name, player)
if region.type in [RegionType.LightWorld, RegionType.DarkWorld]:
for exit in region.exits:
if not exit.connected_region and exit.name in entrance_pool:
inaccessible_entrances.append(exit.name)
#TODO: assign non-item locations to the entrances that exist in the unreachable regions
def connect_inaccessible_regions(world, lw_entrances, dw_entrances, caves, player):
invFlag = world.mode[player] == 'inverted'
random.shuffle(lw_entrances)
random.shuffle(dw_entrances)
from DoorShuffle import find_inaccessible_regions
find_inaccessible_regions(world, player)
# remove regions that have a dungeon entrance
accessible_regions = list()
for region_name in world.inaccessible_regions[player]:
region = world.get_region(region_name, player)
for exit in region.exits:
if exit.connected_region and exit.connected_region.type == RegionType.Dungeon:
accessible_regions.append(region_name)
break
for region_name in accessible_regions.copy():
accessible_regions = list(set(accessible_regions + list(build_accessible_region_list(world, region_name, player, True, False, False))))
world.inaccessible_regions[player] = [r for r in world.inaccessible_regions[player] if r not in accessible_regions]
# split inaccessible into 2 lists for each world
inaccessible_regions = list(world.inaccessible_regions[player])
must_exit_regions = list()
otherworld_must_exit_regions = list()
for region_name in inaccessible_regions.copy():
region = world.get_region(region_name, player)
if region.type not in [RegionType.LightWorld, RegionType.DarkWorld] or not any((not exit.connected_region and exit.spot_type == 'Entrance') for exit in region.exits) \
or (region_name == 'Pyramid Exit Ledge' and invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
inaccessible_regions.remove(region_name)
elif region.type == (RegionType.LightWorld if not invFlag else RegionType.DarkWorld):
must_exit_regions.append(region_name)
elif region.type == (RegionType.DarkWorld if not invFlag else RegionType.LightWorld):
otherworld_must_exit_regions.append(region_name)
def connect_one(region_name, pool):
inaccessible_entrances = list()
region = world.get_region(region_name, player)
for exit in region.exits:
if not exit.connected_region and exit.name in entrance_pool:
inaccessible_entrances.append(exit.name)
random.shuffle(inaccessible_entrances)
connect_mandatory_exits(world, pool, caves, [inaccessible_entrances.pop()], player)
connect_inaccessible_regions(world, lw_entrances, dw_entrances, caves, player)
# connect one connector at a time to ensure multiple connectors aren't assigned to the same inaccessible set of regions
if world.shuffle[player] in ['crossed', 'insanity']:
combined_must_exit_regions = list(must_exit_regions + otherworld_must_exit_regions)
if len(combined_must_exit_regions) > 0:
random.shuffle(combined_must_exit_regions)
connect_one(combined_must_exit_regions[0], [e for e in lw_entrances if e in entrance_pool])
else:
if len(otherworld_must_exit_regions) > 0:
random.shuffle(otherworld_must_exit_regions)
connect_one(otherworld_must_exit_regions[0], [e for e in (dw_entrances if not invFlag else lw_entrances) if e in entrance_pool])
elif len(must_exit_regions) > 0:
random.shuffle(must_exit_regions)
connect_one(must_exit_regions[0], [e for e in (lw_entrances if not invFlag else dw_entrances) if e in entrance_pool])
def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits): def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits):
def shuffle_lists_in_list(ls): def shuffle_lists_in_list(ls):
for i, item in enumerate(ls): for i, item in enumerate(ls):
@@ -1477,6 +1686,137 @@ def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_E
tuplize_lists_in_list(Cave_Three_Exits) tuplize_lists_in_list(Cave_Three_Exits)
def build_sectors(world, player):
from Main import copy_world
from OWEdges import OWTileRegions
# perform accessibility check on duplicate world
for player in range(1, world.players + 1):
world.key_logic[player] = {}
base_world = copy_world(world)
world.key_logic = {}
# build lists of contiguous regions accessible with full inventory (excl portals/mirror/flute/entrances)
regions = list(OWTileRegions.copy().keys())
sectors = list()
while(len(regions) > 0):
explored_regions = build_accessible_region_list(base_world, regions[0], player, False, False, False)
regions = [r for r in regions if r not in explored_regions]
unique_regions = [_ for i in range(len(sectors)) for _ in sectors[i]]
if (any(r in unique_regions for r in explored_regions)):
for s in range(len(sectors)):
if (any(r in sectors[s] for r in explored_regions)):
sectors[s] = set(list(sectors[s]) + list(explored_regions))
break
else:
sectors.append(explored_regions)
# remove water regions if Flippers not in starting inventory
if not any(map(lambda i: i.name == 'Flippers', world.precollected_items)):
for s in range(len(sectors)):
terrains = list()
for regionname in sectors[s]:
region = world.get_region(regionname, player)
if region.terrain == Terrain.Land:
terrains.append(regionname)
sectors[s] = terrains
# within each group, split into contiguous regions accessible only with starting inventory
for s in range(len(sectors)):
regions = list(sectors[s]).copy()
sectors2 = list()
while(len(regions) > 0):
explored_regions = build_accessible_region_list(base_world, regions[0], player, False, True, False)
regions = [r for r in regions if r not in explored_regions]
unique_regions = [_ for i in range(len(sectors2)) for _ in sectors2[i]]
if (any(r in unique_regions for r in explored_regions)):
for s2 in range(len(sectors2)):
if (any(r in sectors2[s2] for r in explored_regions)):
sectors2[s2] = set(list(sectors2[s2]) + list(explored_regions))
break
else:
sectors2.append(explored_regions)
sectors[s] = sectors2
return sectors
def build_accessible_region_list(world, start_region, player, cross_world=False, region_rules=True, ignore_ledges = False):
def explore_region(region_name):
explored_regions.add(region_name)
region = world.get_region(region_name, player)
for exit in region.exits:
if exit.connected_region is not None \
and ((any(map(lambda i: i.name == 'Ocarina', world.precollected_items)) and exit.spot_type == 'Flute') \
or (exit.connected_region.type == region.type or (cross_world and exit.connected_region.type not in [RegionType.Cave, RegionType.Dungeon]))) \
and (not region_rules or exit.access_rule(blank_state)) and (not ignore_ledges or exit.spot_type != 'Ledge') \
and exit.connected_region.name not in explored_regions:
if exit.spot_type == 'Flute':
fluteregion = exit.connected_region
for flutespot in fluteregion.exits:
if flutespot.connected_region and flutespot.connected_region.name not in explored_regions:
explore_region(flutespot.connected_region.name)
else:
explore_region(exit.connected_region.name)
connect_simple(world, 'Links House S&Q', start_region, player)
blank_state = CollectionState(world)
explored_regions = set()
explore_region(start_region)
return explored_regions
def build_accessible_entrance_list(world, start_region, player, assumed_inventory=[], cross_world=False, region_rules=True, exit_rules=True):
from Main import copy_world
from Items import ItemFactory
for player in range(1, world.players + 1):
world.key_logic[player] = {}
base_world = copy_world(world)
base_world.override_bomb_check = True
world.key_logic = {}
connect_simple(base_world, 'Links House S&Q', start_region, player)
blank_state = CollectionState(base_world)
if base_world.mode[player] == 'standard':
blank_state.collect(ItemFactory('Zelda Delivered', player), True)
for item in assumed_inventory:
blank_state.collect(ItemFactory(item, player), True)
explored_regions = build_accessible_region_list(base_world, start_region, player, cross_world, region_rules, False)
entrances = set()
for region_name in explored_regions:
region = base_world.get_region(region_name, player)
for exit in region.exits:
if exit.name in entrance_pool and (not exit_rules or exit.access_rule(blank_state)):
entrances.add(exit.name)
return entrances
def can_reach(world, entrance_name, region_name, player):
from Main import copy_world
from Items import ItemFactory
from DoorShuffle import find_inaccessible_regions
for player in range(1, world.players + 1):
world.key_logic[player] = {}
base_world = copy_world(world)
base_world.override_bomb_check = True
world.key_logic = {}
entrance = world.get_entrance(entrance_name, player)
connect_simple(base_world, 'Links House S&Q', entrance.parent_region.name, player)
blank_state = CollectionState(base_world)
if base_world.mode[player] == 'standard':
blank_state.collect(ItemFactory('Zelda Delivered', player), True)
find_inaccessible_regions(world, player)
return region_name not in world.inaccessible_regions[player]
LW_Dungeon_Entrances = ['Desert Palace Entrance (South)', LW_Dungeon_Entrances = ['Desert Palace Entrance (South)',
'Desert Palace Entrance (West)', 'Desert Palace Entrance (West)',
'Desert Palace Entrance (North)', 'Desert Palace Entrance (North)',