Fix for certain Standard key layouts

Valid key door logic fix
This commit is contained in:
aerinon
2022-12-01 08:30:27 -07:00
parent f977c06b5e
commit 249fae39a0
3 changed files with 44 additions and 10 deletions

View File

@@ -230,7 +230,7 @@ def vanilla_key_logic(world, player):
origin_list = entrances_map[builder.name]
start_regions = convert_regions(origin_list, world, player)
doors = convert_key_doors(default_small_key_doors[builder.name], world, player)
key_layout = build_key_layout(builder, start_regions, doors, world, player)
key_layout = build_key_layout(builder, start_regions, doors, {}, world, player)
valid = validate_key_layout(key_layout, world, player)
if not valid:
logging.getLogger('').info('Vanilla key layout not valid %s', builder.name)
@@ -1758,7 +1758,7 @@ class BuilderDoorCandidates:
def shuffle_door_types(door_type_pools, paths, world, player):
start_regions_map = {}
for name, builder in world.dungeon_layouts[player].items():
start_regions = convert_regions(builder.path_entrances, world, player)
start_regions = convert_regions(find_possible_entrances(world, player, builder), world, player)
start_regions_map[name] = start_regions
builder.candidates = BuilderDoorCandidates()
@@ -2149,7 +2149,7 @@ def find_valid_trap_combination(builder, suggested, start_regions, paths, world,
proposal = kth_combination(sample_list[itr], trap_door_pool, trap_doors_needed)
proposal.extend(custom_trap_doors)
start_regions = filter_start_regions(builder, start_regions, world, player)
start_regions, event_starts = filter_start_regions(builder, start_regions, world, player)
while not validate_trap_layout(proposal, builder, start_regions, paths, world, player):
itr += 1
if itr >= len(sample_list):
@@ -2171,6 +2171,7 @@ def find_valid_trap_combination(builder, suggested, start_regions, paths, world,
def filter_start_regions(builder, start_regions, world, player):
std_flag = world.mode[player] == 'standard' and builder.name == 'Hyrule Castle'
excluded = {} # todo: drop lobbies, might be better to white list instead (two entrances per region)
event_doors = {}
for region in start_regions:
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
if portal and portal.destination:
@@ -2180,9 +2181,21 @@ def filter_start_regions(builder, start_regions, world, player):
or x.parent_region.name == 'Sewer Drop'), None)
if not drop_region:
excluded[region] = None
if portal and not portal.destination:
portal_entrance_region = portal.door.entrance.parent_region.name
if portal_entrance_region not in builder.path_entrances:
excluded[region] = None
if std_flag and (not portal or portal.find_portal_entrance().parent_region.name != 'Hyrule Castle Courtyard'):
excluded[region] = None
return [x for x in start_regions if x not in excluded.keys()]
if portal is None:
entrance = next((x for x in region.entrances
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]
or x.parent_region.name == 'Sewer Drop'), None)
event_doors[entrance] = None
else:
event_doors[portal.find_portal_entrance()] = None
return [x for x in start_regions if x not in excluded.keys()], event_doors
def validate_trap_layout(proposal, builder, start_regions, paths, world, player):
@@ -2412,7 +2425,7 @@ def find_valid_bk_combination(builder, suggested, start_regions, world, player,
proposal = kth_combination(sample_list[itr], bk_door_pool, bk_doors_needed)
proposal.extend(custom_bk_doors)
start_regions = filter_start_regions(builder, start_regions, world, player)
start_regions, event_starts = filter_start_regions(builder, start_regions, world, player)
while not validate_bk_layout(proposal, builder, start_regions, world, player):
itr += 1
if itr >= len(sample_list):
@@ -2556,9 +2569,9 @@ def find_valid_combination(builder, target, start_regions, world, player, drop_k
sample_list = build_sample_list(combinations)
proposal = kth_combination(sample_list[itr], key_door_pool, key_doors_needed)
proposal.extend(custom_key_doors)
start_regions = filter_start_regions(builder, start_regions, world, player)
start_regions, event_starts = filter_start_regions(builder, start_regions, world, player)
key_layout = build_key_layout(builder, start_regions, proposal, world, player)
key_layout = build_key_layout(builder, start_regions, proposal, event_starts, world, player)
determine_prize_lock(key_layout, world, player)
while not validate_key_layout(key_layout, world, player):
itr += 1
@@ -3251,6 +3264,14 @@ def find_accessible_entrances(world, player, builder):
return visited_entrances
def find_possible_entrances(world, player, builder):
entrances = [region.name for region in
(portal.door.entrance.parent_region for portal in world.dungeon_portals[player])
if region.dungeon.name == builder.name]
entrances.extend(drop_entrances[builder.name])
return entrances
def valid_inaccessible_region(r):
return r.type is not RegionType.Cave or (len(r.exits) > 0 and r.name not in ['Links House', 'Chris Houlihan Room'])

View File

@@ -10,7 +10,7 @@ import time
from typing import List
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys, Sector
from BaseClasses import Hook, hook_from_door
from BaseClasses import Hook, hook_from_door, Door
from Regions import dungeon_events, flooded_keys_reverse
from Dungeons import dungeon_regions, split_region_starts
from RoomData import DoorKind
@@ -846,6 +846,14 @@ class ExplorationState(object):
ret.prize_received = self.prize_received
return ret
def init_zelda_event_doors(self, event_starts, player):
for entrance in event_starts:
event_door = Door(player, entrance.name, DoorType.Logical)
event_door.req_event = 'Zelda Drop Off'
event_door.entrance = entrance
event_door.crystal = CrystalBarrier.Orange # always start in orange
self.append_door_to_list(event_door, self.event_doors)
def next_avail_door(self):
self.avail_doors.sort(key=lambda x: 0 if x.flag else 1 if x.door.bigKey else 2)
exp_door = self.avail_doors.pop()

View File

@@ -14,6 +14,7 @@ class KeyLayout(object):
def __init__(self, sector, starts, proposal):
self.sector = sector
self.start_regions = starts
self.event_starts = []
self.proposal = proposal
self.key_logic = KeyLogic(sector.name)
@@ -223,13 +224,14 @@ class KeyCounter(object):
return max(self.used_keys + reserve - len(self.key_only_locations), 0)
def build_key_layout(builder, start_regions, proposal, world, player):
def build_key_layout(builder, start_regions, proposal, event_starts, world, player):
key_layout = KeyLayout(builder.master_sector, start_regions, proposal)
key_layout.flat_prop = flatten_pair_list(key_layout.proposal)
key_layout.max_drops = count_key_drops(key_layout.sector)
key_layout.max_chests = calc_max_chests(builder, key_layout, world, player)
key_layout.big_key_special = check_bk_special(key_layout.sector.region_set(), world, player)
key_layout.all_locations = find_all_locations(key_layout.sector)
key_layout.event_starts = list(event_starts.keys())
return key_layout
@@ -1455,6 +1457,7 @@ def validate_key_layout(key_layout, world, player):
return True
flat_proposal = key_layout.flat_prop
state = ExplorationState(dungeon=key_layout.sector.name)
state.init_zelda_event_doors(key_layout.event_starts, player)
state.key_locations = key_layout.max_chests
state.big_key_special = check_bk_special(key_layout.sector.regions, world, player)
for region in key_layout.start_regions:
@@ -1489,7 +1492,8 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
# todo: allow more key shuffles - refine placement rules
# if (not smalls_avail or available_small_locations == 0) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0):
found_forced_bk = state.found_forced_bk()
smalls_done = not smalls_avail # or not enough_small_locations(state, available_small_locations)
smalls_done = not smalls_avail or available_small_locations == 0
# or not enough_small_locations(state, available_small_locations)
bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk)
# prize door should not be opened if the boss is reachable - but not reached yet
allow_for_prize_lock = (key_layout.prize_can_lock and
@@ -1646,6 +1650,7 @@ def create_key_counters(key_layout, world, player):
key_layout.found_doors.clear()
flat_proposal = key_layout.flat_prop
state = ExplorationState(dungeon=key_layout.sector.name)
state.init_zelda_event_doors(key_layout.event_starts, player)
if world.doorShuffle[player] == 'vanilla':
builder = world.dungeon_layouts[player][key_layout.sector.name]
state.key_locations = len(builder.key_door_proposal) - builder.key_drop_cnt