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:
@@ -79,14 +79,24 @@ def generate_dungeon_main(builder, entrance_region_names, split_dungeon, world,
|
|||||||
dungeon_cache = {}
|
dungeon_cache = {}
|
||||||
backtrack = False
|
backtrack = False
|
||||||
itr = 0
|
itr = 0
|
||||||
|
attempt = 1
|
||||||
finished = False
|
finished = False
|
||||||
# flag if standard and this is hyrule castle
|
# flag if standard and this is hyrule castle
|
||||||
std_flag = world.mode[player] == 'standard' and bk_special
|
std_flag = world.mode[player] == 'standard' and bk_special
|
||||||
while not finished:
|
while not finished:
|
||||||
# what are my choices?
|
# what are my choices?
|
||||||
itr += 1
|
itr += 1
|
||||||
if itr > 5000:
|
if itr > 1000:
|
||||||
raise Exception('Generation taking too long. Ref %s' % name)
|
if attempt > 9:
|
||||||
|
raise Exception('Generation taking too long. Ref %s' % name)
|
||||||
|
proposed_map = {}
|
||||||
|
choices_master = [[]]
|
||||||
|
depth = 0
|
||||||
|
dungeon_cache = {}
|
||||||
|
backtrack = False
|
||||||
|
itr = 0
|
||||||
|
attempt += 1
|
||||||
|
logger.debug(f'Starting new attempt {attempt}')
|
||||||
if depth not in dungeon_cache.keys():
|
if depth not in dungeon_cache.keys():
|
||||||
dungeon, hangers, hooks = gen_dungeon_info(name, builder.sectors, entrance_regions, proposed_map,
|
dungeon, hangers, hooks = gen_dungeon_info(name, builder.sectors, entrance_regions, proposed_map,
|
||||||
doors_to_connect, bk_needed, bk_special, world, player)
|
doors_to_connect, bk_needed, bk_special, world, player)
|
||||||
@@ -158,6 +168,7 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, proposed_map, va
|
|||||||
dungeon = {}
|
dungeon = {}
|
||||||
start = ExplorationState(dungeon=name)
|
start = ExplorationState(dungeon=name)
|
||||||
start.big_key_special = bk_special
|
start.big_key_special = bk_special
|
||||||
|
group_flags, door_map = find_bk_groups(name, available_sectors, proposed_map, bk_special)
|
||||||
|
|
||||||
def exception(d):
|
def exception(d):
|
||||||
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
||||||
@@ -176,16 +187,17 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, proposed_map, va
|
|||||||
for door in sector.outstanding_doors:
|
for door in sector.outstanding_doors:
|
||||||
if not door.stonewall and door not in proposed_map.keys():
|
if not door.stonewall and door not in proposed_map.keys():
|
||||||
hanger_set.add(door)
|
hanger_set.add(door)
|
||||||
|
bk_flag = group_flags[door_map[door]]
|
||||||
parent = door.entrance.parent_region
|
parent = door.entrance.parent_region
|
||||||
crystal_start = CrystalBarrier.Either if parent.crystal_switch else init_crystal
|
crystal_start = CrystalBarrier.Either if parent.crystal_switch else init_crystal
|
||||||
init_state = ExplorationState(crystal_start, dungeon=name)
|
init_state = ExplorationState(crystal_start, dungeon=name)
|
||||||
init_state.big_key_special = start.big_key_special
|
init_state.big_key_special = start.big_key_special
|
||||||
o_state = extend_reachable_state_improved([parent], init_state, proposed_map,
|
o_state = extend_reachable_state_improved([parent], init_state, proposed_map,
|
||||||
valid_doors, False, world, player, exception)
|
valid_doors, bk_flag, world, player, exception)
|
||||||
o_state_cache[door.name] = o_state
|
o_state_cache[door.name] = o_state
|
||||||
piece = create_graph_piece_from_state(door, o_state, o_state, proposed_map, exception)
|
piece = create_graph_piece_from_state(door, o_state, o_state, proposed_map, exception)
|
||||||
dungeon[door.name] = piece
|
dungeon[door.name] = piece
|
||||||
check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, valid_doors, world, player, exception)
|
check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, valid_doors, group_flags, door_map, world, player, exception)
|
||||||
|
|
||||||
# catalog hooks: Dict<Hook, List<Door, Crystal, Door>>
|
# catalog hooks: Dict<Hook, List<Door, Crystal, Door>>
|
||||||
# and hangers: Dict<Hang, List<Door>>
|
# and hangers: Dict<Hang, List<Door>>
|
||||||
@@ -205,7 +217,43 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, proposed_map, va
|
|||||||
return dungeon, hangers, avail_hooks
|
return dungeon, hangers, avail_hooks
|
||||||
|
|
||||||
|
|
||||||
def check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, valid_doors, world, player, exception):
|
def find_bk_groups(name, available_sectors, proposed_map, bk_special):
|
||||||
|
groups = {}
|
||||||
|
door_ids = {}
|
||||||
|
gid = 1
|
||||||
|
for sector in available_sectors:
|
||||||
|
if bk_special:
|
||||||
|
my_gid = None
|
||||||
|
for door in sector.outstanding_doors:
|
||||||
|
if door in proposed_map and proposed_map[door] in door_ids:
|
||||||
|
if my_gid:
|
||||||
|
merge_gid = door_ids[proposed_map[door]]
|
||||||
|
for door in door_ids.keys():
|
||||||
|
if door_ids[door] == merge_gid:
|
||||||
|
door_ids[door] = my_gid
|
||||||
|
groups[my_gid] = groups[my_gid] or groups[merge_gid]
|
||||||
|
else:
|
||||||
|
my_gid = door_ids[proposed_map[door]]
|
||||||
|
if not my_gid:
|
||||||
|
my_gid = gid
|
||||||
|
gid += 1
|
||||||
|
for door in sector.outstanding_doors:
|
||||||
|
door_ids[door] = my_gid
|
||||||
|
if my_gid not in groups.keys():
|
||||||
|
groups[my_gid] = False
|
||||||
|
for region in sector.regions:
|
||||||
|
for loc in region.locations:
|
||||||
|
if loc.forced_item and loc.item.bigkey and name in loc.item.name:
|
||||||
|
groups[my_gid] = True
|
||||||
|
else:
|
||||||
|
for door in sector.outstanding_doors:
|
||||||
|
door_ids[door] = gid
|
||||||
|
groups[gid] = False
|
||||||
|
return groups, door_ids
|
||||||
|
|
||||||
|
|
||||||
|
def check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, valid_doors, group_flags, door_map,
|
||||||
|
world, player, exception):
|
||||||
not_blue = set()
|
not_blue = set()
|
||||||
not_blue.update(hanger_set)
|
not_blue.update(hanger_set)
|
||||||
doors_to_check = set()
|
doors_to_check = set()
|
||||||
@@ -233,17 +281,18 @@ def check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, valid_do
|
|||||||
hang_type = hanger_from_door(door) # am I hangable on a hook?
|
hang_type = hanger_from_door(door) # am I hangable on a hook?
|
||||||
hook_type = hook_from_door(door) # am I hookable onto a hanger?
|
hook_type = hook_from_door(door) # am I hookable onto a hanger?
|
||||||
if (hang_type in blue_hooks and not door.stonewall) or hook_type in blue_hangers:
|
if (hang_type in blue_hooks and not door.stonewall) or hook_type in blue_hangers:
|
||||||
explore_blue_state(door, dungeon, o_state_cache[door.name], proposed_map, valid_doors,
|
bk_flag = group_flags[door_map[door]]
|
||||||
|
explore_blue_state(door, dungeon, o_state_cache[door.name], proposed_map, valid_doors, bk_flag,
|
||||||
world, player, exception)
|
world, player, exception)
|
||||||
doors_to_check.add(door)
|
doors_to_check.add(door)
|
||||||
not_blue.difference_update(doors_to_check)
|
not_blue.difference_update(doors_to_check)
|
||||||
|
|
||||||
|
|
||||||
def explore_blue_state(door, dungeon, o_state, proposed_map, valid_doors, world, player, exception):
|
def explore_blue_state(door, dungeon, o_state, proposed_map, valid_doors, bk_flag, world, player, exception):
|
||||||
parent = door.entrance.parent_region
|
parent = door.entrance.parent_region
|
||||||
blue_start = ExplorationState(CrystalBarrier.Blue, o_state.dungeon)
|
blue_start = ExplorationState(CrystalBarrier.Blue, o_state.dungeon)
|
||||||
blue_start.big_key_special = o_state.big_key_special
|
blue_start.big_key_special = o_state.big_key_special
|
||||||
b_state = extend_reachable_state_improved([parent], blue_start, proposed_map, valid_doors, False,
|
b_state = extend_reachable_state_improved([parent], blue_start, proposed_map, valid_doors, bk_flag,
|
||||||
world, player, exception)
|
world, player, exception)
|
||||||
dungeon[door.name] = create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception)
|
dungeon[door.name] = create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception)
|
||||||
|
|
||||||
@@ -915,7 +964,7 @@ def extend_reachable_state(search_regions, state, world, player):
|
|||||||
return local_state
|
return local_state
|
||||||
|
|
||||||
|
|
||||||
def extend_reachable_state_improved(search_regions, state, proposed_map, valid_doors, isOrigin, world, player, exception):
|
def extend_reachable_state_improved(search_regions, state, proposed_map, valid_doors, bk_flag, world, player, exception):
|
||||||
local_state = state.copy()
|
local_state = state.copy()
|
||||||
for region in search_regions:
|
for region in search_regions:
|
||||||
local_state.visit_region(region)
|
local_state.visit_region(region)
|
||||||
@@ -923,7 +972,7 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, valid_d
|
|||||||
while len(local_state.avail_doors) > 0:
|
while len(local_state.avail_doors) > 0:
|
||||||
explorable_door = local_state.next_avail_door()
|
explorable_door = local_state.next_avail_door()
|
||||||
if explorable_door.door.bigKey:
|
if explorable_door.door.bigKey:
|
||||||
if isOrigin:
|
if bk_flag:
|
||||||
big_not_found = not special_big_key_found(local_state, world, player) if local_state.big_key_special else local_state.count_locations_exclude_specials() == 0
|
big_not_found = not special_big_key_found(local_state, world, player) if local_state.big_key_special else local_state.count_locations_exclude_specials() == 0
|
||||||
if big_not_found:
|
if big_not_found:
|
||||||
continue # we can't open this door
|
continue # we can't open this door
|
||||||
|
|||||||
2
Fill.py
2
Fill.py
@@ -226,7 +226,7 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool =
|
|||||||
|
|
||||||
|
|
||||||
def valid_key_placement(item, location, itempool, world):
|
def valid_key_placement(item, location, itempool, world):
|
||||||
if (not item.smallkey and not item.bigkey) or item.player != location.player or world.retro[item.player]:
|
if (not item.smallkey and not item.bigkey) or item.player != location.player or world.retro[item.player] or world.logic[item.player] == 'nologic':
|
||||||
return True
|
return True
|
||||||
dungeon = location.parent_region.dungeon
|
dungeon = location.parent_region.dungeon
|
||||||
if dungeon:
|
if dungeon:
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ class KeyLogic(object):
|
|||||||
|
|
||||||
def __init__(self, dungeon_name):
|
def __init__(self, dungeon_name):
|
||||||
self.door_rules = {}
|
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.sm_restricted = set()
|
||||||
self.small_key_name = dungeon_keys[dungeon_name]
|
self.small_key_name = dungeon_keys[dungeon_name]
|
||||||
self.bk_name = dungeon_bigs[dungeon_name]
|
self.bk_name = dungeon_bigs[dungeon_name]
|
||||||
@@ -50,7 +51,9 @@ class KeyLogic(object):
|
|||||||
self.logic_min = {}
|
self.logic_min = {}
|
||||||
self.logic_max = {}
|
self.logic_max = {}
|
||||||
self.placement_rules = []
|
self.placement_rules = []
|
||||||
|
self.location_rules = {}
|
||||||
self.outside_keys = 0
|
self.outside_keys = 0
|
||||||
|
self.dungeon = dungeon_name
|
||||||
|
|
||||||
def check_placement(self, unplaced_keys, big_key_loc=None):
|
def check_placement(self, unplaced_keys, big_key_loc=None):
|
||||||
for rule in self.placement_rules:
|
for rule in self.placement_rules:
|
||||||
@@ -77,6 +80,18 @@ class DoorRules(object):
|
|||||||
self.opposite = None
|
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):
|
class PlacementRule(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -88,6 +103,7 @@ class PlacementRule(object):
|
|||||||
self.check_locations_w_bk = None
|
self.check_locations_w_bk = None
|
||||||
self.check_locations_wo_bk = None
|
self.check_locations_wo_bk = None
|
||||||
self.bk_relevant = True
|
self.bk_relevant = True
|
||||||
|
self.key_reduced = False
|
||||||
|
|
||||||
def contradicts(self, rule, unplaced_keys, big_key_loc):
|
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
|
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)
|
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_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':
|
if world.retro[player] and world.mode[player] != 'standard':
|
||||||
return
|
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))
|
rule.check_locations_wo_bk = set(filter_big_chest(accessible_loc))
|
||||||
if valid_rule:
|
if valid_rule:
|
||||||
key_logic.placement_rules.append(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_placement_rules(key_layout, max_ctr)
|
||||||
|
refine_location_rules(key_layout)
|
||||||
|
|
||||||
|
|
||||||
def placement_self_lock_adjustment(rule, max_ctr, blocked_loc, ctr, world, player):
|
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
|
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):
|
def refine_placement_rules(key_layout, max_ctr):
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
changed = True
|
changed = True
|
||||||
@@ -407,6 +457,20 @@ def refine_placement_rules(key_layout, max_ctr):
|
|||||||
removed_rules[r2] = r1
|
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):
|
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
|
key_logic = key_layout.key_logic
|
||||||
rule = PlacementRule()
|
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
|
rule.check_locations_w_bk = accessible_loc
|
||||||
check_sm_restriction_needed(key_layout, max_ctr, rule, blocked_loc)
|
check_sm_restriction_needed(key_layout, max_ctr, rule, blocked_loc)
|
||||||
key_logic.placement_rules.append(rule)
|
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):
|
def queue_sorter(queue_item):
|
||||||
@@ -438,12 +503,10 @@ def queue_sorter_2(queue_item):
|
|||||||
|
|
||||||
|
|
||||||
def find_bk_locked_sections(key_layout, world, player):
|
def find_bk_locked_sections(key_layout, world, player):
|
||||||
if key_layout.big_key_special:
|
|
||||||
return
|
|
||||||
key_counters = key_layout.key_counters
|
key_counters = key_layout.key_counters
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
|
|
||||||
bk_key_not_required = set()
|
bk_not_required = set()
|
||||||
big_chest_allowed_big_key = world.accessibility[player] != 'locations'
|
big_chest_allowed_big_key = world.accessibility[player] != 'locations'
|
||||||
for counter in key_counters.values():
|
for counter in key_counters.values():
|
||||||
key_layout.all_chest_locations.update(counter.free_locations)
|
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:
|
if counter.big_key_opened and counter.important_location:
|
||||||
big_chest_allowed_big_key = False
|
big_chest_allowed_big_key = False
|
||||||
if not counter.big_key_opened:
|
if not counter.big_key_opened:
|
||||||
bk_key_not_required.update(counter.free_locations)
|
bk_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.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:
|
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):
|
def empty_counter(counter):
|
||||||
@@ -936,6 +1008,14 @@ def find_big_chest_locations(locations):
|
|||||||
return ret
|
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):
|
def expand_key_state(state, flat_proposal, world, player):
|
||||||
while len(state.avail_doors) > 0:
|
while len(state.avail_doors) > 0:
|
||||||
exp_door = state.next_avail_door()
|
exp_door = state.next_avail_door()
|
||||||
@@ -1149,7 +1229,7 @@ def set_paired_rules(key_logic, world, player):
|
|||||||
# Soft lock stuff
|
# Soft lock stuff
|
||||||
def validate_key_layout(key_layout, world, player):
|
def validate_key_layout(key_layout, world, player):
|
||||||
# retro is all good - except for hyrule castle in standard mode
|
# 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
|
return True
|
||||||
flat_proposal = key_layout.flat_prop
|
flat_proposal = key_layout.flat_prop
|
||||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
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):
|
if important_location(loc, world, player):
|
||||||
key_counter.important_location = True
|
key_counter.important_location = True
|
||||||
key_counter.other_locations[loc] = None
|
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
|
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:
|
elif loc.name not in dungeon_events:
|
||||||
key_counter.free_locations[loc] = None
|
key_counter.free_locations[loc] = None
|
||||||
else:
|
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))
|
key_counter.big_key_opened = state.visited(world.get_region('Hyrule Dungeon Cellblock', player))
|
||||||
else:
|
else:
|
||||||
key_counter.big_key_opened = state.big_key_opened
|
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
|
return key_counter
|
||||||
|
|
||||||
|
|
||||||
@@ -1322,13 +1399,14 @@ def imp_locations_factory(world, player):
|
|||||||
if imp_locations:
|
if imp_locations:
|
||||||
return imp_locations
|
return imp_locations
|
||||||
imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
|
imp_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
|
||||||
if world.mode[player] == 'standard' or world.doorShuffle[player] == 'crossed':
|
if world.mode[player] == 'standard':
|
||||||
imp_locations.append('Hyrule Dungeon Cellblock')
|
imp_locations.append('Zelda Pickup')
|
||||||
|
imp_locations.append('Zelda Dropoff')
|
||||||
return imp_locations
|
return imp_locations
|
||||||
|
|
||||||
|
|
||||||
def important_location(loc, world, player):
|
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):
|
def create_odd_key_counter(door, parent_counter, key_layout, world, player):
|
||||||
|
|||||||
25
Main.py
25
Main.py
@@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
|
|||||||
from ItemList import generate_itempool, difficulties, fill_prizes
|
from ItemList import generate_itempool, difficulties, fill_prizes
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
__version__ = '0.1.0.1-u'
|
__version__ = '0.1.0.2-u'
|
||||||
|
|
||||||
class EnemizerError(RuntimeError):
|
class EnemizerError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
@@ -145,17 +145,18 @@ def main(args, seed=None, fish=None):
|
|||||||
fill_dungeons(world)
|
fill_dungeons(world)
|
||||||
|
|
||||||
for player in range(1, world.players+1):
|
for player in range(1, world.players+1):
|
||||||
for key_layout in world.key_layout[player].values():
|
if world.logic[player] != 'nologic':
|
||||||
if not validate_key_placement(key_layout, world, player):
|
for key_layout in world.key_layout[player].values():
|
||||||
raise RuntimeError(
|
if not validate_key_placement(key_layout, world, player):
|
||||||
"%s: %s (%s %d)" %
|
raise RuntimeError(
|
||||||
(
|
"%s: %s (%s %d)" %
|
||||||
world.fish.translate("cli","cli","keylock.detected"),
|
(
|
||||||
key_layout.sector.name,
|
world.fish.translate("cli", "cli", "keylock.detected"),
|
||||||
world.fish.translate("cli","cli","player"),
|
key_layout.sector.name,
|
||||||
player
|
world.fish.translate("cli", "cli", "player"),
|
||||||
)
|
player
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(world.fish.translate("cli","cli","fill.world"))
|
logger.info(world.fish.translate("cli","cli","fill.world"))
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
* Crossed Dungeon generation improvements
|
* Crossed Dungeon generation improvements
|
||||||
* Fix for Animated Tiles in crossed dungeon
|
* Fix for Animated Tiles in crossed dungeon
|
||||||
* Stonewall hardlock no longer reachable from certain drops (Sewer Drop, some Skull Woods drops) that were previously possible
|
* Stonewall hardlock no longer reachable from certain drops (Sewer Drop, some Skull Woods drops) that were previously possible
|
||||||
|
* No logic uses less key door logic
|
||||||
|
|
||||||
##### In Progress
|
##### In Progress
|
||||||
|
|
||||||
* ~~TT Attic Hint tile should have a crystal switch accessible now~~
|
* TT Attic Hint tile should have a crystal switch accessible now
|
||||||
|
* Different key logic rules in development
|
||||||
@@ -177,7 +177,7 @@ def create_regions(world, player):
|
|||||||
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
|
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
|
||||||
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
|
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
|
||||||
create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)',
|
create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)',
|
||||||
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
|
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
|
||||||
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
|
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
|
||||||
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
|
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
|
||||||
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
|
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
|
||||||
|
|||||||
Reference in New Issue
Block a user