Merged DR v1.2.0.0
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -251,7 +253,7 @@ def find_all_locations(sector):
|
||||
|
||||
|
||||
def calc_max_chests(builder, key_layout, world, player):
|
||||
if world.doorShuffle[player] != 'crossed':
|
||||
if world.doorShuffle[player] in ['basic', 'vanilla']:
|
||||
return len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
||||
return max(0, builder.key_doors_num - key_layout.max_drops)
|
||||
|
||||
@@ -275,7 +277,7 @@ def analyze_dungeon(key_layout, world, player):
|
||||
key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations))
|
||||
key_logic.bk_chests.update(find_big_key_locked_locations(key_layout.all_chest_locations))
|
||||
key_logic.prize_location = dungeon_table[key_layout.sector.name].prize
|
||||
if world.retro[player] and world.mode[player] != 'standard':
|
||||
if world.keyshuffle[player] == 'universal' and world.mode[player] != 'standard':
|
||||
return
|
||||
|
||||
original_key_counter = find_counter({}, False, key_layout, False)
|
||||
@@ -301,10 +303,10 @@ def analyze_dungeon(key_layout, world, player):
|
||||
key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations))
|
||||
# note to self: this is due to the enough_small_locations function in validate_key_layout_sub_loop
|
||||
# I don't like this exception here or there
|
||||
elif available < possible_smalls and avail_bigs and non_big_locs > 0:
|
||||
max_ctr = find_max_counter(key_layout)
|
||||
bk_lockdown = [x for x in max_ctr.free_locations if x not in key_counter.free_locations]
|
||||
key_logic.bk_restricted.update(filter_big_chest(bk_lockdown))
|
||||
# elif available < possible_smalls and avail_bigs and non_big_locs > 0:
|
||||
# max_ctr = find_max_counter(key_layout)
|
||||
# bk_lockdown = [x for x in max_ctr.free_locations if x not in key_counter.free_locations]
|
||||
# key_logic.bk_restricted.update(filter_big_chest(bk_lockdown))
|
||||
# try to relax the rules here? - smallest requirement that doesn't force a softlock
|
||||
child_queue = deque()
|
||||
for child in key_counter.child_doors.keys():
|
||||
@@ -926,7 +928,7 @@ def self_lock_possible(counter):
|
||||
|
||||
|
||||
def available_chest_small_keys(key_counter, world, player):
|
||||
if not world.keyshuffle[player] and not world.retro[player]:
|
||||
if world.keyshuffle[player] == 'none':
|
||||
cnt = 0
|
||||
for loc in key_counter.free_locations:
|
||||
if key_counter.big_key_opened or '- Big Chest' not in loc.name:
|
||||
@@ -937,7 +939,7 @@ def available_chest_small_keys(key_counter, world, player):
|
||||
|
||||
|
||||
def available_chest_small_keys_logic(key_counter, world, player, sm_restricted):
|
||||
if not world.keyshuffle[player] and not world.retro[player]:
|
||||
if world.keyshuffle[player] == 'none':
|
||||
cnt = 0
|
||||
for loc in key_counter.free_locations:
|
||||
if loc not in sm_restricted and (key_counter.big_key_opened or '- Big Chest' not in loc.name):
|
||||
@@ -1172,6 +1174,16 @@ def expand_key_state(state, flat_proposal, world, player):
|
||||
state.add_all_doors_check_keys(connect_region, flat_proposal, world, player)
|
||||
|
||||
|
||||
def expand_big_key_state(state, flat_proposal, world, player):
|
||||
while len(state.avail_doors) > 0:
|
||||
exp_door = state.next_avail_door()
|
||||
door = exp_door.door
|
||||
connect_region = world.get_entrance(door.name, player).connected_region
|
||||
if state.validate(door, connect_region, world, player):
|
||||
state.visit_region(connect_region, key_checks=True)
|
||||
state.add_all_doors_check_big_keys(connect_region, flat_proposal, world, player)
|
||||
|
||||
|
||||
def flatten_pair_list(paired_list):
|
||||
flat_list = []
|
||||
for d in paired_list:
|
||||
@@ -1401,13 +1413,51 @@ def prize_relevance(key_layout, dungeon_entrance, is_atgt_swapped):
|
||||
return None
|
||||
|
||||
|
||||
def prize_relevance_sig2(start_regions, d_name, dungeon_entrance, world, player):
|
||||
if len(start_regions) > 1 and dungeon_entrance and dungeon_table[d_name].prize:
|
||||
if dungeon_entrance.name == ('Agahmins Tower' if world.is_atgt_swapped(player) else 'Ganons Tower'):
|
||||
return 'GT'
|
||||
elif dungeon_entrance.name == 'Pyramid Fairy':
|
||||
return 'BigBomb'
|
||||
return None
|
||||
|
||||
|
||||
def validate_bk_layout(proposal, builder, start_regions, world, player):
|
||||
bk_special = check_bk_special(builder.master_sector.regions, world, player)
|
||||
if world.bigkeyshuffle[player] and (world.dropshuffle[player] or not bk_special):
|
||||
return True
|
||||
flat_proposal = flatten_pair_list(proposal)
|
||||
state = ExplorationState(dungeon=builder.name)
|
||||
state.big_key_special = bk_special
|
||||
for region in start_regions:
|
||||
dungeon_entrance, portal_door = find_outside_connection(region)
|
||||
prize_relevant_flag = prize_relevance_sig2(start_regions, builder.name, dungeon_entrance, world, player)
|
||||
if prize_relevant_flag:
|
||||
state.append_door_to_list(portal_door, state.prize_doors)
|
||||
state.prize_door_set[portal_door] = dungeon_entrance
|
||||
# key_layout.prize_relevant = prize_relevant_flag
|
||||
else:
|
||||
state.visit_region(region, key_checks=True)
|
||||
state.add_all_doors_check_big_keys(region, flat_proposal, world, player)
|
||||
expand_big_key_state(state, flat_proposal, world, player)
|
||||
if bk_special:
|
||||
for loc in state.found_locations:
|
||||
if loc.forced_big_key():
|
||||
return True
|
||||
else:
|
||||
return len(state.bk_found) > 0
|
||||
return False
|
||||
|
||||
|
||||
# Soft lock stuff
|
||||
def validate_key_layout(key_layout, world, player):
|
||||
# retro is all good - except for hyrule castle in standard mode
|
||||
if (world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')) or world.logic[player] == 'nologic':
|
||||
# universal key is all good - except for hyrule castle in standard mode
|
||||
if world.logic[player] == 'nologic' or (world.keyshuffle[player] == 'universal' and
|
||||
(world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')):
|
||||
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:
|
||||
@@ -1442,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
|
||||
@@ -1532,8 +1583,8 @@ def enough_small_locations(state, avail_small_loc):
|
||||
|
||||
|
||||
def determine_prize_lock(key_layout, world, player):
|
||||
if ((world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle'))
|
||||
or world.logic[player] == 'nologic'):
|
||||
if world.logic[player] == 'nologic' or (world.keyshuffle[player] == 'universal' and
|
||||
(world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')):
|
||||
return # done, doesn't matter what
|
||||
flat_proposal = key_layout.flat_prop
|
||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
||||
@@ -1565,7 +1616,7 @@ def determine_prize_lock(key_layout, world, player):
|
||||
|
||||
|
||||
def cnt_avail_small_locations(free_locations, key_only, state, world, player):
|
||||
if not world.keyshuffle[player] and not world.retro[player]:
|
||||
if world.keyshuffle[player] == 'none':
|
||||
bk_adj = 1 if state.big_key_opened and not state.big_key_special else 0
|
||||
avail_chest_keys = min(free_locations - bk_adj, state.key_locations - key_only)
|
||||
return max(0, avail_chest_keys + key_only - state.used_smalls)
|
||||
@@ -1573,7 +1624,7 @@ def cnt_avail_small_locations(free_locations, key_only, state, world, player):
|
||||
|
||||
|
||||
def cnt_avail_small_locations_by_ctr(free_locations, counter, layout, world, player):
|
||||
if not world.keyshuffle[player] and not world.retro[player]:
|
||||
if world.keyshuffle[player] == 'none':
|
||||
bk_adj = 1 if counter.big_key_opened and not layout.big_key_special else 0
|
||||
avail_chest_keys = min(free_locations - bk_adj, layout.max_chests)
|
||||
return max(0, avail_chest_keys + len(counter.key_only_locations) - counter.used_keys)
|
||||
@@ -1599,12 +1650,13 @@ 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
|
||||
else:
|
||||
builder = world.dungeon_layouts[player][key_layout.sector.name]
|
||||
state.key_locations = builder.total_keys - builder.key_drop_cnt
|
||||
state.key_locations = max(0, builder.total_keys - builder.key_drop_cnt)
|
||||
state.big_key_special = False
|
||||
for region in key_layout.sector.regions:
|
||||
for location in region.locations:
|
||||
@@ -1865,7 +1917,7 @@ def val_hyrule(key_logic, world, player):
|
||||
val_rule(key_logic.door_rules['Hyrule Dungeon Map Room Key Door S'], 1)
|
||||
val_rule(key_logic.door_rules['Hyrule Dungeon Armory Interior Key Door N'], 2)
|
||||
val_rule(key_logic.door_rules['Sewers Dark Cross Key Door N'], 3)
|
||||
val_rule(key_logic.door_rules['Sewers Key Rat Key Door N'], 4)
|
||||
val_rule(key_logic.door_rules['Sewers Key Rat NE'], 4)
|
||||
else:
|
||||
val_rule(key_logic.door_rules['Sewers Secret Room Key Door S'], 2)
|
||||
val_rule(key_logic.door_rules['Sewers Dark Cross Key Door N'], 2)
|
||||
@@ -2016,14 +2068,14 @@ def val_rule(rule, skn, allow=False, loc=None, askn=None, setCheck=None):
|
||||
|
||||
# Soft lock stuff
|
||||
def validate_key_placement(key_layout, world, player):
|
||||
if world.retro[player] or world.accessibility[player] == 'none':
|
||||
if world.keyshuffle[player] == 'universal' or world.accessibility[player] == 'none':
|
||||
return True # Can't keylock in retro. Expected if beatable only.
|
||||
max_counter = find_max_counter(key_layout)
|
||||
keys_outside = 0
|
||||
big_key_outside = False
|
||||
smallkey_name = dungeon_keys[key_layout.sector.name]
|
||||
bigkey_name = dungeon_bigs[key_layout.sector.name]
|
||||
if world.keyshuffle[player]:
|
||||
if world.keyshuffle[player] != 'none':
|
||||
keys_outside = key_layout.max_chests - sum(1 for i in max_counter.free_locations if i.item is not None and i.item.name == smallkey_name and i.item.player == player)
|
||||
if world.bigkeyshuffle[player]:
|
||||
max_counter = find_max_counter(key_layout)
|
||||
|
||||
Reference in New Issue
Block a user