Std+Retro: key logic for escape and exclude some bow logic sectors
Bug with reachable doors
This commit is contained in:
@@ -482,8 +482,6 @@ class CollectionState(object):
|
|||||||
self.reached_doors = {player: set() for player in range(1, parent.players + 1)}
|
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.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.dungeons_to_check = {player: defaultdict(dict) for player in range(1, parent.players + 1)}
|
||||||
|
|
||||||
self.ghost_keys = Counter()
|
|
||||||
self.dungeon_limits = None
|
self.dungeon_limits = None
|
||||||
# self.trace = None
|
# self.trace = None
|
||||||
|
|
||||||
@@ -551,9 +549,9 @@ class CollectionState(object):
|
|||||||
if (new_entrance, new_crystal_state) not in queue:
|
if (new_entrance, new_crystal_state) not in queue:
|
||||||
queue.append((new_entrance, new_crystal_state))
|
queue.append((new_entrance, new_crystal_state))
|
||||||
# else those connections that are not accessible yet
|
# 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
|
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]
|
key_logic = self.world.key_logic[player][dungeon_name]
|
||||||
if door.name not in self.reached_doors[player]:
|
if door.name not in self.reached_doors[player]:
|
||||||
self.door_counter[player][0][dungeon_name] += 1
|
self.door_counter[player][0][dungeon_name] += 1
|
||||||
@@ -561,7 +559,8 @@ class CollectionState(object):
|
|||||||
if key_logic.sm_doors[door]:
|
if key_logic.sm_doors[door]:
|
||||||
self.reached_doors[player].add(key_logic.sm_doors[door].name)
|
self.reached_doors[player].add(key_logic.sm_doors[door].name)
|
||||||
if not connection.can_reach(self):
|
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)
|
checklist[connection.name] = (connection, crystal_state)
|
||||||
elif door.name not in self.opened_doors[player]:
|
elif door.name not in self.opened_doors[player]:
|
||||||
opened_doors = self.opened_doors[player]
|
opened_doors = self.opened_doors[player]
|
||||||
@@ -639,7 +638,7 @@ class CollectionState(object):
|
|||||||
while not done:
|
while not done:
|
||||||
rrp_ = child_state.reachable_regions[player]
|
rrp_ = child_state.reachable_regions[player]
|
||||||
bc_ = child_state.blocked_connections[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)
|
child_state.traverse_world(child_queue, rrp_, bc_, player)
|
||||||
new_events = child_state.sweep_for_events_once()
|
new_events = child_state.sweep_for_events_once()
|
||||||
child_state.stale[player] = False
|
child_state.stale[player] = False
|
||||||
@@ -674,13 +673,19 @@ class CollectionState(object):
|
|||||||
|
|
||||||
terminal_queue = deque()
|
terminal_queue = deque()
|
||||||
for door in common_doors:
|
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)
|
self.opened_doors[player].add(door)
|
||||||
if door in checklist:
|
if door in checklist:
|
||||||
terminal_queue.append(checklist[door])
|
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.door_counter[player][1][dungeon_name] += 1
|
||||||
|
|
||||||
self.dungeon_limits = [dungeon_name]
|
self.set_dungeon_limits(player, dungeon_name)
|
||||||
rrp_ = self.reachable_regions[player]
|
rrp_ = self.reachable_regions[player]
|
||||||
bc_ = self.blocked_connections[player]
|
bc_ = self.blocked_connections[player]
|
||||||
for block, crystal in bc_.items():
|
for block, crystal in bc_.items():
|
||||||
@@ -722,14 +727,20 @@ class CollectionState(object):
|
|||||||
return paired_door.name if paired_door else None
|
return paired_door.name if paired_door else None
|
||||||
return 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
|
@staticmethod
|
||||||
def should_explore_child_state(state, dungeon_name, player):
|
def should_explore_child_state(state, dungeon_name, player):
|
||||||
small_key_name = dungeon_keys[dungeon_name] # todo: universal
|
small_key_name = dungeon_keys[dungeon_name]
|
||||||
key_total = state.prog_items[(small_key_name, player)] + state.ghost_keys[(small_key_name, player)]
|
key_total = state.prog_items[(small_key_name, player)]
|
||||||
remaining_keys = key_total - state.door_counter[player][1][dungeon_name]
|
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]
|
unopened_doors = state.door_counter[player][0][dungeon_name] - state.door_counter[player][1][dungeon_name]
|
||||||
if remaining_keys > 0 and unopened_doors > 0:
|
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()
|
door_candidates, skip = [], set()
|
||||||
for door, paired in key_logic.sm_doors.items():
|
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]:
|
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)
|
player: defaultdict(dict, {name: copy.copy(checklist)
|
||||||
for name, checklist in self.dungeons_to_check[player].items()})
|
for name, checklist in self.dungeons_to_check[player].items()})
|
||||||
for player in range(1, self.world.players + 1)}
|
for player in range(1, self.world.players + 1)}
|
||||||
ret.ghost_keys = self.ghost_keys.copy()
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def apply_dungeon_exploration(self, rrp, player, dungeon_name, checklist):
|
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]
|
common_doors, missing_regions, missing_bc, paths = ec[dungeon_name][exp_key]
|
||||||
terminal_queue = deque()
|
terminal_queue = deque()
|
||||||
for door in common_doors:
|
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)
|
self.opened_doors[player].add(door)
|
||||||
if door in checklist:
|
if door in checklist:
|
||||||
terminal_queue.append(checklist[door])
|
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.door_counter[player][1][dungeon_name] += 1
|
||||||
|
|
||||||
self.dungeon_limits = [dungeon_name]
|
self.set_dungeon_limits(player, dungeon_name)
|
||||||
rrp_ = self.reachable_regions[player]
|
rrp_ = self.reachable_regions[player]
|
||||||
bc_ = self.blocked_connections[player]
|
bc_ = self.blocked_connections[player]
|
||||||
for block, crystal in bc_.items():
|
for block, crystal in bc_.items():
|
||||||
@@ -1812,6 +1828,7 @@ class Sector(object):
|
|||||||
self.entrance_sector = None
|
self.entrance_sector = None
|
||||||
self.destination_entrance = False
|
self.destination_entrance = False
|
||||||
self.equations = None
|
self.equations = None
|
||||||
|
self.item_logic = set()
|
||||||
|
|
||||||
def region_set(self):
|
def region_set(self):
|
||||||
if self.r_name_set is None:
|
if self.r_name_set is None:
|
||||||
@@ -2552,7 +2569,8 @@ dungeon_keys = {
|
|||||||
'Ice Palace': 'Small Key (Ice Palace)',
|
'Ice Palace': 'Small Key (Ice Palace)',
|
||||||
'Misery Mire': 'Small Key (Misery Mire)',
|
'Misery Mire': 'Small Key (Misery Mire)',
|
||||||
'Turtle Rock': 'Small Key (Turtle Rock)',
|
'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):
|
class PotItem(FastEnum):
|
||||||
|
|||||||
@@ -1101,9 +1101,9 @@ def assign_cross_keys(dungeon_builders, world, player):
|
|||||||
logger.debug('Cross Dungeon: Keys unable to assign in pool %s', remaining)
|
logger.debug('Cross Dungeon: Keys unable to assign in pool %s', remaining)
|
||||||
|
|
||||||
# Last Step: Adjust Small Key Dungeon Pool
|
# Last Step: Adjust Small Key Dungeon Pool
|
||||||
if not world.retro[player]:
|
for name, builder in dungeon_builders.items():
|
||||||
for name, builder in dungeon_builders.items():
|
reassign_key_doors(builder, world, player)
|
||||||
reassign_key_doors(builder, world, player)
|
if not world.retro[player]:
|
||||||
log_key_logic(builder.name, world.key_logic[player][builder.name])
|
log_key_logic(builder.name, world.key_logic[player][builder.name])
|
||||||
actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0)
|
actual_chest_keys = max(builder.key_doors_num - builder.key_drop_cnt, 0)
|
||||||
dungeon = world.get_dungeon(name, player)
|
dungeon = world.get_dungeon(name, player)
|
||||||
|
|||||||
@@ -1290,13 +1290,16 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
|||||||
sanc_builder = random.choice(lw_builders)
|
sanc_builder = random.choice(lw_builders)
|
||||||
assign_sector(sanc, sanc_builder, candidate_sectors, global_pole)
|
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 = {}
|
free_location_sectors = {}
|
||||||
crystal_switches = {}
|
crystal_switches = {}
|
||||||
crystal_barriers = {}
|
crystal_barriers = {}
|
||||||
polarized_sectors = {}
|
polarized_sectors = {}
|
||||||
neutral_sectors = {}
|
neutral_sectors = {}
|
||||||
for sector in candidate_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
|
free_location_sectors[sector] = None
|
||||||
elif sector.c_switch:
|
elif sector.c_switch:
|
||||||
crystal_switches[sector] = None
|
crystal_switches[sector] = None
|
||||||
@@ -1306,6 +1309,8 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player,
|
|||||||
neutral_sectors[sector] = None
|
neutral_sectors[sector] = None
|
||||||
else:
|
else:
|
||||||
polarized_sectors[sector] = None
|
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)
|
assign_location_sectors(dungeon_map, free_location_sectors, global_pole)
|
||||||
leftover = assign_crystal_switch_sectors(dungeon_map, crystal_switches, crystal_barriers, 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)
|
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
|
sector.blue_barrier = True
|
||||||
if door.bigKey:
|
if door.bigKey:
|
||||||
sector.bk_required = True
|
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):
|
def assign_sector(sector, dungeon, candidate_sectors, global_pole):
|
||||||
@@ -1521,6 +1529,19 @@ def find_sector(r_name, sectors):
|
|||||||
return None
|
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):
|
def assign_location_sectors(dungeon_map, free_location_sectors, global_pole):
|
||||||
valid = False
|
valid = False
|
||||||
choices = None
|
choices = None
|
||||||
|
|||||||
Reference in New Issue
Block a user