Std+Retro: key logic for escape and exclude some bow logic sectors

Bug with reachable doors
This commit is contained in:
aerinon
2021-07-15 08:03:33 -07:00
parent 9ef24a610d
commit b56f8d4136
3 changed files with 58 additions and 19 deletions

View File

@@ -482,8 +482,6 @@ class CollectionState(object):
self.reached_doors = {player: set() for player in range(1, parent.players + 1)}
self.opened_doors = {player: set() for player in range(1, parent.players + 1)}
self.dungeons_to_check = {player: defaultdict(dict) for player in range(1, parent.players + 1)}
self.ghost_keys = Counter()
self.dungeon_limits = None
# self.trace = None
@@ -551,9 +549,9 @@ class CollectionState(object):
if (new_entrance, new_crystal_state) not in queue:
queue.append((new_entrance, new_crystal_state))
# else those connections that are not accessible yet
if self.is_small_door(connection) and not self.world.retro[player]: # todo: retro
if self.is_small_door(connection):
door = connection.door
dungeon_name = connection.parent_region.dungeon.name # todo: universal
dungeon_name = connection.parent_region.dungeon.name
key_logic = self.world.key_logic[player][dungeon_name]
if door.name not in self.reached_doors[player]:
self.door_counter[player][0][dungeon_name] += 1
@@ -561,7 +559,8 @@ class CollectionState(object):
if key_logic.sm_doors[door]:
self.reached_doors[player].add(key_logic.sm_doors[door].name)
if not connection.can_reach(self):
checklist = self.dungeons_to_check[player][dungeon_name]
checklist_key = 'Universal' if self.world.retro[player] else dungeon_name
checklist = self.dungeons_to_check[player][checklist_key]
checklist[connection.name] = (connection, crystal_state)
elif door.name not in self.opened_doors[player]:
opened_doors = self.opened_doors[player]
@@ -639,7 +638,7 @@ class CollectionState(object):
while not done:
rrp_ = child_state.reachable_regions[player]
bc_ = child_state.blocked_connections[player]
self.dungeon_limits = [dungeon_name]
self.set_dungeon_limits(player, dungeon_name)
child_state.traverse_world(child_queue, rrp_, bc_, player)
new_events = child_state.sweep_for_events_once()
child_state.stale[player] = False
@@ -674,13 +673,19 @@ class CollectionState(object):
terminal_queue = deque()
for door in common_doors:
pair = self.find_door_pair(player, dungeon_name, door)
if door not in self.reached_doors[player]:
self.door_counter[player][0][dungeon_name] += 1
self.reached_doors[player].add(door)
if pair not in self.reached_doors[player]:
self.reached_doors[player].add(pair)
self.opened_doors[player].add(door)
if door in checklist:
terminal_queue.append(checklist[door])
if self.find_door_pair(player, dungeon_name, door) not in self.opened_doors[player]:
if pair not in self.opened_doors[player]:
self.door_counter[player][1][dungeon_name] += 1
self.dungeon_limits = [dungeon_name]
self.set_dungeon_limits(player, dungeon_name)
rrp_ = self.reachable_regions[player]
bc_ = self.blocked_connections[player]
for block, crystal in bc_.items():
@@ -722,14 +727,20 @@ class CollectionState(object):
return paired_door.name if paired_door else None
return None
def set_dungeon_limits(self, player, dungeon_name):
if self.world.retro[player] and self.world.mode[player] == 'standard':
self.dungeon_limits = ['Hyrule Castle', 'Agahnims Tower']
else:
self.dungeon_limits = [dungeon_name]
@staticmethod
def should_explore_child_state(state, dungeon_name, player):
small_key_name = dungeon_keys[dungeon_name] # todo: universal
key_total = state.prog_items[(small_key_name, player)] + state.ghost_keys[(small_key_name, player)]
small_key_name = dungeon_keys[dungeon_name]
key_total = state.prog_items[(small_key_name, player)]
remaining_keys = key_total - state.door_counter[player][1][dungeon_name]
unopened_doors = state.door_counter[player][0][dungeon_name] - state.door_counter[player][1][dungeon_name]
if remaining_keys > 0 and unopened_doors > 0:
key_logic = state.world.key_logic[player][dungeon_name] # todo: universal
key_logic = state.world.key_logic[player][dungeon_name]
door_candidates, skip = [], set()
for door, paired in key_logic.sm_doors.items():
if door.name in state.reached_doors[player] and door.name not in state.opened_doors[player]:
@@ -770,7 +781,6 @@ class CollectionState(object):
player: defaultdict(dict, {name: copy.copy(checklist)
for name, checklist in self.dungeons_to_check[player].items()})
for player in range(1, self.world.players + 1)}
ret.ghost_keys = self.ghost_keys.copy()
return ret
def apply_dungeon_exploration(self, rrp, player, dungeon_name, checklist):
@@ -783,13 +793,19 @@ class CollectionState(object):
common_doors, missing_regions, missing_bc, paths = ec[dungeon_name][exp_key]
terminal_queue = deque()
for door in common_doors:
pair = self.find_door_pair(player, dungeon_name, door)
if door not in self.reached_doors[player]:
self.door_counter[player][0][dungeon_name] += 1
self.reached_doors[player].add(door)
if pair not in self.reached_doors[player]:
self.reached_doors[player].add(pair)
self.opened_doors[player].add(door)
if door in checklist:
terminal_queue.append(checklist[door])
if self.find_door_pair(player, dungeon_name, door) not in self.opened_doors[player]:
if pair not in self.opened_doors[player]:
self.door_counter[player][1][dungeon_name] += 1
self.dungeon_limits = [dungeon_name]
self.set_dungeon_limits(player, dungeon_name)
rrp_ = self.reachable_regions[player]
bc_ = self.blocked_connections[player]
for block, crystal in bc_.items():
@@ -1812,6 +1828,7 @@ class Sector(object):
self.entrance_sector = None
self.destination_entrance = False
self.equations = None
self.item_logic = set()
def region_set(self):
if self.r_name_set is None:
@@ -2552,7 +2569,8 @@ dungeon_keys = {
'Ice Palace': 'Small Key (Ice Palace)',
'Misery Mire': 'Small Key (Misery Mire)',
'Turtle Rock': 'Small Key (Turtle Rock)',
'Ganons Tower': 'Small Key (Ganons Tower)'
'Ganons Tower': 'Small Key (Ganons Tower)',
'Universal': 'Small Key (Universal)'
}
class PotItem(FastEnum):

