Generation improvements
This commit is contained in:
120
DoorShuffle.py
120
DoorShuffle.py
@@ -4,6 +4,7 @@ import logging
|
|||||||
import operator as op
|
import operator as op
|
||||||
import time
|
import time
|
||||||
from enum import unique, Flag
|
from enum import unique, Flag
|
||||||
|
from typing import DefaultDict, Dict, List
|
||||||
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier, DungeonInfo
|
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier, DungeonInfo
|
||||||
@@ -41,8 +42,21 @@ def link_doors(world, player):
|
|||||||
for entrance, ext in straight_staircases:
|
for entrance, ext in straight_staircases:
|
||||||
connect_two_way(world, entrance, ext, player)
|
connect_two_way(world, entrance, ext, player)
|
||||||
|
|
||||||
|
if world.doorShuffle[player] in ['basic', 'crossed']:
|
||||||
|
find_inaccessible_regions(world, player)
|
||||||
|
|
||||||
if world.intensity[player] >= 3:
|
if world.intensity[player] >= 3:
|
||||||
choose_portals(world, player)
|
choose_portals(world, player)
|
||||||
|
else:
|
||||||
|
for portal in world.dungeon_portals[player]:
|
||||||
|
connect_portal(portal, world, player)
|
||||||
|
world.get_portal('Desert East', player).destination = True
|
||||||
|
world.get_portal('Desert Back', player).deadEnd = True
|
||||||
|
world.get_portal('Skull 1', player).deadEnd = True
|
||||||
|
world.get_portal('Skull 2 West', player).destination = True
|
||||||
|
world.get_portal('Skull 3', player).deadEnd = True
|
||||||
|
world.get_portal('Turtle Rock Lazy Eyes', player).destination = True
|
||||||
|
world.get_portal('Turtle Rock Eye Bridge', player).destination = True
|
||||||
|
|
||||||
if world.doorShuffle[player] == 'vanilla':
|
if world.doorShuffle[player] == 'vanilla':
|
||||||
for entrance, ext in open_edges:
|
for entrance, ext in open_edges:
|
||||||
@@ -320,12 +334,12 @@ def choose_portals(world, player):
|
|||||||
world.get_room(0x62, player).delete(5)
|
world.get_room(0x62, player).delete(5)
|
||||||
world.get_room(0x62, player).change(1, DoorKind.DungeonEntrance)
|
world.get_room(0x62, player).change(1, DoorKind.DungeonEntrance)
|
||||||
|
|
||||||
find_inaccessible_regions(world, player)
|
|
||||||
info_map = {}
|
info_map = {}
|
||||||
for dungeon, portal_list in dungeon_portals.items():
|
for dungeon, portal_list in dungeon_portals.items():
|
||||||
info = DungeonInfo(dungeon)
|
info = DungeonInfo(dungeon)
|
||||||
region_map = defaultdict(list)
|
region_map = defaultdict(list)
|
||||||
reachable_portals = []
|
reachable_portals = []
|
||||||
|
inaccessible_portals = []
|
||||||
for portal in portal_list:
|
for portal in portal_list:
|
||||||
placeholder = world.get_region(portal + ' Placeholder', player)
|
placeholder = world.get_region(portal + ' Placeholder', player)
|
||||||
portal_region = placeholder.exits[0].connected_region
|
portal_region = placeholder.exits[0].connected_region
|
||||||
@@ -333,12 +347,17 @@ def choose_portals(world, player):
|
|||||||
world.get_portal(portal, player).light_world = True
|
world.get_portal(portal, player).light_world = True
|
||||||
if portal_region.name in world.inaccessible_regions[player]:
|
if portal_region.name in world.inaccessible_regions[player]:
|
||||||
region_map[portal_region.name].append(portal)
|
region_map[portal_region.name].append(portal)
|
||||||
|
inaccessible_portals.append(portal)
|
||||||
else:
|
else:
|
||||||
reachable_portals.append(portal)
|
reachable_portals.append(portal)
|
||||||
info.total = len(portal_list)
|
info.total = len(portal_list)
|
||||||
info.required_passage = region_map
|
info.required_passage = region_map
|
||||||
if len(reachable_portals) == 0:
|
if len(reachable_portals) == 0:
|
||||||
raise Exception('please inspect this case')
|
if len(inaccessible_portals) == 1:
|
||||||
|
info.sole_entrance = inaccessible_portals[0]
|
||||||
|
info.required_passage.clear()
|
||||||
|
else:
|
||||||
|
raise Exception('please inspect this case')
|
||||||
if len(reachable_portals) == 1:
|
if len(reachable_portals) == 1:
|
||||||
info.sole_entrance = reachable_portals[0]
|
info.sole_entrance = reachable_portals[0]
|
||||||
info_map[dungeon] = info
|
info_map[dungeon] = info
|
||||||
@@ -347,7 +366,7 @@ def choose_portals(world, player):
|
|||||||
portal_assignment = defaultdict(list)
|
portal_assignment = defaultdict(list)
|
||||||
for dungeon, info in info_map.items():
|
for dungeon, info in info_map.items():
|
||||||
outstanding_portals = list(dungeon_portals[dungeon])
|
outstanding_portals = list(dungeon_portals[dungeon])
|
||||||
if dungeon == 'Hyrule Castle' and world.mode[player] == 'Standard':
|
if dungeon == 'Hyrule Castle' and world.mode[player] == 'standard':
|
||||||
sanc = world.get_portal('Sanctuary', player)
|
sanc = world.get_portal('Sanctuary', player)
|
||||||
sanc.destination = True
|
sanc.destination = True
|
||||||
clean_up_portal_assignment(portal_assignment, dungeon, sanc, master_door_list, outstanding_portals)
|
clean_up_portal_assignment(portal_assignment, dungeon, sanc, master_door_list, outstanding_portals)
|
||||||
@@ -413,6 +432,27 @@ def connect_portal(portal, world, player):
|
|||||||
world.regions.remove(placeholder)
|
world.regions.remove(placeholder)
|
||||||
|
|
||||||
|
|
||||||
|
def connect_portal_copy(portal, world, player):
|
||||||
|
ent, ext = portal_map[portal.name]
|
||||||
|
if world.mode[player] == 'inverted' and portal.name in ['Ganons Tower', 'Agahnims Tower']:
|
||||||
|
ext = 'Inverted ' + ext
|
||||||
|
portal_entrance = world.get_entrance(portal.door.entrance.name, player) # ensures I get the right one for copying
|
||||||
|
target_exit = world.get_entrance(ext, player)
|
||||||
|
entrance_region = portal_entrance.parent_region
|
||||||
|
copy_entrance = None
|
||||||
|
for e in portal_entrance.parent_region.entrances:
|
||||||
|
if e.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] and e.parent_region.name != 'Menu':
|
||||||
|
copy_entrance = e
|
||||||
|
break
|
||||||
|
entrance_region.exits.remove(portal_entrance)
|
||||||
|
entrance_region.exits.append(target_exit)
|
||||||
|
target_exit.parent_region = entrance_region
|
||||||
|
target_exit.connected_region = copy_entrance.parent_region
|
||||||
|
|
||||||
|
placeholder = world.get_region(portal.name + ' Placeholder', player)
|
||||||
|
world.regions.remove(placeholder)
|
||||||
|
|
||||||
|
|
||||||
def find_portal_candidates(door_list, dungeon, need_passage=False, dead_end_allowed=False, crossed=False, bk_shuffle=False):
|
def find_portal_candidates(door_list, dungeon, need_passage=False, dead_end_allowed=False, crossed=False, bk_shuffle=False):
|
||||||
filter_list = [x for x in door_list if bk_shuffle or not x.bk_shuffle_req]
|
filter_list = [x for x in door_list if bk_shuffle or not x.bk_shuffle_req]
|
||||||
if need_passage:
|
if need_passage:
|
||||||
@@ -469,7 +509,7 @@ def clean_up_portal_assignment(portal_assignment, dungeon, portal, master_door_l
|
|||||||
|
|
||||||
def create_dungeon_entrances(world, player):
|
def create_dungeon_entrances(world, player):
|
||||||
entrance_map = defaultdict(list)
|
entrance_map = defaultdict(list)
|
||||||
split_map = defaultdict(dict)
|
split_map: DefaultDict[str, DefaultDict[str, List]] = defaultdict(lambda: defaultdict(list))
|
||||||
for key, portal_list in dungeon_portals.items():
|
for key, portal_list in dungeon_portals.items():
|
||||||
if world.mode[player] == 'standard' and key in standard_starts.keys():
|
if world.mode[player] == 'standard' and key in standard_starts.keys():
|
||||||
portal = world.get_portal('Hyrule Castle South', player)
|
portal = world.get_portal('Hyrule Castle South', player)
|
||||||
@@ -477,13 +517,43 @@ def create_dungeon_entrances(world, player):
|
|||||||
else:
|
else:
|
||||||
if key in dungeon_drops.keys():
|
if key in dungeon_drops.keys():
|
||||||
entrance_map[key].extend(dungeon_drops[key])
|
entrance_map[key].extend(dungeon_drops[key])
|
||||||
for portal_name in portal_list:
|
if key in split_portals.keys() and world.intensity[player] >= 3:
|
||||||
portal = world.get_portal(portal_name, player)
|
dead_ends = []
|
||||||
r_name = portal.door.entrance.parent_region.name
|
destinations = []
|
||||||
entrance_map[key].append(r_name)
|
the_rest = []
|
||||||
if key in split_portals.keys():
|
for portal_name in portal_list:
|
||||||
for split_key in split_portals[key]:
|
portal = world.get_portal(portal_name, player)
|
||||||
split_map[key][split_key] = []
|
r_name = portal.door.entrance.parent_region.name
|
||||||
|
entrance_map[key].append(r_name)
|
||||||
|
if portal.deadEnd:
|
||||||
|
dead_ends.append(r_name)
|
||||||
|
elif portal.destination:
|
||||||
|
destinations.append(r_name)
|
||||||
|
else:
|
||||||
|
the_rest.append(r_name)
|
||||||
|
choices = list(split_portals[key])
|
||||||
|
for r_name in dead_ends:
|
||||||
|
choice = random.choice(choices)
|
||||||
|
choices.remove(choice)
|
||||||
|
split_map[key][choice].append(r_name)
|
||||||
|
for r_name in the_rest:
|
||||||
|
choice = random.choice(choices)
|
||||||
|
split_map[key][choice].append(r_name)
|
||||||
|
dest_choices = [x for x in choices if len(split_map[key][x]) > 0]
|
||||||
|
for r_name in destinations:
|
||||||
|
choice = random.choice(dest_choices)
|
||||||
|
split_map[key][choice].append(r_name)
|
||||||
|
else:
|
||||||
|
for portal_name in portal_list:
|
||||||
|
portal = world.get_portal(portal_name, player)
|
||||||
|
r_name = portal.door.entrance.parent_region.name
|
||||||
|
entrance_map[key].append(r_name)
|
||||||
|
if key in split_portals.keys():
|
||||||
|
for split_key in split_portals[key]:
|
||||||
|
if split_key not in split_map[key]:
|
||||||
|
split_map[key][split_key] = []
|
||||||
|
if world.intensity[player] < 3:
|
||||||
|
split_map[key][split_portal_defaults[key][r_name]].append(r_name)
|
||||||
return entrance_map, split_map
|
return entrance_map, split_map
|
||||||
|
|
||||||
|
|
||||||
@@ -2376,19 +2446,19 @@ split_portals = {
|
|||||||
'Skull Woods': ['1', '2', '3']
|
'Skull Woods': ['1', '2', '3']
|
||||||
}
|
}
|
||||||
|
|
||||||
# split_portals = {
|
split_portal_defaults = {
|
||||||
# 'Desert Palace': {
|
'Desert Palace': {
|
||||||
# 'Desert Back': 'Back',
|
'Desert Back Lobby': 'Back',
|
||||||
# 'Desert South': 'Main',
|
'Desert Main Lobby': 'Main',
|
||||||
# 'Desert West': 'Main',
|
'Desert West Lobby': 'Main',
|
||||||
# 'Desert East': 'Main'
|
'Desert East Lobby': 'Main'
|
||||||
# },
|
},
|
||||||
# 'Skull Woods': {
|
'Skull Woods': {
|
||||||
# 'Skull 1': '1',
|
'Skull 1 Lobby': '1',
|
||||||
# 'Skull 2 East': '2',
|
'Skull 2 East Lobby': '2',
|
||||||
# 'Skull 2 West': '2',
|
'Skull 2 West Lobby': '2',
|
||||||
# 'Skull 3': '3'
|
'Skull 3 Lobby': '3'
|
||||||
# }
|
}
|
||||||
# }
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1253,15 +1253,16 @@ def create_dungeon_builders(all_sectors, 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)
|
||||||
lw_builders = []
|
if sanc: # only run if sanc if a candidate
|
||||||
for name, portal_list in dungeon_portals.items():
|
lw_builders = []
|
||||||
for portal_name in portal_list:
|
for name, portal_list in dungeon_portals.items():
|
||||||
if world.get_portal(portal_name, player).light_world:
|
for portal_name in portal_list:
|
||||||
lw_builders.append(dungeon_map[name])
|
if world.get_portal(portal_name, player).light_world:
|
||||||
break
|
lw_builders.append(dungeon_map[name])
|
||||||
# portals only - not drops for mirror stuff
|
break
|
||||||
sanc_builder = random.choice(lw_builders)
|
# portals only - not drops for mirror stuff
|
||||||
assign_sector(sanc, sanc_builder, candidate_sectors, global_pole)
|
sanc_builder = random.choice(lw_builders)
|
||||||
|
assign_sector(sanc, sanc_builder, candidate_sectors, global_pole)
|
||||||
|
|
||||||
free_location_sectors = {}
|
free_location_sectors = {}
|
||||||
crystal_switches = {}
|
crystal_switches = {}
|
||||||
|
|||||||
4
Main.py
4
Main.py
@@ -16,7 +16,7 @@ from InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
|||||||
from EntranceShuffle import link_entrances, link_inverted_entrances
|
from EntranceShuffle import link_entrances, link_inverted_entrances
|
||||||
from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, JsonRom, get_hash_string
|
from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, JsonRom, get_hash_string
|
||||||
from Doors import create_doors
|
from Doors import create_doors
|
||||||
from DoorShuffle import link_doors, connect_portal
|
from DoorShuffle import link_doors, connect_portal_copy
|
||||||
from RoomData import create_rooms
|
from RoomData import create_rooms
|
||||||
from Rules import set_rules
|
from Rules import set_rules
|
||||||
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
||||||
@@ -444,7 +444,7 @@ def copy_world(world):
|
|||||||
ret.dungeon_portals = world.dungeon_portals
|
ret.dungeon_portals = world.dungeon_portals
|
||||||
for player, portals in world.dungeon_portals.items():
|
for player, portals in world.dungeon_portals.items():
|
||||||
for portal in portals:
|
for portal in portals:
|
||||||
connect_portal(portal, ret, player)
|
connect_portal_copy(portal, ret, player)
|
||||||
ret.sanc_portal = world.sanc_portal
|
ret.sanc_portal = world.sanc_portal
|
||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
|
|||||||
Reference in New Issue
Block a user