Fixed key door candidate finder to stay within own dungeon Standard mode support added Added missed Pre-moldorm chest Started work on new key logic analyzer
199 lines
7.6 KiB
Python
199 lines
7.6 KiB
Python
import collections
|
|
|
|
from Regions import dungeon_events
|
|
from DungeonGenerator import ExplorationState
|
|
|
|
|
|
class KeyRegion(object):
|
|
|
|
def __init__(self):
|
|
self.access_doors = set()
|
|
self.free_locations = []
|
|
self.prize_region = False
|
|
self.key_only_locations = []
|
|
self.child_doors = set()
|
|
self.bk_locked = False
|
|
self.parent_region = None
|
|
|
|
def __eq__(self, other):
|
|
if self.prize_region != other.prize_region:
|
|
return False
|
|
if self.bk_locked != other.bk_locked:
|
|
return False
|
|
if len(self.free_locations) != len(other.free_locations):
|
|
return False
|
|
if len(self.key_only_locations) != len(other.key_only_locations):
|
|
return False
|
|
if len(set(self.free_locations).difference(set(other.free_locations))) > 0:
|
|
return False
|
|
if len(set(self.key_only_locations).difference(set(other.key_only_locations))) > 0:
|
|
return False
|
|
if not self.check_child_dest(self.child_doors, other.child_doors, other.access_doors):
|
|
return False
|
|
if not self.check_child_dest(other.child_doors, self.child_doors, self.access_doors):
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def check_child_dest(child_doors, other_child, other_access):
|
|
for child in child_doors:
|
|
if child in other_child:
|
|
continue
|
|
else:
|
|
found = False
|
|
for access in other_access:
|
|
if access.dest == child:
|
|
found = True
|
|
break
|
|
if not found:
|
|
return False
|
|
return True
|
|
|
|
# def issubset(self, other):
|
|
# if self.prize_region != other.prize_region:
|
|
# return False
|
|
# if self.bk_locked != other.bk_locked:
|
|
# return False
|
|
# if not set(self.free_locations).issubset(set(other.free_locations)):
|
|
# return False
|
|
# if not set(self.key_only_locations).issubset(set(other.key_only_locations)):
|
|
# return False
|
|
# if not set(self.child_doors).issubset(set(other.child_doors)):
|
|
# return False
|
|
# return True
|
|
#
|
|
# def issuperset(self, other):
|
|
# if self.prize_region != other.prize_region:
|
|
# return False
|
|
# if self.bk_locked != other.bk_locked:
|
|
# return False
|
|
# if not set(self.free_locations).issuperset(set(other.free_locations)):
|
|
# return False
|
|
# if not set(self.key_only_locations).issuperset(set(other.key_only_locations)):
|
|
# return False
|
|
# if not set(self.child_doors).issuperset(set(other.child_doors)):
|
|
# return False
|
|
# return True
|
|
|
|
|
|
def analyze_dungeon(key_layout, world, player):
|
|
flat_proposal = flatten_pair_list(key_layout.proposal)
|
|
key_regions = create_key_regions(key_layout, flat_proposal, world, player)
|
|
start = key_regions['Origin']
|
|
|
|
|
|
def create_key_regions(key_layout, flat_proposal, world, player):
|
|
key_regions = {}
|
|
state = ExplorationState()
|
|
state.key_locations = len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
|
state.big_key_special = world.get_region('Hyrule Dungeon Cellblock', player) in key_layout.sector.regions
|
|
for region in key_layout.start_regions:
|
|
state.visit_region(region, key_checks=True)
|
|
state.add_all_doors_check_keys(region, flat_proposal, world, player)
|
|
expand_key_state(state, flat_proposal, world, player)
|
|
key_regions['Origin'] = create_key_region(state, None, None)
|
|
queue = collections.deque([(key_regions['Origin'], state)])
|
|
while len(queue) > 0:
|
|
next_key_region, parent_state = queue.popleft()
|
|
for door in next_key_region.child_doors:
|
|
child_state = parent_state.copy()
|
|
# open the door
|
|
open_a_door(door, child_state, flat_proposal)
|
|
expand_key_state(child_state, flat_proposal, world, player)
|
|
child_kr = create_key_region(child_state, next_key_region, door)
|
|
check_for_duplicates_sub_super_set(key_regions, child_kr, door.name)
|
|
queue.append((child_kr, child_state))
|
|
return key_regions
|
|
|
|
|
|
def check_for_duplicates_sub_super_set(key_regions, new_kr, door_name):
|
|
is_new = True
|
|
for kr in key_regions.values():
|
|
if new_kr == kr:
|
|
kr.access_doors.update(new_kr.access_doors)
|
|
kr.child_doors.update(new_kr.child_doors)
|
|
key_regions[door_name] = kr
|
|
is_new = False
|
|
break
|
|
# if new_kr.issubset(kr):
|
|
# break
|
|
# if new_kr.issuperset(kr):
|
|
# break
|
|
if is_new:
|
|
key_regions[door_name] = new_kr
|
|
|
|
|
|
def create_key_region(state, parent_region, door):
|
|
key_region = KeyRegion()
|
|
key_region.parent_region = parent_region
|
|
p_region = parent_region
|
|
parent_doors = set()
|
|
parent_locations = set()
|
|
while p_region is not None:
|
|
parent_doors.update(p_region.child_doors)
|
|
parent_locations.update(p_region.free_locations+p_region.key_only_locations)
|
|
p_region = p_region.parent_region
|
|
u_doors = unique_doors(state.small_doors+state.big_doors).difference(parent_doors)
|
|
key_region.child_doors.update(u_doors)
|
|
region_locations = set(state.found_locations).difference(parent_locations)
|
|
for loc in region_locations:
|
|
if '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']:
|
|
key_region.prize_region = True
|
|
elif loc.event and 'Small Key' in loc.item.name:
|
|
key_region.key_only_locations.append(loc)
|
|
elif loc.name not in dungeon_events:
|
|
key_region.free_locations.append(loc)
|
|
key_region.bk_locked = state.big_key_opened
|
|
if door is not None:
|
|
key_region.access_doors.add(door)
|
|
return key_region
|
|
|
|
|
|
def open_a_door(door, child_state, flat_proposal):
|
|
if door.bigKey:
|
|
child_state.big_key_opened = True
|
|
child_state.avail_doors.extend(child_state.big_doors)
|
|
child_state.opened_doors.extend(set([d.door for d in child_state.big_doors]))
|
|
child_state.big_doors.clear()
|
|
else:
|
|
child_state.opened_doors.append(door)
|
|
doors_to_open = [x for x in child_state.small_doors if x.door == door]
|
|
child_state.small_doors[:] = [x for x in child_state.small_doors if x.door != door]
|
|
child_state.avail_doors.extend(doors_to_open)
|
|
dest_door = door.dest
|
|
if dest_door in flat_proposal:
|
|
child_state.opened_doors.append(dest_door)
|
|
if child_state.in_door_list_ic(dest_door, child_state.small_doors):
|
|
now_available = [x for x in child_state.small_doors if x.door == dest_door]
|
|
child_state.small_doors[:] = [x for x in child_state.small_doors if x.door != dest_door]
|
|
child_state.avail_doors.extend(now_available)
|
|
|
|
|
|
def unique_doors(doors):
|
|
unique_d_set = set()
|
|
for d in doors:
|
|
if d.door not in unique_d_set:
|
|
unique_d_set.add(d.door)
|
|
return unique_d_set
|
|
|
|
|
|
def expand_key_state(state, flat_proposal, world, player):
|
|
while len(state.avail_doors) > 0:
|
|
exp_door = state.next_avail_door()
|
|
door = exp_door.door
|
|
connect_region = world.get_entrance(door.name, player).connected_region
|
|
if state.validate(door, connect_region, world):
|
|
state.visit_region(connect_region, key_checks=True)
|
|
state.add_all_doors_check_keys(connect_region, flat_proposal, world, player)
|
|
|
|
|
|
def flatten_pair_list(paired_list):
|
|
flat_list = []
|
|
for d in paired_list:
|
|
if type(d) is tuple:
|
|
flat_list.append(d[0])
|
|
flat_list.append(d[1])
|
|
else:
|
|
flat_list.append(d)
|
|
return flat_list
|