Fix for last ditch problems
Special bk adjustments Exception for self locking key doors in key lock checker
This commit is contained in:
@@ -432,7 +432,7 @@ class World(object):
|
|||||||
else:
|
else:
|
||||||
return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1)))
|
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 starting_state:
|
||||||
if self.has_beaten_game(starting_state):
|
if self.has_beaten_game(starting_state):
|
||||||
return True
|
return True
|
||||||
@@ -456,6 +456,9 @@ class World(object):
|
|||||||
|
|
||||||
if not sphere:
|
if not sphere:
|
||||||
# ran out of places and did not finish yet, quit
|
# 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
|
return False
|
||||||
|
|
||||||
for location in sphere:
|
for location in sphere:
|
||||||
|
|||||||
8
Fill.py
8
Fill.py
@@ -295,12 +295,14 @@ def last_ditch_placement(item_to_place, locations, world, state, base_state, ite
|
|||||||
if swap_spot:
|
if swap_spot:
|
||||||
logging.getLogger('').debug(f'Swapping {old_item} for {item_to_place}')
|
logging.getLogger('').debug(f'Swapping {old_item} for {item_to_place}')
|
||||||
world.push_item(swap_spot, old_item, False)
|
world.push_item(swap_spot, old_item, False)
|
||||||
|
swap_spot.event = True
|
||||||
locations.remove(swap_spot)
|
locations.remove(swap_spot)
|
||||||
locations.append(new_spot)
|
locations.append(new_spot)
|
||||||
return new_spot
|
return new_spot
|
||||||
else:
|
else:
|
||||||
new_spot.item = restore_item
|
new_spot.item = restore_item
|
||||||
|
else:
|
||||||
|
location.item = old_item
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -315,10 +317,12 @@ def find_spot_for_item(item_to_place, locations, world, base_state, pool,
|
|||||||
for location in locations:
|
for location in locations:
|
||||||
maximum_exploration_state = sweep_from_pool()
|
maximum_exploration_state = sweep_from_pool()
|
||||||
perform_access_check = True
|
perform_access_check = True
|
||||||
|
old_item = None
|
||||||
if world.accessibility[item_to_place.player] == '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)
|
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
|
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
|
location.item = item_to_place
|
||||||
test_state = maximum_exploration_state.copy()
|
test_state = maximum_exploration_state.copy()
|
||||||
test_state.stale[item_to_place.player] = True
|
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):
|
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
|
return location
|
||||||
if item_to_place.smallkey or item_to_place.bigkey:
|
if item_to_place.smallkey or item_to_place.bigkey:
|
||||||
location.item = None
|
location.item = old_item
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ class PlacementRule(object):
|
|||||||
self.needed_keys_w_bk = None
|
self.needed_keys_w_bk = None
|
||||||
self.needed_keys_wo_bk = None
|
self.needed_keys_wo_bk = None
|
||||||
self.check_locations_w_bk = None
|
self.check_locations_w_bk = None
|
||||||
|
self.special_bk_avail = False
|
||||||
self.check_locations_wo_bk = None
|
self.check_locations_wo_bk = None
|
||||||
self.bk_relevant = True
|
self.bk_relevant = True
|
||||||
self.key_reduced = False
|
self.key_reduced = False
|
||||||
@@ -164,7 +165,10 @@ class PlacementRule(object):
|
|||||||
def loc_has_bk(l):
|
def loc_has_bk(l):
|
||||||
return (big_key_loc is not None and big_key_loc == l) or (l.item and l.item.bigkey)
|
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:
|
if not bk_found:
|
||||||
return True
|
return True
|
||||||
check_locations = self.check_locations_wo_bk if bk_blocked else self.check_locations_w_bk
|
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
|
return
|
||||||
|
|
||||||
original_key_counter = find_counter({}, False, key_layout, False)
|
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)])
|
queue = deque([(None, original_key_counter)])
|
||||||
doors_completed = set()
|
doors_completed = set()
|
||||||
visited_cid = set()
|
visited_cid = set()
|
||||||
@@ -340,6 +346,8 @@ def create_exhaustive_placement_rules(key_layout, world, player):
|
|||||||
else:
|
else:
|
||||||
placement_self_lock_adjustment(rule, max_ctr, blocked_loc, key_counter, world, player)
|
placement_self_lock_adjustment(rule, max_ctr, blocked_loc, key_counter, world, player)
|
||||||
rule.check_locations_w_bk = accessible_loc
|
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)
|
# check_sm_restriction_needed(key_layout, max_ctr, rule, blocked_loc)
|
||||||
else:
|
else:
|
||||||
if big_key_progress(key_counter) and only_sm_doors(key_counter):
|
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
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def forced_big_key_avail(locations):
|
||||||
|
for loc in locations:
|
||||||
|
if loc.forced_big_key():
|
||||||
|
return loc
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# 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
|
||||||
@@ -1962,6 +1977,7 @@ def validate_key_placement(key_layout, world, player):
|
|||||||
found_prize = False
|
found_prize = False
|
||||||
can_progress = (not counter.big_key_opened and big_found and any(d.bigKey for d in counter.child_doors)) or \
|
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 \
|
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)
|
(key_layout.prize_relevant and not counter.prize_doors_opened and found_prize)
|
||||||
if not can_progress:
|
if not can_progress:
|
||||||
missing_locations = set(max_counter.free_locations.keys()).difference(found_locations)
|
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
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -244,7 +244,7 @@ def main(args, seed=None, fish=None):
|
|||||||
balance_multiworld_progression(world)
|
balance_multiworld_progression(world)
|
||||||
|
|
||||||
# if we only check for beatable, we can do this sanity check first before creating the rom
|
# 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"))
|
raise RuntimeError(world.fish.translate("cli","cli","cannot.beat.game"))
|
||||||
|
|
||||||
for player in range(1, world.players+1):
|
for player in range(1, world.players+1):
|
||||||
|
|||||||
Reference in New Issue
Block a user