View File

@@ -1101,9 +1101,9 @@ def assign_cross_keys(dungeon_builders, world, player):
logger.debug('Cross Dungeon: Keys unable to assign in pool %s', remaining)
# Last Step: Adjust Small Key Dungeon Pool
if not world.retro[player]:
for name, builder in dungeon_builders.items():
reassign_key_doors(builder, world, player)
for name, builder in dungeon_builders.items():
reassign_key_doors(builder, world, player)
if not world.retro[player]:
log_key_logic(builder.name, world.key_logic[player][builder.name])
actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0)
dungeon = world.get_dungeon(name, player)

View File

@@ -1290,13 +1290,16 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
sanc_builder = random.choice(lw_builders)
assign_sector(sanc, sanc_builder, candidate_sectors, global_pole)
bow_sectors, retro_std_flag = {}, world.retro[player] and world.mode[player] == 'standard'
free_location_sectors = {}
crystal_switches = {}
crystal_barriers = {}
polarized_sectors = {}
neutral_sectors = {}
for sector in candidate_sectors:
if sector.chest_locations > 0:
if retro_std_flag and 'Bow' in sector.item_logic: # these need to be distributed outside of HC
bow_sectors[sector] = None
elif sector.chest_locations > 0:
free_location_sectors[sector] = None
elif sector.c_switch:
crystal_switches[sector] = None
@@ -1306,6 +1309,8 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
neutral_sectors[sector] = None
else:
polarized_sectors[sector] = None
if bow_sectors:
assign_bow_sectors(dungeon_map, bow_sectors, global_pole)
assign_location_sectors(dungeon_map, free_location_sectors, global_pole)
leftover = assign_crystal_switch_sectors(dungeon_map, crystal_switches, crystal_barriers, global_pole)
ensure_crystal_switches_reachable(dungeon_map, leftover, polarized_sectors, crystal_barriers, global_pole)
@@ -1471,6 +1476,9 @@ def define_sector_features(sectors):
sector.blue_barrier = True
if door.bigKey:
sector.bk_required = True
if region.name in ['PoD Mimics 2', 'PoD Bow Statue Right', 'PoD Mimics 1', 'GT Mimics 1', 'GT Mimics 2',
'Eastern Single Eyegore', 'Eastern Duo Eyegores']:
sector.item_logic.add('Bow')
def assign_sector(sector, dungeon, candidate_sectors, global_pole):
@@ -1521,6 +1529,19 @@ def find_sector(r_name, sectors):
return None
def assign_bow_sectors(dungeon_map, bow_sectors, global_pole):
sector_list = list(bow_sectors)
random.shuffle(sector_list)
population = []
for name in dungeon_map:
if name != 'Hyrule Castle':
population.append(name)
choices = random.choices(population, k=len(sector_list))
for i, choice in enumerate(choices):
builder = dungeon_map[choice]
assign_sector(sector_list[i], builder, bow_sectors, global_pole)
def assign_location_sectors(dungeon_map, free_location_sectors, global_pole):
valid = False
choices = None