Fix for certain Standard key layouts
Valid key door logic fix
This commit is contained in:
@@ -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'])
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user