Some generation improvements (bk checked better, backtrack added re-attempts)
No logic skips more key logic Some prototyping new key rules
This commit is contained in:
@@ -41,7 +41,8 @@ class KeyLogic(object):
|
||||
|
||||
def __init__(self, dungeon_name):
|
||||
self.door_rules = {}
|
||||
self.bk_restricted = set()
|
||||
self.bk_restricted = set() # subset of free locations
|
||||
self.bk_locked = set() # includes potentially other locations and key only locations
|
||||
self.sm_restricted = set()
|
||||
self.small_key_name = dungeon_keys[dungeon_name]
|
||||
self.bk_name = dungeon_bigs[dungeon_name]
|
||||
@@ -50,7 +51,9 @@ class KeyLogic(object):
|
||||
self.logic_min = {}
|
||||
self.logic_max = {}
|
||||
self.placement_rules = []
|
||||
self.location_rules = {}
|
||||
self.outside_keys = 0
|
||||
self.dungeon = dungeon_name
|
||||
|
||||
def check_placement(self, unplaced_keys, big_key_loc=None):
|
||||
for rule in self.placement_rules:
|
||||
@@ -77,6 +80,18 @@ class DoorRules(object):
|
||||
self.opposite = None
|
||||
|
||||
|
||||
class LocationRule(object):
|
||||
def __init__(self):
|
||||
self.small_key_num = 0
|
||||
self.conditional_sets = []
|
||||
|
||||
|
||||
class ConditionalLocationRule(object):
|
||||
def __init__(self, conditional_set):
|
||||
self.conditional_set = conditional_set
|
||||
self.small_key_num = 0
|
||||
|
||||
|
||||
class PlacementRule(object):
|
||||
|
||||
def __init__(self):
|
||||
@@ -88,6 +103,7 @@ class PlacementRule(object):
|
||||
self.check_locations_w_bk = None
|
||||
self.check_locations_wo_bk = None
|
||||
self.bk_relevant = True
|
||||
self.key_reduced = False
|
||||
|
||||
def contradicts(self, rule, unplaced_keys, big_key_loc):
|
||||
bk_blocked = big_key_loc in self.bk_conditional_set if self.bk_conditional_set else False
|
||||
@@ -208,6 +224,7 @@ def analyze_dungeon(key_layout, world, player):
|
||||
|
||||
find_bk_locked_sections(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))
|
||||
if world.retro[player] and world.mode[player] != 'standard':
|
||||
return
|
||||
|
||||
@@ -297,7 +314,9 @@ def create_exhaustive_placement_rules(key_layout, world, player):
|
||||
rule.check_locations_wo_bk = set(filter_big_chest(accessible_loc))
|
||||
if valid_rule:
|
||||
key_logic.placement_rules.append(rule)
|
||||
adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr)
|
||||
refine_placement_rules(key_layout, max_ctr)
|
||||
refine_location_rules(key_layout)
|
||||
|
||||
|
||||
def placement_self_lock_adjustment(rule, max_ctr, blocked_loc, ctr, world, player):
|
||||
@@ -319,6 +338,37 @@ def check_sm_restriction_needed(key_layout, max_ctr, rule, blocked):
|
||||
return False
|
||||
|
||||
|
||||
def adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr):
|
||||
if rule.bk_conditional_set:
|
||||
test_set = (rule.bk_conditional_set - key_logic.bk_locked) - set(max_ctr.key_only_locations.keys())
|
||||
needed = rule.needed_keys_wo_bk if test_set else 0
|
||||
else:
|
||||
test_set = None
|
||||
needed = rule.needed_keys_w_bk
|
||||
if needed > 0:
|
||||
accessible_loc.update(key_counter.other_locations)
|
||||
blocked_loc = key_layout.all_locations-accessible_loc
|
||||
for location in blocked_loc:
|
||||
if location not in key_logic.location_rules.keys():
|
||||
loc_rule = LocationRule()
|
||||
key_logic.location_rules[location] = loc_rule
|
||||
else:
|
||||
loc_rule = key_logic.location_rules[location]
|
||||
if test_set:
|
||||
if location not in key_logic.bk_locked:
|
||||
cond_rule = None
|
||||
for other in loc_rule.conditional_sets:
|
||||
if other.conditional_set == test_set:
|
||||
cond_rule = other
|
||||
break
|
||||
if not cond_rule:
|
||||
cond_rule = ConditionalLocationRule(test_set)
|
||||
loc_rule.conditional_sets.append(cond_rule)
|
||||
cond_rule.small_key_num = max(needed, cond_rule.small_key_num)
|
||||
else:
|
||||
loc_rule.small_key_num = max(needed, loc_rule.small_key_num)
|
||||
|
||||
|
||||
def refine_placement_rules(key_layout, max_ctr):
|
||||
key_logic = key_layout.key_logic
|
||||
changed = True
|
||||
@@ -407,6 +457,20 @@ def refine_placement_rules(key_layout, max_ctr):
|
||||
removed_rules[r2] = r1
|
||||
|
||||
|
||||
def refine_location_rules(key_layout):
|
||||
locs_to_remove = []
|
||||
for loc, rule in key_layout.key_logic.location_rules.items():
|
||||
conditions_to_remove = []
|
||||
for cond_rule in rule.conditional_sets:
|
||||
if cond_rule.small_key_num <= rule.small_key_num:
|
||||
conditions_to_remove.append(cond_rule)
|
||||
rule.conditional_sets = [x for x in rule.conditional_sets if x not in conditions_to_remove]
|
||||
if rule.small_key_num == 0 and len(rule.conditional_sets) == 0:
|
||||
locs_to_remove.append(loc)
|
||||
for loc in locs_to_remove:
|
||||
del key_layout.key_logic.location_rules[loc]
|
||||
|
||||
|
||||
def create_inclusive_rule(key_layout, max_ctr, code, key_counter, blocked_loc, accessible_loc, min_keys, world, player):
|
||||
key_logic = key_layout.key_logic
|
||||
rule = PlacementRule()
|
||||
@@ -421,6 +485,7 @@ def create_inclusive_rule(key_layout, max_ctr, code, key_counter, blocked_loc, a
|
||||
rule.check_locations_w_bk = accessible_loc
|
||||
check_sm_restriction_needed(key_layout, max_ctr, rule, blocked_loc)
|
||||
key_logic.placement_rules.append(rule)
|
||||
adjust_locations_rules(key_logic, rule, accessible_loc, key_layout, key_counter, max_ctr)
|
||||
|
||||
|
||||
def queue_sorter(queue_item):
|
||||
@@ -438,12 +503,10 @@ def queue_sorter_2(queue_item):
|
||||
|
||||
|
||||
def find_bk_locked_sections(key_layout, world, player):
|
||||
if key_layout.big_key_special:
|
||||
return
|
||||
key_counters = key_layout.key_counters
|
||||
key_logic = key_layout.key_logic
|
||||
|
||||
bk_key_not_required = set()
|
||||
bk_not_required = set()
|
||||
big_chest_allowed_big_key = world.accessibility[player] != 'locations'
|
||||
for counter in key_counters.values():
|
||||
key_layout.all_chest_locations.update(counter.free_locations)
|
||||
@@ -452,10 +515,19 @@ def find_bk_locked_sections(key_layout, world, player):
|
||||
if counter.big_key_opened and counter.important_location:
|
||||
big_chest_allowed_big_key = False
|
||||
if not counter.big_key_opened:
|
||||
bk_key_not_required.update(counter.free_locations)
|
||||
key_logic.bk_restricted.update(dict.fromkeys(set(key_layout.all_chest_locations).difference(bk_key_not_required)))
|
||||
bk_not_required.update(counter.free_locations)
|
||||
bk_not_required.update(counter.key_only_locations)
|
||||
bk_not_required.update(counter.other_locations)
|
||||
# todo?: handle bk special differently in cross dungeon
|
||||
# notably: things behind bk doors - relying on the bk door logic atm
|
||||
if not key_layout.big_key_special:
|
||||
key_logic.bk_restricted.update(dict.fromkeys(set(key_layout.all_chest_locations).difference(bk_not_required)))
|
||||
key_logic.bk_locked.update(dict.fromkeys(set(key_layout.all_locations) - bk_not_required))
|
||||
if not big_chest_allowed_big_key:
|
||||
key_logic.bk_restricted.update(find_big_chest_locations(key_layout.all_chest_locations))
|
||||
bk_required_locations = find_big_chest_locations(key_layout.all_chest_locations)
|
||||
bk_required_locations += find_big_key_locked_locations(key_layout.all_chest_locations)
|
||||
key_logic.bk_restricted.update(bk_required_locations)
|
||||
key_logic.bk_locked.update(bk_required_locations)
|
||||
|
||||
|
||||
def empty_counter(counter):
|
||||
@@ -936,6 +1008,14 @@ def find_big_chest_locations(locations):
|
||||
return ret
|
||||
|
||||
|
||||
def find_big_key_locked_locations(locations):
|
||||
ret = []
|
||||
for loc in locations:
|
||||
if loc.name in ["Thieves' Town - Blind's Cell", "Hyrule Castle - Zelda's Chest"]:
|
||||
ret.append(loc)
|
||||
return ret
|
||||
|
||||
|
||||
def expand_key_state(state, flat_proposal, world, player):
|
||||
while len(state.avail_doors) > 0:
|
||||
exp_door = state.next_avail_door()
|
||||
@@ -1149,7 +1229,7 @@ def set_paired_rules(key_logic, world, player):
|
||||
# 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'):
|
||||
if (world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')) or world.logic[player] == 'nologic':
|
||||
return True
|
||||
flat_proposal = key_layout.flat_prop
|
||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
||||
@@ -1294,8 +1374,10 @@ def create_key_counter(state, key_layout, world, player):
|
||||
if important_location(loc, world, player):
|
||||
key_counter.important_location = True
|
||||
key_counter.other_locations[loc] = None
|
||||
elif loc.event and 'Small Key' in loc.item.name:
|
||||
elif loc.forced_item and loc.item.name == key_layout.key_logic.small_key_name:
|
||||
key_counter.key_only_locations[loc] = None
|
||||
elif loc.forced_item and loc.item.name == key_layout.key_logic.bk_name:
|
||||
key_counter.other_locations[loc] = None
|
||||
elif loc.name not in dungeon_events:
|
||||
key_counter.free_locations[loc] = None
|
||||
else:
|
||||
@@ -1306,11 +1388,6 @@ def create_key_counter(state, key_layout, world, player):
|
||||
key_counter.big_key_opened = state.visited(world.get_region('Hyrule Dungeon Cellblock', player))
|
||||
else:
|
||||
key_counter.big_key_opened = state.big_key_opened
|
||||
# if soft_lock_check:
|
||||
# avail_chests = available_chest_small_keys(key_counter, key_counter.big_key_opened, world)
|
||||
# avail_keys = avail_chests + len(key_counter.key_only_locations)
|
||||
# if avail_keys <= key_counter.used_keys and avail_keys < key_layout.max_chests + key_layout.max_drops:
|
||||
# raise SoftLockException()
|
||||
return key_counter
|
||||
|
||||
|
||||
@@ -1322,13 +1399,14 @@ def imp_locations_factory(world, player):
|
||||
if imp_locations:
|
||||
return imp_locations
|
||||
imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
|
||||
if world.mode[player] == 'standard' or world.doorShuffle[player] == 'crossed':
|
||||
imp_locations.append('Hyrule Dungeon Cellblock')
|
||||
if world.mode[player] == 'standard':
|
||||
imp_locations.append('Zelda Pickup')
|
||||
imp_locations.append('Zelda Dropoff')
|
||||
return imp_locations
|
||||
|
||||
|
||||
def important_location(loc, world, player):
|
||||
return '- Prize' in loc.name or loc.name in imp_locations_factory(world, player)
|
||||
return '- Prize' in loc.name or loc.name in imp_locations_factory(world, player) or (loc.forced_item is not None and loc.item.bigkey)
|
||||
|
||||
|
||||
def create_odd_key_counter(door, parent_counter, key_layout, world, player):
|
||||
|
||||
Reference in New Issue
Block a user