Fix for last ditch problems

Special bk adjustments
Exception for self locking key doors in key lock checker
This commit is contained in:
aerinon
2021-08-16 15:28:20 -06:00
parent 54004118f5
commit b53a005545
4 changed files with 36 additions and 5 deletions

View File

@@ -432,7 +432,7 @@ class World(object):
else:
return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1)))
def can_beat_game(self, starting_state=None):
def can_beat_game(self, starting_state=None, log_error=False):
if starting_state:
if self.has_beaten_game(starting_state):
return True
@@ -456,6 +456,9 @@ class World(object):
if not sphere:
# ran out of places and did not finish yet, quit
if log_error:
missing_locations = ", ".join([x.name for x in prog_locations])
logging.getLogger('').error(f'Cannot reach the following locations: {missing_locations}')
return False
for location in sphere:

View File

@@ -295,12 +295,14 @@ def last_ditch_placement(item_to_place, locations, world, state, base_state, ite
if swap_spot:
logging.getLogger('').debug(f'Swapping {old_item} for {item_to_place}')
world.push_item(swap_spot, old_item, False)
swap_spot.event = True
locations.remove(swap_spot)
locations.append(new_spot)
return new_spot
else:
new_spot.item = restore_item
else:
location.item = old_item
return None
@@ -315,10 +317,12 @@ def find_spot_for_item(item_to_place, locations, world, base_state, pool,
for location in locations:
maximum_exploration_state = sweep_from_pool()
perform_access_check = True
old_item = None
if world.accessibility[item_to_place.player] == 'none':
perform_access_check = not world.has_beaten_game(maximum_exploration_state, item_to_place.player) if single_player_placement else not world.has_beaten_game(maximum_exploration_state)
if item_to_place.smallkey or item_to_place.bigkey: # a better test to see if a key can go there
old_item = location.item
location.item = item_to_place
test_state = maximum_exploration_state.copy()
test_state.stale[item_to_place.player] = True
@@ -329,7 +333,7 @@ def find_spot_for_item(item_to_place, locations, world, base_state, pool,
and valid_key_placement(item_to_place, location, pool if (keys_in_itempool and keys_in_itempool[item_to_place.player]) else world.itempool, world):
return location
if item_to_place.smallkey or item_to_place.bigkey:
location.item = None
location.item = old_item
return None

View File

@@ -116,6 +116,7 @@ class PlacementRule(object):
self.needed_keys_w_bk = None
self.needed_keys_wo_bk = None
self.check_locations_w_bk = None
self.special_bk_avail = False
self.check_locations_wo_bk = None
self.bk_relevant = True
self.key_reduced = False
@@ -164,7 +165,10 @@ class PlacementRule(object):
def loc_has_bk(l):
return (big_key_loc is not None and big_key_loc == l) or (l.item and l.item.bigkey)
bk_found = any(loc for loc in self.check_locations_w_bk if loc_has_bk(loc))
# todo: sometimes the bk avail rule doesn't mean the bk must be avail or this rule is invalid
# but sometimes it certainly does
# check threshold vs len(check_loc) maybe to determine bk isn't relevant?
bk_found = self.special_bk_avail or any(loc for loc in self.check_locations_w_bk if loc_has_bk(loc))
if not bk_found:
return True
check_locations = self.check_locations_wo_bk if bk_blocked else self.check_locations_w_bk
@@ -258,6 +262,8 @@ def analyze_dungeon(key_layout, world, player):
return
original_key_counter = find_counter({}, False, key_layout, False)
if key_layout.big_key_special and forced_big_key_avail(original_key_counter.other_locations) is not None:
original_key_counter = find_counter({}, True, key_layout, False)
queue = deque([(None, original_key_counter)])
doors_completed = set()
visited_cid = set()
@@ -340,6 +346,8 @@ def create_exhaustive_placement_rules(key_layout, world, player):
else:
placement_self_lock_adjustment(rule, max_ctr, blocked_loc, key_counter, world, player)
rule.check_locations_w_bk = accessible_loc
if key_layout.big_key_special:
rule.special_bk_avail = forced_big_key_avail(key_counter.important_locations) is not None
# check_sm_restriction_needed(key_layout, max_ctr, rule, blocked_loc)
else:
if big_key_progress(key_counter) and only_sm_doors(key_counter):
@@ -1359,6 +1367,13 @@ def check_bk_special(regions, world, player):
return False
def forced_big_key_avail(locations):
for loc in locations:
if loc.forced_big_key():
return loc
return None
# Soft lock stuff
def validate_key_layout(key_layout, world, player):
# retro is all good - except for hyrule castle in standard mode
@@ -1962,6 +1977,7 @@ def validate_key_placement(key_layout, world, player):
found_prize = False
can_progress = (not counter.big_key_opened and big_found and any(d.bigKey for d in counter.child_doors)) or \
found_keys > counter.used_keys and any(not d.bigKey for d in counter.child_doors) or \
self_locked_child_door(key_layout, counter) or \
(key_layout.prize_relevant and not counter.prize_doors_opened and found_prize)
if not can_progress:
missing_locations = set(max_counter.free_locations.keys()).difference(found_locations)
@@ -1976,3 +1992,11 @@ def validate_key_placement(key_layout, world, player):
return True
def self_locked_child_door(key_layout, counter):
if len(counter.child_doors) == 1:
door = next(iter(counter.child_doors.keys()))
return door.smallKey and key_layout.key_logic.door_rules[door.name].allow_small
return False

View File

@@ -244,7 +244,7 @@ def main(args, seed=None, fish=None):
balance_multiworld_progression(world)
# if we only check for beatable, we can do this sanity check first before creating the rom
if not world.can_beat_game():
if not world.can_beat_game(log_error=True):
raise RuntimeError(world.fish.translate("cli","cli","cannot.beat.game"))
for player in range(1, world.players+1):