Couple minor door fixes
New key logic algorithm - some advanced rules in place to allow more locations Changed generation to handle big key placement better
This commit is contained in:
@@ -127,12 +127,9 @@ def vanilla_key_logic(world, player):
|
|||||||
raise Exception('Vanilla key layout not valid %s' % sector.name)
|
raise Exception('Vanilla key layout not valid %s' % sector.name)
|
||||||
if player not in world.key_logic.keys():
|
if player not in world.key_logic.keys():
|
||||||
world.key_logic[player] = {}
|
world.key_logic[player] = {}
|
||||||
if sector.name in ['Agahnims Tower', 'Tower of Hera', 'Desert Palace', 'Eastern Palace']:
|
key_layout_2 = KeyLayout(sector, start_regions, doors)
|
||||||
key_layout_2 = KeyLayout(sector, start_regions, doors)
|
key_layout_2 = analyze_dungeon(key_layout_2, world, player)
|
||||||
key_layout_2 = analyze_dungeon(key_layout_2, world, player)
|
world.key_logic[player][sector.name] = key_layout_2.key_logic
|
||||||
world.key_logic[player][sector.name] = key_layout_2.key_logic
|
|
||||||
else:
|
|
||||||
world.key_logic[player][sector.name] = key_layout.key_logic
|
|
||||||
validate_vanilla_key_logic(world, player)
|
validate_vanilla_key_logic(world, player)
|
||||||
|
|
||||||
|
|
||||||
@@ -297,16 +294,17 @@ def within_dungeon(world, player):
|
|||||||
last_key = None
|
last_key = None
|
||||||
while len(sector_queue) > 0:
|
while len(sector_queue) > 0:
|
||||||
key, sector_list, entrance_list = sector_queue.popleft()
|
key, sector_list, entrance_list = sector_queue.popleft()
|
||||||
|
split_dungeon = key in split_region_starts.keys()
|
||||||
origin_list = list(entrance_list)
|
origin_list = list(entrance_list)
|
||||||
find_enabled_origins(sector_list, enabled_entrances, origin_list, entrances_map, key)
|
find_enabled_origins(sector_list, enabled_entrances, origin_list, entrances_map, key)
|
||||||
origin_list_sans_drops = remove_drop_origins(origin_list)
|
origin_list_sans_drops = remove_drop_origins(origin_list)
|
||||||
if len(origin_list) <= 0:
|
if len(origin_list_sans_drops) <= 0:
|
||||||
if last_key == key:
|
if last_key == key:
|
||||||
raise Exception('Infinte loop detected %s' % key)
|
raise Exception('Infinte loop detected %s' % key)
|
||||||
sector_queue.append((key, sector_list, entrance_list))
|
sector_queue.append((key, sector_list, entrance_list))
|
||||||
last_key = key
|
last_key = key
|
||||||
else:
|
else:
|
||||||
ds = generate_dungeon(sector_list, origin_list_sans_drops, world, player)
|
ds = generate_dungeon(sector_list, origin_list_sans_drops, split_dungeon, world, player)
|
||||||
find_new_entrances(ds, connections, potentials, enabled_entrances, world, player)
|
find_new_entrances(ds, connections, potentials, enabled_entrances, world, player)
|
||||||
ds.name = key
|
ds.name = key
|
||||||
layout_starts = origin_list if len(entrance_list) <= 0 else entrance_list
|
layout_starts = origin_list if len(entrance_list) <= 0 else entrance_list
|
||||||
@@ -340,7 +338,7 @@ def determine_entrance_list(world, player):
|
|||||||
for ent in region.entrances:
|
for ent in region.entrances:
|
||||||
parent = ent.parent_region
|
parent = ent.parent_region
|
||||||
if parent.type != RegionType.Dungeon or parent.name == 'Sewer Drop':
|
if parent.type != RegionType.Dungeon or parent.name == 'Sewer Drop':
|
||||||
if parent.name not in world.inaccessible_regions[player] or drop_exception(region_name):
|
if parent.name not in world.inaccessible_regions[player]:
|
||||||
entrance_map[key].append(region_name)
|
entrance_map[key].append(region_name)
|
||||||
else:
|
else:
|
||||||
if ent.parent_region not in potential_entrances.keys():
|
if ent.parent_region not in potential_entrances.keys():
|
||||||
@@ -366,8 +364,11 @@ def find_enabled_origins(sectors, enabled, entrance_list, entrance_map, key):
|
|||||||
for region in sector.regions:
|
for region in sector.regions:
|
||||||
if region.name in enabled.keys() and region.name not in entrance_list:
|
if region.name in enabled.keys() and region.name not in entrance_list:
|
||||||
entrance_list.append(region.name)
|
entrance_list.append(region.name)
|
||||||
if enabled[region.name] != region.name:
|
origin_reg, origin_dungeon = enabled[region.name]
|
||||||
|
if origin_reg != region.name and origin_dungeon != region.dungeon:
|
||||||
entrance_map[key].append(region.name)
|
entrance_map[key].append(region.name)
|
||||||
|
if drop_exception(region.name): # only because they have unique regions
|
||||||
|
entrance_list.append(region.name)
|
||||||
|
|
||||||
|
|
||||||
def remove_drop_origins(entrance_list):
|
def remove_drop_origins(entrance_list):
|
||||||
@@ -379,7 +380,7 @@ def find_new_entrances(sector, connections, potentials, enabled, world, player):
|
|||||||
if region.name in connections.keys() and connections[region.name] in potentials.keys():
|
if region.name in connections.keys() and connections[region.name] in potentials.keys():
|
||||||
new_region = connections[region.name]
|
new_region = connections[region.name]
|
||||||
for potential in potentials.pop(new_region):
|
for potential in potentials.pop(new_region):
|
||||||
enabled[potential] = region.name
|
enabled[potential] = (region.name, region.dungeon)
|
||||||
# see if this unexplored region connects elsewhere
|
# see if this unexplored region connects elsewhere
|
||||||
queue = collections.deque(new_region.exits)
|
queue = collections.deque(new_region.exits)
|
||||||
visited = set()
|
visited = set()
|
||||||
@@ -389,7 +390,7 @@ def find_new_entrances(sector, connections, potentials, enabled, world, player):
|
|||||||
region_name = ext.connected_region.name
|
region_name = ext.connected_region.name
|
||||||
if region_name in connections.keys() and connections[region_name] in potentials.keys():
|
if region_name in connections.keys() and connections[region_name] in potentials.keys():
|
||||||
for potential in potentials.pop(connections[region_name]):
|
for potential in potentials.pop(connections[region_name]):
|
||||||
enabled[potential] = region.name
|
enabled[potential] = (region.name, region.dungeon)
|
||||||
if ext.connected_region.name in world.inaccessible_regions[player]:
|
if ext.connected_region.name in world.inaccessible_regions[player]:
|
||||||
for new_exit in ext.connected_region.exits:
|
for new_exit in ext.connected_region.exits:
|
||||||
if new_exit not in visited:
|
if new_exit not in visited:
|
||||||
@@ -528,6 +529,7 @@ def find_compatible_door_in_regions(world, door, regions, player):
|
|||||||
return region, proposed_door
|
return region, proposed_door
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
def find_compatible_door_in_list(ugly_regions, world, door, doors, player):
|
def find_compatible_door_in_list(ugly_regions, world, door, doors, player):
|
||||||
if door.type in [DoorType.Hole, DoorType.Warp, DoorType.Logical]:
|
if door.type in [DoorType.Hole, DoorType.Warp, DoorType.Logical]:
|
||||||
return door
|
return door
|
||||||
@@ -877,8 +879,30 @@ def shuffle_key_doors(dungeon_sector, entrances, world, player):
|
|||||||
# make changes
|
# make changes
|
||||||
if player not in world.key_logic.keys():
|
if player not in world.key_logic.keys():
|
||||||
world.key_logic[player] = {}
|
world.key_logic[player] = {}
|
||||||
world.key_logic[player][dungeon_sector.name] = key_layout.key_logic
|
key_layout_new = analyze_dungeon(key_layout, world, player)
|
||||||
reassign_key_doors(current_doors, proposal, world, player)
|
reassign_key_doors(current_doors, proposal, world, player)
|
||||||
|
log_key_logic(dungeon_sector.name, key_layout_new.key_logic)
|
||||||
|
world.key_logic[player][dungeon_sector.name] = key_layout_new.key_logic
|
||||||
|
|
||||||
|
|
||||||
|
def log_key_logic(d_name, key_logic):
|
||||||
|
logger = logging.getLogger('')
|
||||||
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
|
logger.debug('Key Logic for %s', d_name)
|
||||||
|
if len(key_logic.bk_restricted) > 0:
|
||||||
|
logger.debug('-BK Restrictions')
|
||||||
|
for restriction in key_logic.bk_restricted:
|
||||||
|
logger.debug(restriction)
|
||||||
|
if len(key_logic.sm_restricted) > 0:
|
||||||
|
logger.debug('-Small Restrictions')
|
||||||
|
for restriction in key_logic.sm_restricted:
|
||||||
|
logger.debug(restriction)
|
||||||
|
for key in key_logic.door_rules.keys():
|
||||||
|
rule = key_logic.door_rules[key]
|
||||||
|
logger.debug('--Rule for %s: Nrm:%s Allow:%s Loc:%s Alt:%s', key, rule.small_key_num, rule.allow_small, rule.small_location, rule.alternate_small_key)
|
||||||
|
if rule.alternate_small_key is not None:
|
||||||
|
for loc in rule.alternate_big_key_loc:
|
||||||
|
logger.debug('---BK Loc %s', loc.name)
|
||||||
|
|
||||||
|
|
||||||
class KeyLayout(object):
|
class KeyLayout(object):
|
||||||
@@ -1792,7 +1816,7 @@ interior_doors = [
|
|||||||
('Skull East Bridge ES', 'Skull West Bridge Nook WS'),
|
('Skull East Bridge ES', 'Skull West Bridge Nook WS'),
|
||||||
('Skull Star Pits WS', 'Skull Torch Room ES'),
|
('Skull Star Pits WS', 'Skull Torch Room ES'),
|
||||||
('Skull Torch Room EN', 'Skull Vines WN'),
|
('Skull Torch Room EN', 'Skull Vines WN'),
|
||||||
('Skull Spike Corner WS', 'Skull Final Drop ES'),
|
('Skull Spike Corner ES', 'Skull Final Drop WS'),
|
||||||
('Thieves Hallway WS', 'Thieves Pot Alcove Mid ES'),
|
('Thieves Hallway WS', 'Thieves Pot Alcove Mid ES'),
|
||||||
('Thieves Conveyor Maze SW', 'Thieves Pot Alcove Top NW'),
|
('Thieves Conveyor Maze SW', 'Thieves Pot Alcove Top NW'),
|
||||||
('Thieves Conveyor Maze EN', 'Thieves Hallway WN'),
|
('Thieves Conveyor Maze EN', 'Thieves Hallway WN'),
|
||||||
@@ -1943,7 +1967,7 @@ default_small_key_doors = {
|
|||||||
('Skull Map Room SE', 'Skull Pinball NE'),
|
('Skull Map Room SE', 'Skull Pinball NE'),
|
||||||
('Skull 2 West Lobby NW', 'Skull X Room SW'),
|
('Skull 2 West Lobby NW', 'Skull X Room SW'),
|
||||||
('Skull 3 Lobby NW', 'Skull Star Pits SW'),
|
('Skull 3 Lobby NW', 'Skull Star Pits SW'),
|
||||||
('Skull Spike Corner WS', 'Skull Final Drop ES')
|
('Skull Spike Corner ES', 'Skull Final Drop WS')
|
||||||
],
|
],
|
||||||
'Thieves Town': [
|
'Thieves Town': [
|
||||||
('Thieves Hallway WS', 'Thieves Pot Alcove Mid ES'),
|
('Thieves Hallway WS', 'Thieves Pot Alcove Mid ES'),
|
||||||
|
|||||||
8
Doors.py
8
Doors.py
@@ -524,8 +524,8 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'Skull Vines WN', Intr).dir(We, 0x49, Top, High).pos(1),
|
create_door(player, 'Skull Vines WN', Intr).dir(We, 0x49, Top, High).pos(1),
|
||||||
create_door(player, 'Skull Vines NW', Nrml).dir(No, 0x49, Left, High).pos(0),
|
create_door(player, 'Skull Vines NW', Nrml).dir(No, 0x49, Left, High).pos(0),
|
||||||
create_door(player, 'Skull Spike Corner SW', Nrml).dir(So, 0x39, Left, High).no_exit().trap(0x4).pos(0),
|
create_door(player, 'Skull Spike Corner SW', Nrml).dir(So, 0x39, Left, High).no_exit().trap(0x4).pos(0),
|
||||||
create_door(player, 'Skull Spike Corner WS', Intr).dir(We, 0x39, Bot, High).small_key().pos(1),
|
create_door(player, 'Skull Spike Corner ES', Intr).dir(Ea, 0x39, Bot, High).small_key().pos(1),
|
||||||
create_door(player, 'Skull Final Drop ES', Intr).dir(Ea, 0x39, Bot, High).small_key().pos(1),
|
create_door(player, 'Skull Final Drop WS', Intr).dir(We, 0x39, Bot, High).small_key().pos(1),
|
||||||
create_door(player, 'Skull Final Drop Hole', Hole),
|
create_door(player, 'Skull Final Drop Hole', Hole),
|
||||||
|
|
||||||
create_door(player, 'Thieves Lobby N Edge', Open).dir(No, 0xdb, None, Low),
|
create_door(player, 'Thieves Lobby N Edge', Open).dir(No, 0xdb, None, Low),
|
||||||
@@ -984,7 +984,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'GT Four Torches NW', Intr).dir(No, 0x1c, Left, High).pos(2),
|
create_door(player, 'GT Four Torches NW', Intr).dir(No, 0x1c, Left, High).pos(2),
|
||||||
create_door(player, 'GT Fairy Abyss SW', Intr).dir(So, 0x1c, Left, High).pos(2),
|
create_door(player, 'GT Fairy Abyss SW', Intr).dir(So, 0x1c, Left, High).pos(2),
|
||||||
create_door(player, 'GT Four Torches Up Stairs', Sprl).dir(Up, 0x1c, 0, HTH).ss(Z, 0x1b, 0x2c, True, True),
|
create_door(player, 'GT Four Torches Up Stairs', Sprl).dir(Up, 0x1c, 0, HTH).ss(Z, 0x1b, 0x2c, True, True),
|
||||||
create_door(player, 'GT Crystal Paths Down Stairs', Sprl).dir(Dn, 0x6b, 0, HTH).ss(A, 0x12, 0x00, True),
|
create_door(player, 'GT Crystal Paths Down Stairs', Sprl).dir(Dn, 0x6b, 0, HTH).ss(A, 0x12, 0x00, False, True),
|
||||||
create_door(player, 'GT Crystal Paths SW', Intr).dir(So, 0x6b, Left, High).pos(3),
|
create_door(player, 'GT Crystal Paths SW', Intr).dir(So, 0x6b, Left, High).pos(3),
|
||||||
create_door(player, 'GT Mimics 1 NW', Intr).dir(No, 0x6b, Left, High).pos(3),
|
create_door(player, 'GT Mimics 1 NW', Intr).dir(No, 0x6b, Left, High).pos(3),
|
||||||
create_door(player, 'GT Mimics 1 ES', Intr).dir(Ea, 0x6b, Bot, High).pos(2),
|
create_door(player, 'GT Mimics 1 ES', Intr).dir(Ea, 0x6b, Bot, High).pos(2),
|
||||||
@@ -1044,7 +1044,7 @@ def create_doors(world, player):
|
|||||||
create_door(player, 'GT Right Moldorm Ledge Down Stairs', Sprl).dir(Dn, 0x4d, 0, HTH).ss(S, 0x12, 0x80),
|
create_door(player, 'GT Right Moldorm Ledge Down Stairs', Sprl).dir(Dn, 0x4d, 0, HTH).ss(S, 0x12, 0x80),
|
||||||
create_door(player, 'GT Moldorm Pit Up Stairs', Sprl).dir(Up, 0xa6, 0, HTH).ss(S, 0x1b, 0x6c),
|
create_door(player, 'GT Moldorm Pit Up Stairs', Sprl).dir(Up, 0xa6, 0, HTH).ss(S, 0x1b, 0x6c),
|
||||||
create_door(player, 'GT Frozen Over ES', Nrml).dir(Ea, 0x4c, Bot, High).trap(0x4).pos(0),
|
create_door(player, 'GT Frozen Over ES', Nrml).dir(Ea, 0x4c, Bot, High).trap(0x4).pos(0),
|
||||||
create_door(player, 'GT Frozen Over Up Stairs', Sprl).dir(Up, 0x4c, 0, HTH).ss(S, 0x1a, 0x6c, False, True),
|
create_door(player, 'GT Frozen Over Up Stairs', Sprl).dir(Up, 0x4c, 0, HTH).ss(S, 0x1a, 0x6c, True),
|
||||||
create_door(player, 'GT Brightly Lit Hall Down Stairs', Sprl).dir(Dn, 0x1d, 0, HTH).ss(S, 0x11, 0x80, False, True),
|
create_door(player, 'GT Brightly Lit Hall Down Stairs', Sprl).dir(Dn, 0x1d, 0, HTH).ss(S, 0x11, 0x80, False, True),
|
||||||
create_door(player, 'GT Brightly Lit Hall NW', Nrml).dir(No, 0x1d, Left, High).big_key().pos(0),
|
create_door(player, 'GT Brightly Lit Hall NW', Nrml).dir(No, 0x1d, Left, High).big_key().pos(0),
|
||||||
create_door(player, 'GT Agahnim 2 SW', Nrml).dir(So, 0x0d, Left, High).no_exit().trap(0x4).pos(0)
|
create_door(player, 'GT Agahnim 2 SW', Nrml).dir(So, 0x0d, Left, High).no_exit().trap(0x4).pos(0)
|
||||||
|
|||||||
@@ -24,17 +24,20 @@ class GraphPiece:
|
|||||||
self.hanger_crystal = None
|
self.hanger_crystal = None
|
||||||
self.hooks = {}
|
self.hooks = {}
|
||||||
self.visited_regions = set()
|
self.visited_regions = set()
|
||||||
|
self.possible_bk_locations = set()
|
||||||
|
|
||||||
|
|
||||||
def generate_dungeon(available_sectors, entrance_region_names, world, player):
|
def generate_dungeon(available_sectors, entrance_region_names, split_dungeon, world, player):
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
entrance_regions = convert_regions(entrance_region_names, world, player)
|
entrance_regions = convert_regions(entrance_region_names, world, player)
|
||||||
doors_to_connect = set()
|
doors_to_connect = set()
|
||||||
all_regions = set()
|
all_regions = set()
|
||||||
|
bk_needed = False
|
||||||
for sector in available_sectors:
|
for sector in available_sectors:
|
||||||
for door in sector.outstanding_doors:
|
for door in sector.outstanding_doors:
|
||||||
doors_to_connect.add(door)
|
doors_to_connect.add(door)
|
||||||
all_regions.update(sector.regions)
|
all_regions.update(sector.regions)
|
||||||
|
bk_needed = bk_needed or determine_if_bk_needed(sector, split_dungeon, world, player)
|
||||||
proposed_map = {}
|
proposed_map = {}
|
||||||
choices_master = [[]]
|
choices_master = [[]]
|
||||||
depth = 0
|
depth = 0
|
||||||
@@ -51,7 +54,7 @@ def generate_dungeon(available_sectors, entrance_region_names, world, player):
|
|||||||
if depth not in dungeon_cache.keys():
|
if depth not in dungeon_cache.keys():
|
||||||
dungeon, hangers, hooks = gen_dungeon_info(available_sectors, entrance_regions, proposed_map, doors_to_connect, world, player)
|
dungeon, hangers, hooks = gen_dungeon_info(available_sectors, entrance_regions, proposed_map, doors_to_connect, world, player)
|
||||||
dungeon_cache[depth] = dungeon, hangers, hooks
|
dungeon_cache[depth] = dungeon, hangers, hooks
|
||||||
valid = check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions)
|
valid = check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions, bk_needed)
|
||||||
else:
|
else:
|
||||||
dungeon, hangers, hooks = dungeon_cache[depth]
|
dungeon, hangers, hooks = dungeon_cache[depth]
|
||||||
valid = True
|
valid = True
|
||||||
@@ -96,10 +99,20 @@ def generate_dungeon(available_sectors, entrance_region_names, world, player):
|
|||||||
return master_sector
|
return master_sector
|
||||||
|
|
||||||
|
|
||||||
|
def determine_if_bk_needed(sector, split_dungeon, world, player):
|
||||||
|
if not split_dungeon:
|
||||||
|
for region in sector.regions:
|
||||||
|
for ext in region.exits:
|
||||||
|
door = world.check_for_door(ext.name, player)
|
||||||
|
if door is not None and door.bigKey:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def gen_dungeon_info(available_sectors, entrance_regions, proposed_map, valid_doors, world, player):
|
def gen_dungeon_info(available_sectors, entrance_regions, proposed_map, valid_doors, world, player):
|
||||||
# step 1 create dungeon: Dict<DoorName|Origin, GraphPiece>
|
# step 1 create dungeon: Dict<DoorName|Origin, GraphPiece>
|
||||||
dungeon = {}
|
dungeon = {}
|
||||||
original_state = extend_reachable_state_improved(entrance_regions, ExplorationState(), proposed_map, valid_doors, True, world, player)
|
original_state = extend_reachable_state_improved(entrance_regions, ExplorationState(), proposed_map, valid_doors, world, player)
|
||||||
dungeon['Origin'] = create_graph_piece_from_state(None, original_state, original_state, proposed_map)
|
dungeon['Origin'] = create_graph_piece_from_state(None, original_state, original_state, proposed_map)
|
||||||
doors_to_connect = set()
|
doors_to_connect = set()
|
||||||
hanger_set = set()
|
hanger_set = set()
|
||||||
@@ -110,7 +123,7 @@ def gen_dungeon_info(available_sectors, entrance_regions, proposed_map, valid_do
|
|||||||
if not door.stonewall and door not in proposed_map.keys():
|
if not door.stonewall and door not in proposed_map.keys():
|
||||||
hanger_set.add(door)
|
hanger_set.add(door)
|
||||||
parent = parent_region(door, world, player).parent_region
|
parent = parent_region(door, world, player).parent_region
|
||||||
o_state = extend_reachable_state_improved([parent], ExplorationState(), proposed_map, valid_doors, False, world, player)
|
o_state = extend_reachable_state_improved([parent], ExplorationState(), proposed_map, valid_doors, world, player)
|
||||||
o_state_cache[door.name] = o_state
|
o_state_cache[door.name] = o_state
|
||||||
piece = create_graph_piece_from_state(door, o_state, o_state, proposed_map)
|
piece = create_graph_piece_from_state(door, o_state, o_state, proposed_map)
|
||||||
dungeon[door.name] = piece
|
dungeon[door.name] = piece
|
||||||
@@ -170,7 +183,7 @@ def check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, valid_do
|
|||||||
def explore_blue_state(door, dungeon, o_state, proposed_map, valid_doors, world, player):
|
def explore_blue_state(door, dungeon, o_state, proposed_map, valid_doors, world, player):
|
||||||
parent = parent_region(door, world, player).parent_region
|
parent = parent_region(door, world, player).parent_region
|
||||||
blue_start = ExplorationState(CrystalBarrier.Blue)
|
blue_start = ExplorationState(CrystalBarrier.Blue)
|
||||||
b_state = extend_reachable_state_improved([parent], blue_start, proposed_map, valid_doors, False, world, player)
|
b_state = extend_reachable_state_improved([parent], blue_start, proposed_map, valid_doors, world, player)
|
||||||
dungeon[door.name] = create_graph_piece_from_state(door, o_state, b_state, proposed_map)
|
dungeon[door.name] = create_graph_piece_from_state(door, o_state, b_state, proposed_map)
|
||||||
|
|
||||||
|
|
||||||
@@ -231,7 +244,7 @@ def filter_choices(next_hanger, door, orig_hang, prev_choices, hook_candidates):
|
|||||||
return next_hanger != door and orig_hang != next_hanger and door not in hook_candidates
|
return next_hanger != door and orig_hang != next_hanger and door not in hook_candidates
|
||||||
|
|
||||||
|
|
||||||
def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions):
|
def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_regions, bk_needed):
|
||||||
# evaluate if everything is still plausible
|
# evaluate if everything is still plausible
|
||||||
|
|
||||||
# only origin is left in the dungeon and not everything is connected
|
# only origin is left in the dungeon and not everything is connected
|
||||||
@@ -265,10 +278,15 @@ def check_valid(dungeon, hangers, hooks, proposed_map, doors_to_connect, all_reg
|
|||||||
if len(outstanding_doors[key]) > 0 and len(hangers[key]) == 0 and len(hooks[opp_key]) == 0:
|
if len(outstanding_doors[key]) > 0 and len(hangers[key]) == 0 and len(hooks[opp_key]) == 0:
|
||||||
return False
|
return False
|
||||||
all_visited = set()
|
all_visited = set()
|
||||||
|
bk_possible = not bk_needed
|
||||||
for piece in dungeon.values():
|
for piece in dungeon.values():
|
||||||
all_visited.update(piece.visited_regions)
|
all_visited.update(piece.visited_regions)
|
||||||
|
if not bk_possible and len(piece.possible_bk_locations) > 0:
|
||||||
|
bk_possible = True
|
||||||
if len(all_regions.difference(all_visited)) > 0:
|
if len(all_regions.difference(all_visited)) > 0:
|
||||||
return False
|
return False
|
||||||
|
if not bk_possible:
|
||||||
|
return False
|
||||||
new_hangers_found = True
|
new_hangers_found = True
|
||||||
accessible_hook_types = []
|
accessible_hook_types = []
|
||||||
hanger_matching = set()
|
hanger_matching = set()
|
||||||
@@ -346,9 +364,15 @@ def create_graph_piece_from_state(door, o_state, b_state, proposed_map):
|
|||||||
graph_piece.visited_regions.update(o_state.visited_orange)
|
graph_piece.visited_regions.update(o_state.visited_orange)
|
||||||
graph_piece.visited_regions.update(b_state.visited_blue)
|
graph_piece.visited_regions.update(b_state.visited_blue)
|
||||||
graph_piece.visited_regions.update(b_state.visited_orange)
|
graph_piece.visited_regions.update(b_state.visited_orange)
|
||||||
|
graph_piece.possible_bk_locations.update(filter_for_potential_bk_locations(o_state.bk_found))
|
||||||
|
graph_piece.possible_bk_locations.update(filter_for_potential_bk_locations(b_state.bk_found))
|
||||||
return graph_piece
|
return graph_piece
|
||||||
|
|
||||||
|
|
||||||
|
def filter_for_potential_bk_locations(locations):
|
||||||
|
return [x for x in locations if '- Big Chest' not in x.name and '- Prize' not in x.name and x.name not in dungeon_events and x.name not in key_only_locations.keys()]
|
||||||
|
|
||||||
|
|
||||||
def parent_region(door, world, player):
|
def parent_region(door, world, player):
|
||||||
return world.get_entrance(door.name, player)
|
return world.get_entrance(door.name, player)
|
||||||
|
|
||||||
@@ -496,6 +520,7 @@ class ExplorationState(object):
|
|||||||
self.used_locations = 0
|
self.used_locations = 0
|
||||||
self.key_locations = 0
|
self.key_locations = 0
|
||||||
self.used_smalls = 0
|
self.used_smalls = 0
|
||||||
|
self.bk_found = set()
|
||||||
|
|
||||||
self.non_door_entrances = []
|
self.non_door_entrances = []
|
||||||
|
|
||||||
@@ -520,6 +545,7 @@ class ExplorationState(object):
|
|||||||
ret.used_locations = self.used_locations
|
ret.used_locations = self.used_locations
|
||||||
ret.used_smalls = self.used_smalls
|
ret.used_smalls = self.used_smalls
|
||||||
ret.found_locations = list(self.found_locations)
|
ret.found_locations = list(self.found_locations)
|
||||||
|
ret.bk_found = set(self.bk_found)
|
||||||
|
|
||||||
ret.non_door_entrances = list(self.non_door_entrances)
|
ret.non_door_entrances = list(self.non_door_entrances)
|
||||||
return ret
|
return ret
|
||||||
@@ -529,7 +555,7 @@ class ExplorationState(object):
|
|||||||
self.crystal = exp_door.crystal
|
self.crystal = exp_door.crystal
|
||||||
return exp_door
|
return exp_door
|
||||||
|
|
||||||
def visit_region(self, region, key_region=None, key_checks=False):
|
def visit_region(self, region, key_region=None, key_checks=False, bk_Flag=False):
|
||||||
if self.crystal == CrystalBarrier.Either:
|
if self.crystal == CrystalBarrier.Either:
|
||||||
if region not in self.visited_blue:
|
if region not in self.visited_blue:
|
||||||
self.visited_blue.append(region)
|
self.visited_blue.append(region)
|
||||||
@@ -547,6 +573,8 @@ class ExplorationState(object):
|
|||||||
self.ttl_locations += 1
|
self.ttl_locations += 1
|
||||||
if location not in self.found_locations:
|
if location not in self.found_locations:
|
||||||
self.found_locations.append(location)
|
self.found_locations.append(location)
|
||||||
|
if not bk_Flag:
|
||||||
|
self.bk_found.add(location)
|
||||||
if location.name in dungeon_events and location.name not in self.events:
|
if location.name in dungeon_events and location.name not in self.events:
|
||||||
if self.flooded_key_check(location):
|
if self.flooded_key_check(location):
|
||||||
self.perform_event(location.name, key_region)
|
self.perform_event(location.name, key_region)
|
||||||
@@ -607,22 +635,22 @@ class ExplorationState(object):
|
|||||||
elif not self.in_door_list(door, self.avail_doors):
|
elif not self.in_door_list(door, self.avail_doors):
|
||||||
self.append_door_to_list(door, self.avail_doors)
|
self.append_door_to_list(door, self.avail_doors)
|
||||||
|
|
||||||
def add_all_doors_check_proposed(self, region, proposed_map, valid_doors, isOrigin, world, player):
|
def add_all_doors_check_proposed(self, region, proposed_map, valid_doors, flag, world, player):
|
||||||
for door in get_doors(world, region, player):
|
for door in get_doors(world, region, player):
|
||||||
if self.can_traverse_bk_check(door, isOrigin):
|
if self.can_traverse(door):
|
||||||
if door.controller is not None:
|
if door.controller is not None:
|
||||||
door = door.controller
|
door = door.controller
|
||||||
if door.dest is None and door not in proposed_map.keys() and door in valid_doors:
|
if door.dest is None and door not in proposed_map.keys() and door in valid_doors:
|
||||||
if not self.in_door_list_ic(door, self.unattached_doors):
|
if not self.in_door_list_ic(door, self.unattached_doors):
|
||||||
self.append_door_to_list(door, self.unattached_doors)
|
self.append_door_to_list(door, self.unattached_doors, flag)
|
||||||
else:
|
else:
|
||||||
other = self.find_door_in_list(door, self.unattached_doors)
|
other = self.find_door_in_list(door, self.unattached_doors)
|
||||||
if self.crystal != other.crystal:
|
if self.crystal != other.crystal:
|
||||||
other.crystal = CrystalBarrier.Either
|
other.crystal = CrystalBarrier.Either
|
||||||
elif door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door, self.event_doors):
|
elif door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door, self.event_doors):
|
||||||
self.append_door_to_list(door, self.event_doors)
|
self.append_door_to_list(door, self.event_doors, flag)
|
||||||
elif not self.in_door_list(door, self.avail_doors):
|
elif not self.in_door_list(door, self.avail_doors):
|
||||||
self.append_door_to_list(door, self.avail_doors)
|
self.append_door_to_list(door, self.avail_doors, flag)
|
||||||
|
|
||||||
def add_all_doors_check_key_region(self, region, key_region, world, player):
|
def add_all_doors_check_key_region(self, region, key_region, world, player):
|
||||||
for door in get_doors(world, region, player):
|
for door in get_doors(world, region, player):
|
||||||
@@ -676,9 +704,16 @@ class ExplorationState(object):
|
|||||||
return False
|
return False
|
||||||
if door.crystal not in [CrystalBarrier.Null, CrystalBarrier.Either]:
|
if door.crystal not in [CrystalBarrier.Null, CrystalBarrier.Either]:
|
||||||
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
||||||
return not isOrigin or not door.bigKey or len(self.found_locations) > 0
|
return not isOrigin or not door.bigKey or self.count_locations_exclude_specials() > 0
|
||||||
# return not door.bigKey or len([x for x in self.found_locations if '- Prize' not in x.name]) > 0
|
# return not door.bigKey or len([x for x in self.found_locations if '- Prize' not in x.name]) > 0
|
||||||
|
|
||||||
|
def count_locations_exclude_specials(self):
|
||||||
|
cnt = 0
|
||||||
|
for loc in self.found_locations:
|
||||||
|
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events and loc.name not in key_only_locations.keys():
|
||||||
|
cnt += 1
|
||||||
|
return cnt
|
||||||
|
|
||||||
def validate(self, door, region, world, player):
|
def validate(self, door, region, world, player):
|
||||||
return self.can_traverse(door) and not self.visited(region) and valid_region_to_explore(region, world, player)
|
return self.can_traverse(door) and not self.visited(region) and valid_region_to_explore(region, world, player)
|
||||||
|
|
||||||
@@ -702,11 +737,11 @@ class ExplorationState(object):
|
|||||||
return d
|
return d
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def append_door_to_list(self, door, door_list):
|
def append_door_to_list(self, door, door_list, flag=False):
|
||||||
if door.crystal == CrystalBarrier.Null:
|
if door.crystal == CrystalBarrier.Null:
|
||||||
door_list.append(ExplorableDoor(door, self.crystal))
|
door_list.append(ExplorableDoor(door, self.crystal, flag))
|
||||||
else:
|
else:
|
||||||
door_list.append(ExplorableDoor(door, door.crystal))
|
door_list.append(ExplorableDoor(door, door.crystal, flag))
|
||||||
|
|
||||||
def key_door_sort(self, d):
|
def key_door_sort(self, d):
|
||||||
if d.door.smallKey:
|
if d.door.smallKey:
|
||||||
@@ -719,9 +754,10 @@ class ExplorationState(object):
|
|||||||
|
|
||||||
class ExplorableDoor(object):
|
class ExplorableDoor(object):
|
||||||
|
|
||||||
def __init__(self, door, crystal):
|
def __init__(self, door, crystal, flag):
|
||||||
self.door = door
|
self.door = door
|
||||||
self.crystal = crystal
|
self.crystal = crystal
|
||||||
|
self.flag = flag
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.__unicode__())
|
return str(self.__unicode__())
|
||||||
@@ -745,11 +781,11 @@ def extend_reachable_state(search_regions, state, world, player):
|
|||||||
return local_state
|
return local_state
|
||||||
|
|
||||||
|
|
||||||
def extend_reachable_state_improved(search_regions, state, proposed_map, valid_doors, isOrigin, world, player):
|
def extend_reachable_state_improved(search_regions, state, proposed_map, valid_doors, world, player):
|
||||||
local_state = state.copy()
|
local_state = state.copy()
|
||||||
for region in search_regions:
|
for region in search_regions:
|
||||||
local_state.visit_region(region)
|
local_state.visit_region(region)
|
||||||
local_state.add_all_doors_check_proposed(region, proposed_map, valid_doors, isOrigin, world, player)
|
local_state.add_all_doors_check_proposed(region, proposed_map, valid_doors, False, world, player)
|
||||||
while len(local_state.avail_doors) > 0:
|
while len(local_state.avail_doors) > 0:
|
||||||
explorable_door = local_state.next_avail_door()
|
explorable_door = local_state.next_avail_door()
|
||||||
if explorable_door.door in proposed_map:
|
if explorable_door.door in proposed_map:
|
||||||
@@ -758,8 +794,9 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, valid_d
|
|||||||
connect_region = world.get_entrance(explorable_door.door.name, player).connected_region
|
connect_region = world.get_entrance(explorable_door.door.name, player).connected_region
|
||||||
if connect_region is not None:
|
if connect_region is not None:
|
||||||
if valid_region_to_explore(connect_region, world, player) and not local_state.visited(connect_region):
|
if valid_region_to_explore(connect_region, world, player) and not local_state.visited(connect_region):
|
||||||
local_state.visit_region(connect_region)
|
flag = explorable_door.flag or explorable_door.door.bigKey
|
||||||
local_state.add_all_doors_check_proposed(connect_region, proposed_map, valid_doors, isOrigin, world, player)
|
local_state.visit_region(connect_region, bk_Flag=flag)
|
||||||
|
local_state.add_all_doors_check_proposed(connect_region, proposed_map, valid_doors, flag, world, player)
|
||||||
return local_state
|
return local_state
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,73 +8,14 @@ from DungeonGenerator import ExplorationState
|
|||||||
class KeySphere(object):
|
class KeySphere(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.access_doors = set()
|
self.access_door = None
|
||||||
self.free_locations = []
|
self.free_locations = set()
|
||||||
self.prize_region = False
|
self.prize_region = False
|
||||||
self.key_only_locations = []
|
self.key_only_locations = set()
|
||||||
self.child_doors = set()
|
self.child_doors = set()
|
||||||
self.bk_locked = False
|
self.bk_locked = False
|
||||||
self.parent_sphere = None
|
self.parent_sphere = None
|
||||||
|
self.other_locations = set()
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class KeyLayout(object):
|
class KeyLayout(object):
|
||||||
@@ -121,6 +62,7 @@ class DoorRules(object):
|
|||||||
self.alternate_big_key_loc = set()
|
self.alternate_big_key_loc = set()
|
||||||
# for a place with only 1 free location/key_only_location behind it ... no goals and locations
|
# for a place with only 1 free location/key_only_location behind it ... no goals and locations
|
||||||
self.allow_small = False
|
self.allow_small = False
|
||||||
|
self.small_location = None
|
||||||
|
|
||||||
|
|
||||||
class KeyCounter(object):
|
class KeyCounter(object):
|
||||||
@@ -133,11 +75,21 @@ class KeyCounter(object):
|
|||||||
self.open_doors = set()
|
self.open_doors = set()
|
||||||
self.used_keys = 0
|
self.used_keys = 0
|
||||||
self.big_key_opened = False
|
self.big_key_opened = False
|
||||||
|
self.important_location = False
|
||||||
|
|
||||||
def update(self, key_sphere):
|
def update(self, key_sphere):
|
||||||
self.free_locations.update(key_sphere.free_locations)
|
self.free_locations.update(key_sphere.free_locations)
|
||||||
self.key_only_locations.update(key_sphere.key_only_locations)
|
self.key_only_locations.update(key_sphere.key_only_locations)
|
||||||
self.child_doors.update(key_sphere.child_doors)
|
self.child_doors.update([x for x in key_sphere.child_doors if x not in self.open_doors and x.dest not in self.open_doors])
|
||||||
|
self.important_location = self.important_location or key_sphere.prize_region or self.special_region(key_sphere)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def special_region(key_sphere):
|
||||||
|
for other in key_sphere.other_locations:
|
||||||
|
# todo: zelda's cell is special in standard, and probably crossed too
|
||||||
|
if other.name in ['Attic Cracked Floor', 'Suspicious Maiden']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def open_door(self, door, flat_proposal):
|
def open_door(self, door, flat_proposal):
|
||||||
if door in flat_proposal:
|
if door in flat_proposal:
|
||||||
@@ -146,13 +98,15 @@ class KeyCounter(object):
|
|||||||
self.open_doors.add(door)
|
self.open_doors.add(door)
|
||||||
if door.dest in flat_proposal:
|
if door.dest in flat_proposal:
|
||||||
self.open_doors.add(door.dest)
|
self.open_doors.add(door.dest)
|
||||||
|
if door.dest in self.child_doors:
|
||||||
|
self.child_doors.remove(door.dest)
|
||||||
elif door.bigKey:
|
elif door.bigKey:
|
||||||
self.big_key_opened = True
|
self.big_key_opened = True
|
||||||
self.child_doors.remove(door)
|
self.child_doors.remove(door)
|
||||||
self.open_doors.add(door)
|
self.open_doors.add(door)
|
||||||
|
|
||||||
def used_smalls_loc(self):
|
def used_smalls_loc(self, reserve=0):
|
||||||
return max(self.used_keys - len(self.key_only_locations), 0)
|
return max(self.used_keys + reserve - len(self.key_only_locations), 0)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
ret = KeyCounter(self.max_chests)
|
ret = KeyCounter(self.max_chests)
|
||||||
@@ -161,6 +115,8 @@ class KeyCounter(object):
|
|||||||
ret.child_doors.update(self.child_doors)
|
ret.child_doors.update(self.child_doors)
|
||||||
ret.used_keys = self.used_keys
|
ret.used_keys = self.used_keys
|
||||||
ret.open_doors.update(self.open_doors)
|
ret.open_doors.update(self.open_doors)
|
||||||
|
ret.big_key_opened = self.big_key_opened
|
||||||
|
ret.important_location = self.important_location
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@@ -171,55 +127,56 @@ def analyze_dungeon(key_layout, world, player):
|
|||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
key_layout.max_chests = len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
key_layout.max_chests = len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
||||||
|
|
||||||
find_bk_locked_sections(key_layout)
|
find_bk_locked_sections(key_layout, world)
|
||||||
|
|
||||||
key_counter = KeyCounter(key_layout.max_chests)
|
key_counter = KeyCounter(key_layout.max_chests)
|
||||||
key_counter.update(key_layout.key_spheres['Origin'])
|
key_counter.update(key_layout.key_spheres['Origin'])
|
||||||
queue = collections.deque([(key_layout.key_spheres['Origin'], key_counter)])
|
queue = collections.deque([(key_layout.key_spheres['Origin'], key_counter)])
|
||||||
|
doors_completed = set()
|
||||||
|
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
key_sphere, key_counter = queue.popleft()
|
key_sphere, key_counter = queue.popleft()
|
||||||
chest_keys = available_chest_small_keys(key_counter, False, world) # todo: when to count the bk chests
|
chest_keys = available_chest_small_keys(key_counter, False, world)
|
||||||
# chest_keys_bk = available_chest_small_keys(key_counter, True, world)
|
# chest_keys_bk = available_chest_small_keys(key_counter, True, world)
|
||||||
available = chest_keys + len(key_counter.key_only_locations) - key_counter.used_keys
|
available = chest_keys + len(key_counter.key_only_locations) - key_counter.used_keys
|
||||||
possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop)
|
possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop)
|
||||||
# todo: big chest counts?
|
if not key_counter.big_key_opened:
|
||||||
if chest_keys == count_locations_big_optional(key_counter.free_locations) and available <= possible_smalls:
|
if chest_keys == count_locations_big_optional(key_counter.free_locations) and available <= possible_smalls:
|
||||||
key_logic.bk_restricted.update(key_counter.free_locations)
|
key_logic.bk_restricted.update(key_counter.free_locations)
|
||||||
# logic min
|
# logic min?
|
||||||
if not key_sphere.bk_locked and big_chest_in_locations(key_counter.free_locations):
|
if not key_sphere.bk_locked and big_chest_in_locations(key_counter.free_locations):
|
||||||
key_logic.sm_restricted.update(find_big_chest_locations(key_counter.free_locations))
|
key_logic.sm_restricted.update(find_big_chest_locations(key_counter.free_locations))
|
||||||
# if available <= possible_smalls:
|
minimal_keys = None
|
||||||
# in this case, at least 1 child must have the available rule - unless relaxing is possible
|
# todo: this feels like big key doors aren't accounted for - you may or may not find the big_key door at this point
|
||||||
|
if available + key_counter.used_keys <= possible_smalls:
|
||||||
|
minimal_keys = available + key_counter.used_keys
|
||||||
|
# todo: detect forced subsequent keys - see keypuzzles
|
||||||
# try to relax the rules here?
|
# try to relax the rules here?
|
||||||
for child in key_sphere.child_doors:
|
for child in key_sphere.child_doors:
|
||||||
next_sphere = key_layout.key_spheres[child.name]
|
next_sphere = key_layout.key_spheres[child.name]
|
||||||
if not empty_sphere(next_sphere):
|
if not empty_sphere(next_sphere) and child not in doors_completed:
|
||||||
if not child.bigKey:
|
if not child.bigKey:
|
||||||
# todo: calculate based on big key doors vs smalls - eastern dark square
|
expanded_counter = expand_counter_to_last_door(child, key_counter, key_layout, set())
|
||||||
rule = DoorRules(min(available, possible_smalls) + key_counter.used_keys)
|
rule = create_rule(expanded_counter, key_layout, minimal_keys, world)
|
||||||
|
check_for_self_lock_key(rule, next_sphere, key_layout, world)
|
||||||
|
bk_restricted_rules(rule, next_sphere, key_counter, key_layout, minimal_keys, world)
|
||||||
key_logic.door_rules[child.name] = rule
|
key_logic.door_rules[child.name] = rule
|
||||||
|
doors_completed.add(next_sphere.access_door)
|
||||||
next_counter = increment_key_counter(child, next_sphere, key_counter, key_layout.flat_prop)
|
next_counter = increment_key_counter(child, next_sphere, key_counter, key_layout.flat_prop)
|
||||||
queue.append((next_sphere, next_counter))
|
queue.append((next_sphere, next_counter))
|
||||||
return key_layout
|
return key_layout
|
||||||
|
|
||||||
|
|
||||||
# for child in key_sphere.child_doors:
|
def find_bk_locked_sections(key_layout, world):
|
||||||
# next_sphere = key_spheres[child.name]
|
|
||||||
# if not empty_sphere(next_sphere):
|
|
||||||
# sm_rule = calc_basic_small_key_rule(key_sphere, key_spheres, key_layout, flat_proposal, world, player)
|
|
||||||
|
|
||||||
|
|
||||||
def find_bk_locked_sections(key_layout):
|
|
||||||
key_spheres = key_layout.key_spheres
|
key_spheres = key_layout.key_spheres
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
|
|
||||||
bk_key_not_required = set()
|
bk_key_not_required = set()
|
||||||
big_chest_allowed_big_key = True
|
big_chest_allowed_big_key = world.accessibility != 'locations'
|
||||||
for key in key_spheres.keys():
|
for key in key_spheres.keys():
|
||||||
sphere = key_spheres[key]
|
sphere = key_spheres[key]
|
||||||
key_layout.all_chest_locations.update(sphere.free_locations)
|
key_layout.all_chest_locations.update(sphere.free_locations)
|
||||||
if sphere.bk_locked and sphere.prize_region:
|
if sphere.bk_locked and (sphere.prize_region or KeyCounter.special_region(sphere)):
|
||||||
big_chest_allowed_big_key = False
|
big_chest_allowed_big_key = False
|
||||||
if not sphere.bk_locked:
|
if not sphere.bk_locked:
|
||||||
bk_key_not_required.update(sphere.free_locations)
|
bk_key_not_required.update(sphere.free_locations)
|
||||||
@@ -234,6 +191,21 @@ def empty_sphere(sphere):
|
|||||||
return not sphere.prize_region
|
return not sphere.prize_region
|
||||||
|
|
||||||
|
|
||||||
|
def relative_empty_sphere(sphere, key_counter):
|
||||||
|
if len(sphere.key_only_locations.difference(key_counter.key_only_locations)) > 0:
|
||||||
|
return False
|
||||||
|
if len(sphere.free_locations.difference(key_counter.free_locations)) > 0:
|
||||||
|
return False
|
||||||
|
new_child_door = False
|
||||||
|
for child in sphere.child_doors:
|
||||||
|
if child not in key_counter.child_doors and child not in key_counter.open_doors and (not child.bigKey or not key_counter.big_key_opened):
|
||||||
|
new_child_door = True
|
||||||
|
break
|
||||||
|
if new_child_door:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def increment_key_counter(door, sphere, key_counter, flat_proposal):
|
def increment_key_counter(door, sphere, key_counter, flat_proposal):
|
||||||
new_counter = key_counter.copy()
|
new_counter = key_counter.copy()
|
||||||
new_counter.open_door(door, flat_proposal)
|
new_counter.open_door(door, flat_proposal)
|
||||||
@@ -241,75 +213,75 @@ def increment_key_counter(door, sphere, key_counter, flat_proposal):
|
|||||||
return new_counter
|
return new_counter
|
||||||
|
|
||||||
|
|
||||||
def check_for_big_doors(door, key_counter, key_layout):
|
def expand_counter_to_last_door(door, key_counter, key_layout, ignored_doors):
|
||||||
|
door_sphere = key_layout.key_spheres[door.name]
|
||||||
|
small_doors = set()
|
||||||
big_doors = set()
|
big_doors = set()
|
||||||
for other in key_counter.child_doors:
|
for other in key_counter.child_doors:
|
||||||
if other != door and other.bigKey:
|
if other != door and other not in ignored_doors:
|
||||||
big_doors.add(other)
|
if other.bigKey:
|
||||||
big_key_available = len(key_counter.free_location) - key_counter.used_smalls_loc > 0
|
big_doors.add(other)
|
||||||
if len(big_doors) == 0 or not big_key_available:
|
elif other.dest not in small_doors:
|
||||||
|
small_doors.add(other)
|
||||||
|
# I feel bk might be available if the current small door could use a key_only_loc - the param might cover this case
|
||||||
|
big_key_available = len(key_counter.free_locations) - key_counter.used_smalls_loc(1) > 0
|
||||||
|
if len(small_doors) == 0 and (len(big_doors) == 0 or not big_key_available):
|
||||||
return key_counter
|
return key_counter
|
||||||
new_counter = key_counter
|
new_counter = key_counter
|
||||||
for big_door in big_doors:
|
last_counter = key_counter
|
||||||
big_sphere = key_layout.key_spheres[big_door.name]
|
new_ignored = set(ignored_doors)
|
||||||
new_counter = increment_key_counter(big_door, big_sphere, new_counter, key_layout.flat_prop)
|
for new_door in small_doors.union(big_doors):
|
||||||
# nested big key doors?
|
new_sphere = key_layout.key_spheres[new_door.name]
|
||||||
|
new_counter = increment_key_counter(new_door, new_sphere, new_counter, key_layout.flat_prop)
|
||||||
|
# this means the new_door invalidates the door / leads to the same stuff
|
||||||
|
if relative_empty_sphere(door_sphere, new_counter):
|
||||||
|
new_counter = last_counter
|
||||||
|
new_ignored.add(new_door)
|
||||||
|
else:
|
||||||
|
last_counter = new_counter
|
||||||
old_counter = None
|
old_counter = None
|
||||||
while old_counter != new_counter:
|
while old_counter != new_counter:
|
||||||
old_counter = new_counter
|
old_counter = new_counter
|
||||||
new_counter = check_for_big_doors(door, old_counter, key_layout)
|
new_counter = expand_counter_to_last_door(door, old_counter, key_layout, new_ignored)
|
||||||
# I think I've opened them all!
|
|
||||||
return new_counter
|
return new_counter
|
||||||
|
|
||||||
|
|
||||||
# def calc_basic_small_key_rule(key_sphere, key_spheres, key_layout, flat_proposal, world, player):
|
def create_rule(key_counter, key_layout, minimal_keys, world):
|
||||||
# free_locations = set()
|
chest_keys = available_chest_small_keys(key_counter, key_counter.big_key_opened, world)
|
||||||
# key_only_locations = set()
|
available = chest_keys + len(key_counter.key_only_locations) - key_counter.used_keys
|
||||||
# offshoot_doors = set()
|
possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop)
|
||||||
# queue = collections.deque()
|
required_keys = min(available, possible_smalls) + key_counter.used_keys
|
||||||
# parent = key_sphere.parent_sphere
|
if minimal_keys is None or required_keys <= minimal_keys:
|
||||||
# while parent is not None:
|
return DoorRules(required_keys)
|
||||||
# queue.append(parent)
|
else:
|
||||||
# parent = parent.parent_sphere
|
return DoorRules(minimal_keys)
|
||||||
# while len(queue) > 0:
|
|
||||||
# previous = queue.popleft()
|
|
||||||
# free_locations.update(previous.free_locations)
|
def check_for_self_lock_key(rule, sphere, key_layout, world):
|
||||||
# key_only_locations.update(previous.key_only_locations)
|
if world.accessibility != 'locations':
|
||||||
# for other_door in parent.child_doors:
|
counter = KeyCounter(key_layout.max_chests)
|
||||||
# if other_door not in key_sphere.access_doors:
|
counter.update(sphere)
|
||||||
# offshoot_doors.add(other_door)
|
if not self_lock_possible(counter):
|
||||||
# # todo: bk versions
|
return
|
||||||
# chest_keys = available_chest_small_keys(key_layout, free_locations, key_sphere.bk_locked, world, player)
|
queue = collections.deque(counter.child_doors)
|
||||||
# parent_avail = chest_keys + len(key_only_locations)
|
already_queued = set(counter.child_doors)
|
||||||
#
|
while len(queue) > 0:
|
||||||
# usuable_elsewhere = 0
|
child = queue.popleft()
|
||||||
# open_set = set()
|
if child not in counter.open_doors:
|
||||||
# queue = collections.deque(offshoot_doors)
|
counter = increment_key_counter(child, key_layout.key_spheres[child.name], counter, key_layout.flat_prop)
|
||||||
# while len(queue) > 0:
|
if not self_lock_possible(counter):
|
||||||
# offshoot = queue.popleft()
|
return
|
||||||
# open_set.add(offshoot)
|
for new_door in counter.child_doors:
|
||||||
# if offshoot in flat_proposal:
|
if new_door not in already_queued:
|
||||||
# usuable_elsewhere += 1
|
queue.append(new_door)
|
||||||
# # else bk door
|
already_queued.add(new_door)
|
||||||
# if offshoot.dest in flat_proposal:
|
if len(counter.free_locations) == 1 and len(counter.key_only_locations) == 0 and not counter.important_location:
|
||||||
# open_set.add(offshoot.dest)
|
rule.allow_small = True
|
||||||
# off_sphere = key_spheres[offshoot.name]
|
rule.small_location = next(iter(counter.free_locations))
|
||||||
# free_locations.update(off_sphere.free_locations)
|
|
||||||
# key_only_locations.update(off_sphere.key_only_locations)
|
|
||||||
# for other_door in off_sphere.child_doors:
|
def self_lock_possible(counter):
|
||||||
# if other_door not in key_sphere.access_doors and other_door not in open_set:
|
return len(counter.free_locations) <= 1 and len(counter.key_only_locations) == 0 and not counter.important_location
|
||||||
# queue.append(other_door)
|
|
||||||
# # todo: bk versions
|
|
||||||
# offshoot_chest = available_chest_small_keys(key_layout, free_locations, key_sphere.bk_locked, world, player)
|
|
||||||
# offshoot_avail = offshoot_chest + len(key_only_locations)
|
|
||||||
#
|
|
||||||
# if usuable_elsewhere == parent_avail and offshoot_avail > parent_avail:
|
|
||||||
# return usuable_elsewhere + 1
|
|
||||||
# if usuable_elsewhere == parent_avail and offshoot_avail == parent_avail:
|
|
||||||
# return usuable_elsewhere
|
|
||||||
# if usuable_elsewhere < parent_avail:
|
|
||||||
# return usuable_elsewhere + 1
|
|
||||||
# return 10
|
|
||||||
|
|
||||||
|
|
||||||
def available_chest_small_keys(key_counter, bk, world):
|
def available_chest_small_keys(key_counter, bk, world):
|
||||||
@@ -322,14 +294,59 @@ def available_chest_small_keys(key_counter, bk, world):
|
|||||||
else:
|
else:
|
||||||
return key_counter.max_chests
|
return key_counter.max_chests
|
||||||
|
|
||||||
# derive key rules from key regions
|
|
||||||
# how many small key available at a given point (locations found / keysanity / retro)
|
def bk_restricted_rules(rule, sphere, key_counter, key_layout, minimal_keys, world):
|
||||||
# how many doors can be opened before you vs. smalls available
|
if sphere.bk_locked:
|
||||||
# soft lock detection - should it be run here?
|
return
|
||||||
# run with both bk off (locked behind current door) and bk found (elsewhere in the dungeon)
|
expanded_counter = expand_counter_no_big_doors(sphere.access_door, key_counter, key_layout, set())
|
||||||
# rules generally smaller if bk locked behind current door
|
bk_number = create_rule(expanded_counter, key_layout, minimal_keys, world).small_key_num
|
||||||
# big key restriction based on bk_locked
|
if bk_number == rule.small_key_num:
|
||||||
# prize regions - TT is weird as there are intermediate goals - assume child doors as well?
|
return
|
||||||
|
post_counter = KeyCounter(key_layout.max_chests)
|
||||||
|
post_counter.update(sphere)
|
||||||
|
other_doors_beyond_me = [x for x in post_counter.child_doors if not x.bigKey]
|
||||||
|
queue = collections.deque(other_doors_beyond_me)
|
||||||
|
already_queued = set(other_doors_beyond_me)
|
||||||
|
while len(queue) > 0:
|
||||||
|
child = queue.popleft()
|
||||||
|
if child not in post_counter.open_doors:
|
||||||
|
post_counter = increment_key_counter(child, key_layout.key_spheres[child.name], post_counter, key_layout.flat_prop)
|
||||||
|
for new_door in post_counter.child_doors:
|
||||||
|
if not new_door.bigKey and new_door not in already_queued and new_door.dest not in already_queued:
|
||||||
|
queue.append(new_door)
|
||||||
|
already_queued.add(new_door)
|
||||||
|
unique_loc = post_counter.free_locations.difference(expanded_counter.free_locations)
|
||||||
|
if len(unique_loc) > 0:
|
||||||
|
rule.alternate_small_key = bk_number
|
||||||
|
rule.alternate_big_key_loc.update(unique_loc)
|
||||||
|
|
||||||
|
|
||||||
|
def expand_counter_no_big_doors(door, key_counter, key_layout, ignored_doors):
|
||||||
|
door_sphere = key_layout.key_spheres[door.name]
|
||||||
|
small_doors = set()
|
||||||
|
for other in key_counter.child_doors:
|
||||||
|
if other != door and other not in ignored_doors:
|
||||||
|
if other.dest not in small_doors and not other.bigKey:
|
||||||
|
small_doors.add(other)
|
||||||
|
if len(small_doors) == 0:
|
||||||
|
return key_counter
|
||||||
|
new_counter = key_counter
|
||||||
|
last_counter = key_counter
|
||||||
|
new_ignored = set(ignored_doors)
|
||||||
|
for new_door in small_doors:
|
||||||
|
new_sphere = key_layout.key_spheres[new_door.name]
|
||||||
|
new_counter = increment_key_counter(new_door, new_sphere, new_counter, key_layout.flat_prop)
|
||||||
|
# this means the new_door invalidates the door / leads to the same stuff
|
||||||
|
if relative_empty_sphere(door_sphere, new_counter):
|
||||||
|
new_counter = last_counter
|
||||||
|
new_ignored.add(new_door)
|
||||||
|
else:
|
||||||
|
last_counter = new_counter
|
||||||
|
old_counter = None
|
||||||
|
while old_counter != new_counter:
|
||||||
|
old_counter = new_counter
|
||||||
|
new_counter = expand_counter_no_big_doors(door, old_counter, key_layout, new_ignored)
|
||||||
|
return new_counter
|
||||||
|
|
||||||
|
|
||||||
def create_key_spheres(key_layout, world, player):
|
def create_key_spheres(key_layout, world, player):
|
||||||
@@ -352,28 +369,15 @@ def create_key_spheres(key_layout, world, player):
|
|||||||
open_a_door(door, child_state, flat_proposal)
|
open_a_door(door, child_state, flat_proposal)
|
||||||
expand_key_state(child_state, flat_proposal, world, player)
|
expand_key_state(child_state, flat_proposal, world, player)
|
||||||
child_kr = create_key_sphere(child_state, next_key_sphere, door)
|
child_kr = create_key_sphere(child_state, next_key_sphere, door)
|
||||||
check_for_duplicates_sub_super_set(key_spheres, child_kr, door.name)
|
if door.name not in key_spheres.keys():
|
||||||
queue.append((child_kr, child_state))
|
key_spheres[door.name] = child_kr
|
||||||
|
queue.append((child_kr, child_state))
|
||||||
|
else:
|
||||||
|
old_sphere = key_spheres[door.name]
|
||||||
|
old_sphere.bk_locked = old_sphere.bk_locked and child_kr.bk_locked
|
||||||
return key_spheres
|
return key_spheres
|
||||||
|
|
||||||
|
|
||||||
def check_for_duplicates_sub_super_set(key_spheres, new_kr, door_name):
|
|
||||||
is_new = True
|
|
||||||
for kr in key_spheres.values():
|
|
||||||
if new_kr == kr: # todo: what about parent regions...
|
|
||||||
kr.access_doors.update(new_kr.access_doors)
|
|
||||||
kr.child_doors.update(new_kr.child_doors)
|
|
||||||
key_spheres[door_name] = kr
|
|
||||||
is_new = False
|
|
||||||
break
|
|
||||||
# if new_kr.issubset(kr):
|
|
||||||
# break
|
|
||||||
# if new_kr.issuperset(kr):
|
|
||||||
# break
|
|
||||||
if is_new:
|
|
||||||
key_spheres[door_name] = new_kr
|
|
||||||
|
|
||||||
|
|
||||||
def create_key_sphere(state, parent_sphere, door):
|
def create_key_sphere(state, parent_sphere, door):
|
||||||
key_sphere = KeySphere()
|
key_sphere = KeySphere()
|
||||||
key_sphere.parent_sphere = parent_sphere
|
key_sphere.parent_sphere = parent_sphere
|
||||||
@@ -382,7 +386,9 @@ def create_key_sphere(state, parent_sphere, door):
|
|||||||
parent_locations = set()
|
parent_locations = set()
|
||||||
while p_region is not None:
|
while p_region is not None:
|
||||||
parent_doors.update(p_region.child_doors)
|
parent_doors.update(p_region.child_doors)
|
||||||
parent_locations.update(p_region.free_locations+p_region.key_only_locations)
|
parent_locations.update(p_region.free_locations)
|
||||||
|
parent_locations.update(p_region.key_only_locations)
|
||||||
|
parent_locations.update(p_region.other_locations)
|
||||||
p_region = p_region.parent_sphere
|
p_region = p_region.parent_sphere
|
||||||
u_doors = unique_doors(state.small_doors+state.big_doors).difference(parent_doors)
|
u_doors = unique_doors(state.small_doors+state.big_doors).difference(parent_doors)
|
||||||
key_sphere.child_doors.update(u_doors)
|
key_sphere.child_doors.update(u_doors)
|
||||||
@@ -390,14 +396,17 @@ def create_key_sphere(state, parent_sphere, door):
|
|||||||
for loc in region_locations:
|
for loc in region_locations:
|
||||||
if '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']:
|
if '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']:
|
||||||
key_sphere.prize_region = True
|
key_sphere.prize_region = True
|
||||||
|
key_sphere.other_locations.add(loc)
|
||||||
elif loc.event and 'Small Key' in loc.item.name:
|
elif loc.event and 'Small Key' in loc.item.name:
|
||||||
key_sphere.key_only_locations.append(loc)
|
key_sphere.key_only_locations.add(loc)
|
||||||
elif loc.name not in dungeon_events:
|
elif loc.name not in dungeon_events:
|
||||||
key_sphere.free_locations.append(loc)
|
key_sphere.free_locations.add(loc)
|
||||||
|
else:
|
||||||
|
key_sphere.other_locations.add(loc)
|
||||||
# todo: Cellblock in a dungeon with a big_key door or chest - Crossed Mode
|
# todo: Cellblock in a dungeon with a big_key door or chest - Crossed Mode
|
||||||
key_sphere.bk_locked = state.big_key_opened if not state.big_key_special else False
|
key_sphere.bk_locked = state.big_key_opened if not state.big_key_special else False
|
||||||
if door is not None:
|
if door is not None:
|
||||||
key_sphere.access_doors.add(door)
|
key_sphere.access_door = door
|
||||||
return key_sphere
|
return key_sphere
|
||||||
|
|
||||||
|
|
||||||
@@ -483,66 +492,175 @@ def flatten_pair_list(paired_list):
|
|||||||
return flat_list
|
return flat_list
|
||||||
|
|
||||||
|
|
||||||
## vanilla validation code
|
# Soft lock stuff
|
||||||
|
class SoftLockException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# vanilla validation code
|
||||||
def validate_vanilla_key_logic(world, player):
|
def validate_vanilla_key_logic(world, player):
|
||||||
validators = {
|
validators = {
|
||||||
'Hyrule Castle': val_unimplemented,
|
'Hyrule Castle': val_hyrule,
|
||||||
'Eastern Palace': val_eastern,
|
'Eastern Palace': val_eastern,
|
||||||
'Desert Palace': val_desert,
|
'Desert Palace': val_desert,
|
||||||
'Tower of Hera': val_hera,
|
'Tower of Hera': val_hera,
|
||||||
'Agahnims Tower': val_tower,
|
'Agahnims Tower': val_tower,
|
||||||
'Palace of Darkness': val_unimplemented,
|
'Palace of Darkness': val_pod,
|
||||||
'Swamp Palace': val_unimplemented,
|
'Swamp Palace': val_swamp,
|
||||||
'Skull Woods': val_unimplemented,
|
'Skull Woods': val_skull,
|
||||||
'Thieves Town': val_unimplemented,
|
'Thieves Town': val_thieves,
|
||||||
'Ice Palace': val_unimplemented,
|
'Ice Palace': val_ice,
|
||||||
'Misery Mire': val_unimplemented,
|
'Misery Mire': val_mire,
|
||||||
'Turtle Rock': val_unimplemented,
|
'Turtle Rock': val_turtle,
|
||||||
'Ganons Tower': val_unimplemented
|
'Ganons Tower': val_ganons
|
||||||
}
|
}
|
||||||
key_logic_dict = world.key_logic[player]
|
key_logic_dict = world.key_logic[player]
|
||||||
for key, key_logic in key_logic_dict.items():
|
for key, key_logic in key_logic_dict.items():
|
||||||
validators[key](key_logic)
|
validators[key](key_logic, world, player)
|
||||||
|
|
||||||
|
|
||||||
def val_unimplemented(key_logic):
|
def val_hyrule(key_logic, world, player):
|
||||||
assert True
|
val_rule(key_logic.door_rules['Sewers Secret Room Key Door S'], 2)
|
||||||
|
val_rule(key_logic.door_rules['Sewers Dark Cross Key Door N'], 2)
|
||||||
|
val_rule(key_logic.door_rules['Hyrule Dungeon Map Room Key Door S'], 2)
|
||||||
|
val_rule(key_logic.door_rules['Hyrule Dungeon Armory Interior Key Door N'], 3, True, 'Hyrule Castle - Zelda\'s Chest')
|
||||||
|
# val_rule(key_logic.door_rules['Hyrule Dungeon Armory Interior Key Door N'], 4)
|
||||||
|
|
||||||
|
|
||||||
def val_eastern(key_logic):
|
def val_eastern(key_logic, world, player):
|
||||||
dark_square_rule = key_logic.door_rules['Eastern Dark Square Key Door WN']
|
# val_rule(key_logic.door_rules['Eastern Dark Square Key Door WN'], 2, False, None, 1, {'Eastern Palace - Big Key Chest'})
|
||||||
assert dark_square_rule.small_key_num == 2
|
val_rule(key_logic.door_rules['Eastern Dark Square Key Door WN'], 1)
|
||||||
# todo: allow big_key behind the door
|
val_rule(key_logic.door_rules['Eastern Darkness Up Stairs'], 2)
|
||||||
# assert dark_square_rule.alternate_small_key == 1
|
assert world.get_location('Eastern Palace - Big Chest', player) in key_logic.bk_restricted
|
||||||
# assert 'Eastern Palace - Big Key Chest' in dark_square_rule.alternat_big_key_loc
|
assert world.get_location('Eastern Palace - Boss', player) in key_logic.bk_restricted
|
||||||
# assert len(dark_square_rule.alternat_big_key_loc) == 1
|
|
||||||
assert key_logic.door_rules['Eastern Darkness Up Stairs'].small_key_num == 2
|
|
||||||
assert 'Eastern Palace - Big Chest' in key_logic.bk_restricted
|
|
||||||
assert 'Eastern Palace - Boss' in key_logic.bk_restricted
|
|
||||||
assert len(key_logic.bk_restricted) == 2
|
assert len(key_logic.bk_restricted) == 2
|
||||||
|
|
||||||
|
|
||||||
def val_desert(key_logic):
|
def val_desert(key_logic, world, player):
|
||||||
assert key_logic.door_rules['Desert East Wing Key Door EN'].small_key_num == 2
|
val_rule(key_logic.door_rules['Desert East Wing Key Door EN'], 2)
|
||||||
assert key_logic.door_rules['Desert Tiles 1 Up Stairs'].small_key_num == 2
|
val_rule(key_logic.door_rules['Desert Tiles 1 Up Stairs'], 2)
|
||||||
assert key_logic.door_rules['Desert Beamos Hall NE'].small_key_num == 3
|
val_rule(key_logic.door_rules['Desert Beamos Hall NE'], 3)
|
||||||
assert key_logic.door_rules['Desert Tiles 2 NE'].small_key_num == 4
|
val_rule(key_logic.door_rules['Desert Tiles 2 NE'], 4)
|
||||||
assert 'Desert Palace - Big Chest' in key_logic.bk_restricted
|
assert world.get_location('Desert Palace - Big Chest', player) in key_logic.bk_restricted
|
||||||
assert 'Desert Palace - Boss' in key_logic.bk_restricted
|
assert world.get_location('Desert Palace - Boss', player) in key_logic.bk_restricted
|
||||||
assert len(key_logic.bk_restricted) == 2
|
assert len(key_logic.bk_restricted) == 2
|
||||||
|
|
||||||
|
|
||||||
def val_hera(key_logic):
|
def val_hera(key_logic, world, player):
|
||||||
assert key_logic.door_rules['Hera Lobby Key Stairs'].small_key_num == 1
|
val_rule(key_logic.door_rules['Hera Lobby Key Stairs'], 1, True, 'Tower of Hera - Big Key Chest')
|
||||||
assert 'Tower of Hera - Big Chest' in key_logic.bk_restricted
|
assert world.get_location('Tower of Hera - Big Chest', player) in key_logic.bk_restricted
|
||||||
assert 'Tower of Hera - Compass Chest' in key_logic.bk_restricted
|
assert world.get_location('Tower of Hera - Compass Chest', player) in key_logic.bk_restricted
|
||||||
assert 'Tower of Hera - Boss' in key_logic.bk_restricted
|
assert world.get_location('Tower of Hera - Boss', player) in key_logic.bk_restricted
|
||||||
assert len(key_logic.bk_restricted) == 3
|
assert len(key_logic.bk_restricted) == 3
|
||||||
|
|
||||||
|
|
||||||
def val_tower(key_logic):
|
def val_tower(key_logic, world, player):
|
||||||
assert key_logic.door_rules['Tower Room 03 Up Stairs'].small_key_num == 1
|
val_rule(key_logic.door_rules['Tower Room 03 Up Stairs'], 1)
|
||||||
assert key_logic.door_rules['Tower Dark Maze ES'].small_key_num == 2
|
val_rule(key_logic.door_rules['Tower Dark Maze ES'], 2)
|
||||||
assert key_logic.door_rules['Tower Dark Chargers Up Stairs'].small_key_num == 3
|
val_rule(key_logic.door_rules['Tower Dark Archers Up Stairs'], 3)
|
||||||
assert key_logic.door_rules['Tower Circle of Pots WS'].small_key_num == 4
|
val_rule(key_logic.door_rules['Tower Circle of Pots WS'], 4)
|
||||||
|
|
||||||
|
|
||||||
|
def val_pod(key_logic, world, player):
|
||||||
|
val_rule(key_logic.door_rules['PoD Arena Main NW'], 4)
|
||||||
|
val_rule(key_logic.door_rules['PoD Basement Ledge Up Stairs'], 6, True, 'Palace of Darkness - Big Key Chest')
|
||||||
|
val_rule(key_logic.door_rules['PoD Compass Room SE'], 6, True, 'Palace of Darkness - Harmless Hellway')
|
||||||
|
val_rule(key_logic.door_rules['PoD Falling Bridge WN'], 6)
|
||||||
|
val_rule(key_logic.door_rules['PoD Dark Pegs WN'], 6)
|
||||||
|
assert world.get_location('Palace of Darkness - Big Chest', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Palace of Darkness - Boss', player) in key_logic.bk_restricted
|
||||||
|
assert len(key_logic.bk_restricted) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def val_swamp(key_logic, world, player):
|
||||||
|
val_rule(key_logic.door_rules['Swamp Entrance Down Stairs'], 1)
|
||||||
|
val_rule(key_logic.door_rules['Swamp Pot Row WS'], 2)
|
||||||
|
val_rule(key_logic.door_rules['Swamp Trench 1 Key Ledge NW'], 3)
|
||||||
|
val_rule(key_logic.door_rules['Swamp Hub North Ledge N'], 5)
|
||||||
|
val_rule(key_logic.door_rules['Swamp Hub WN'], 6)
|
||||||
|
val_rule(key_logic.door_rules['Swamp Waterway NW'], 6)
|
||||||
|
assert world.get_location('Swamp Palace - Entrance', player) in key_logic.bk_restricted
|
||||||
|
assert len(key_logic.bk_restricted) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def val_skull(key_logic, world, player):
|
||||||
|
val_rule(key_logic.door_rules['Skull 3 Lobby NW'], 4)
|
||||||
|
val_rule(key_logic.door_rules['Skull Spike Corner ES'], 5)
|
||||||
|
|
||||||
|
|
||||||
|
def val_thieves(key_logic, world, player):
|
||||||
|
val_rule(key_logic.door_rules['Thieves Hallway WS'], 1)
|
||||||
|
val_rule(key_logic.door_rules['Thieves Spike Switch Up Stairs'], 3)
|
||||||
|
val_rule(key_logic.door_rules['Thieves Conveyor Bridge WS'], 3, True, 'Thieves\' Town - Big Chest')
|
||||||
|
assert world.get_location('Thieves\' Town - Attic', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Thieves\' Town - Boss', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Thieves\' Town - Blind\'s Cell', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Thieves\' Town - Big Chest', player) in key_logic.bk_restricted
|
||||||
|
assert len(key_logic.bk_restricted) == 4
|
||||||
|
|
||||||
|
|
||||||
|
def val_ice(key_logic, world, player):
|
||||||
|
val_rule(key_logic.door_rules['Ice Jelly Key Down Stairs'], 1)
|
||||||
|
val_rule(key_logic.door_rules['Ice Conveyor SW'], 2)
|
||||||
|
val_rule(key_logic.door_rules['Ice Backwards Room Down Stairs'], 5)
|
||||||
|
assert world.get_location('Ice Palace - Boss', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Ice Palace - Big Chest', player) in key_logic.bk_restricted
|
||||||
|
assert len(key_logic.bk_restricted) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def val_mire(key_logic, world, player):
|
||||||
|
mire_west_wing = {'Misery Mire - Big Key Chest', 'Misery Mire - Compass Chest'}
|
||||||
|
val_rule(key_logic.door_rules['Mire Spikes NW'], 5)
|
||||||
|
val_rule(key_logic.door_rules['Mire Hub WS'], 5, False, None, 4, mire_west_wing)
|
||||||
|
val_rule(key_logic.door_rules['Mire Conveyor Crystal WS'], 6, False, None, 5, mire_west_wing)
|
||||||
|
assert world.get_location('Misery Mire - Boss', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Misery Mire - Big Chest', player) in key_logic.bk_restricted
|
||||||
|
assert len(key_logic.bk_restricted) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def val_turtle(key_logic, world, player):
|
||||||
|
val_rule(key_logic.door_rules['TR Hub NW'], 1)
|
||||||
|
val_rule(key_logic.door_rules['TR Pokey 1 NW'], 2)
|
||||||
|
val_rule(key_logic.door_rules['TR Chain Chomps Down Stairs'], 3)
|
||||||
|
val_rule(key_logic.door_rules['TR Pokey 2 ES'], 6, True, 'Turtle Rock - Big Key Chest', 4, {'Turtle Rock - Big Key Chest'})
|
||||||
|
val_rule(key_logic.door_rules['TR Crystaroller Down Stairs'], 5)
|
||||||
|
val_rule(key_logic.door_rules['TR Dash Bridge WS'], 6)
|
||||||
|
assert world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Turtle Rock - Eye Bridge - Top Left', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Turtle Rock - Eye Bridge - Top Right', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Turtle Rock - Boss', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Turtle Rock - Crystaroller Room', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Turtle Rock - Big Chest', player) in key_logic.bk_restricted
|
||||||
|
assert len(key_logic.bk_restricted) == 7
|
||||||
|
|
||||||
|
|
||||||
|
def val_ganons(key_logic, world, player):
|
||||||
|
rando_room = {'Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'}
|
||||||
|
compass_room = {'Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'}
|
||||||
|
gt_middle = {'Ganons Tower - Big Key Room - Left', 'Ganons Tower - Big Key Chest', 'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest'}
|
||||||
|
val_rule(key_logic.door_rules['GT Double Switch EN'], 7, False, None, 5, rando_room.union({'Ganons Tower - Firesnake Room'}))
|
||||||
|
val_rule(key_logic.door_rules['GT Hookshot ES'], 8, True, 'Ganons Tower - Map Chest', 6, {'Ganons Tower - Map Chest'})
|
||||||
|
val_rule(key_logic.door_rules['GT Tile Room EN'], 7, False, None, 5, compass_room)
|
||||||
|
val_rule(key_logic.door_rules['GT Firesnake Room SW'], 8, False, None, 6, rando_room)
|
||||||
|
val_rule(key_logic.door_rules['GT Conveyor Star Pits EN'], 8, False, None, 6, gt_middle)
|
||||||
|
val_rule(key_logic.door_rules['GT Mini Helmasaur Room WN'], 7)
|
||||||
|
val_rule(key_logic.door_rules['GT Crystal Circles SW'], 8)
|
||||||
|
assert world.get_location('Ganons Tower - Mini Helmasaur Room - Left', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Ganons Tower - Mini Helmasaur Room - Right', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Ganons Tower - Big Chest', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Ganons Tower - Pre-Moldorm Chest', player) in key_logic.bk_restricted
|
||||||
|
assert world.get_location('Ganons Tower - Validation Chest', player) in key_logic.bk_restricted
|
||||||
|
assert len(key_logic.bk_restricted) == 5
|
||||||
|
|
||||||
|
|
||||||
|
def val_rule(rule, skn, allow=False, loc=None, askn=None, setCheck=None):
|
||||||
|
if setCheck is None:
|
||||||
|
setCheck = set()
|
||||||
|
assert rule.small_key_num == skn
|
||||||
|
assert rule.allow_small == allow
|
||||||
|
assert rule.small_location == loc or rule.small_location.name == loc
|
||||||
|
assert rule.alternate_small_key == askn
|
||||||
|
assert len(setCheck) == len(rule.alternate_big_key_loc)
|
||||||
|
for loc in rule.alternate_big_key_loc:
|
||||||
|
assert loc.name in setCheck
|
||||||
|
|||||||
@@ -438,8 +438,8 @@ def create_regions(world, player):
|
|||||||
create_dungeon_region(player, 'Skull Star Pits', 'Skull Woods', None, ['Skull Star Pits SW', 'Skull Star Pits WS']),
|
create_dungeon_region(player, 'Skull Star Pits', 'Skull Woods', None, ['Skull Star Pits SW', 'Skull Star Pits WS']),
|
||||||
create_dungeon_region(player, 'Skull Torch Room', 'Skull Woods', None, ['Skull Torch Room ES', 'Skull Torch Room EN']),
|
create_dungeon_region(player, 'Skull Torch Room', 'Skull Woods', None, ['Skull Torch Room ES', 'Skull Torch Room EN']),
|
||||||
create_dungeon_region(player, 'Skull Vines', 'Skull Woods', None, ['Skull Vines WN', 'Skull Vines NW']),
|
create_dungeon_region(player, 'Skull Vines', 'Skull Woods', None, ['Skull Vines WN', 'Skull Vines NW']),
|
||||||
create_dungeon_region(player, 'Skull Spike Corner', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop'], ['Skull Spike Corner SW', 'Skull Spike Corner WS']),
|
create_dungeon_region(player, 'Skull Spike Corner', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop'], ['Skull Spike Corner SW', 'Skull Spike Corner ES']),
|
||||||
create_dungeon_region(player, 'Skull Final Drop', 'Skull Woods', None, ['Skull Final Drop ES', 'Skull Final Drop Hole']),
|
create_dungeon_region(player, 'Skull Final Drop', 'Skull Woods', None, ['Skull Final Drop WS', 'Skull Final Drop Hole']),
|
||||||
create_dungeon_region(player, 'Skull Boss', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
|
create_dungeon_region(player, 'Skull Boss', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
|
||||||
|
|
||||||
# tt
|
# tt
|
||||||
|
|||||||
56
Rules.py
56
Rules.py
@@ -265,8 +265,6 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Eastern Map Balcony Hook Path', player), lambda state: state.has('Hookshot', player))
|
set_rule(world.get_entrance('Eastern Map Balcony Hook Path', player), lambda state: state.has('Hookshot', player))
|
||||||
# Big key rules
|
# Big key rules
|
||||||
set_rule(world.get_location('Eastern Palace - Big Chest', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
set_rule(world.get_location('Eastern Palace - Big Chest', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Eastern Palace - Big Chest', player), 'Big Key (Eastern Palace)', player)
|
|
||||||
set_rule(world.get_entrance('Eastern Big Key NE', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
set_rule(world.get_entrance('Eastern Big Key NE', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||||
set_rule(world.get_entrance('Eastern Courtyard N', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
set_rule(world.get_entrance('Eastern Courtyard N', player), lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||||
|
|
||||||
@@ -276,8 +274,6 @@ def global_rules(world, player):
|
|||||||
|
|
||||||
# Desert
|
# Desert
|
||||||
set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player))
|
set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Desert Palace - Big Chest', player), 'Big Key (Desert Palace)', player)
|
|
||||||
set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has_Boots(player))
|
set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has_Boots(player))
|
||||||
set_rule(world.get_entrance('Desert Wall Slide NW', player), lambda state: state.has_fire_source(player))
|
set_rule(world.get_entrance('Desert Wall Slide NW', player), lambda state: state.has_fire_source(player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Desert Palace - Prize', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Desert Palace - Prize', player))
|
||||||
@@ -285,8 +281,6 @@ def global_rules(world, player):
|
|||||||
|
|
||||||
# Tower of Hera
|
# Tower of Hera
|
||||||
set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player))
|
set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Tower of Hera - Big Chest', player), 'Big Key (Tower of Hera)', player)
|
|
||||||
set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player))
|
set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player))
|
||||||
set_rule(world.get_entrance('Hera Startile Corner NW', player), lambda state: state.has('Big Key (Tower of Hera)', player))
|
set_rule(world.get_entrance('Hera Startile Corner NW', player), lambda state: state.has('Big Key (Tower of Hera)', player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player))
|
||||||
@@ -299,8 +293,6 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('PoD Bow Statue Down Ladder', player), lambda state: state.can_shoot_arrows(player))
|
set_rule(world.get_entrance('PoD Bow Statue Down Ladder', player), lambda state: state.can_shoot_arrows(player))
|
||||||
set_rule(world.get_entrance('PoD Dark Alley NE', player), lambda state: state.has('Big Key (Palace of Darkness)', player))
|
set_rule(world.get_entrance('PoD Dark Alley NE', player), lambda state: state.has('Big Key (Palace of Darkness)', player))
|
||||||
set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player))
|
set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Palace of Darkness - Big Chest', player), 'Big Key (Palace of Darkness)', player)
|
|
||||||
set_rule(world.get_entrance('PoD Map Balcony Drop Down', player), lambda state: state.has('Hammer', player))
|
set_rule(world.get_entrance('PoD Map Balcony Drop Down', player), lambda state: state.has('Hammer', player))
|
||||||
set_rule(world.get_entrance('PoD Dark Pegs WN', player), lambda state: state.has('Hammer', player))
|
set_rule(world.get_entrance('PoD Dark Pegs WN', player), lambda state: state.has('Hammer', player))
|
||||||
set_rule(world.get_entrance('PoD Dark Pegs Up Ladder', player), lambda state: state.has('Hammer', player))
|
set_rule(world.get_entrance('PoD Dark Pegs Up Ladder', player), lambda state: state.has('Hammer', player))
|
||||||
@@ -336,15 +328,11 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Swamp Waterway NE', player), lambda state: state.has('Flippers', player))
|
set_rule(world.get_entrance('Swamp Waterway NE', player), lambda state: state.has('Flippers', player))
|
||||||
set_rule(world.get_location('Swamp Palace - Waterway Pot Key', player), lambda state: state.has('Flippers', player))
|
set_rule(world.get_location('Swamp Palace - Waterway Pot Key', player), lambda state: state.has('Flippers', player))
|
||||||
set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player))
|
set_rule(world.get_location('Swamp Palace - Big Chest', player), lambda state: state.has('Big Key (Swamp Palace)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Swamp Palace - Big Chest', player), 'Big Key (Swamp Palace)', player)
|
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player))
|
||||||
|
|
||||||
set_rule(world.get_entrance('Skull Big Chest Hookpath', player), lambda state: state.has('Hookshot', player))
|
set_rule(world.get_entrance('Skull Big Chest Hookpath', player), lambda state: state.has('Hookshot', player))
|
||||||
set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player))
|
set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Skull Woods - Big Chest', player), 'Big Key (Skull Woods)', player)
|
|
||||||
set_rule(world.get_entrance('Skull Torch Room EN', player), lambda state: state.has('Fire Rod', player))
|
set_rule(world.get_entrance('Skull Torch Room EN', player), lambda state: state.has('Fire Rod', player))
|
||||||
set_rule(world.get_entrance('Skull Vines NW', player), lambda state: state.has_sword(player))
|
set_rule(world.get_entrance('Skull Vines NW', player), lambda state: state.has_sword(player))
|
||||||
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player))
|
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player))
|
||||||
@@ -353,8 +341,6 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Thieves BK Corner NE', player), lambda state: state.has('Big Key (Thieves Town)', player))
|
set_rule(world.get_entrance('Thieves BK Corner NE', player), lambda state: state.has('Big Key (Thieves Town)', player))
|
||||||
# blind can't have the small key? - not necessarily true anymore - but likely still
|
# blind can't have the small key? - not necessarily true anymore - but likely still
|
||||||
set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state.has('Big Key (Thieves Town)', player) and state.has('Hammer', player)))
|
set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state.has('Big Key (Thieves Town)', player) and state.has('Hammer', player)))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Thieves\' Town - Big Chest', player), 'Big Key (Thieves Town)', player)
|
|
||||||
for entrance in ['Thieves Basement Block Path', 'Thieves Blocked Entry Path', 'Thieves Conveyor Block Path', 'Thieves Conveyor Bridge Block Path']:
|
for entrance in ['Thieves Basement Block Path', 'Thieves Blocked Entry Path', 'Thieves Conveyor Block Path', 'Thieves Conveyor Bridge Block Path']:
|
||||||
set_rule(world.get_entrance(entrance, player), lambda state: state.can_lift_rocks(player))
|
set_rule(world.get_entrance(entrance, player), lambda state: state.can_lift_rocks(player))
|
||||||
for location in ['Thieves\' Town - Blind\'s Cell', 'Thieves\' Town - Boss']:
|
for location in ['Thieves\' Town - Blind\'s Cell', 'Thieves\' Town - Boss']:
|
||||||
@@ -368,8 +354,6 @@ def global_rules(world, player):
|
|||||||
|
|
||||||
set_rule(world.get_entrance('Ice Lobby WS', player), lambda state: state.can_melt_things(player))
|
set_rule(world.get_entrance('Ice Lobby WS', player), lambda state: state.can_melt_things(player))
|
||||||
set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player))
|
set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Ice Palace - Big Chest', player), 'Big Key (Ice Palace)', player)
|
|
||||||
set_rule(world.get_entrance('Ice Hammer Block ES', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
|
set_rule(world.get_entrance('Ice Hammer Block ES', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
|
||||||
set_rule(world.get_location('Ice Palace - Hammer Block Key Drop', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
|
set_rule(world.get_location('Ice Palace - Hammer Block Key Drop', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
|
||||||
set_rule(world.get_location('Ice Palace - Map Chest', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
|
set_rule(world.get_location('Ice Palace - Map Chest', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player))
|
||||||
@@ -390,8 +374,6 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Mire Falling Bridge WN', player), lambda state: state.has_Boots(player) or state.has('Hookshot', player)) # this is due to the fact the the door opposite is blocked
|
set_rule(world.get_entrance('Mire Falling Bridge WN', player), lambda state: state.has_Boots(player) or state.has('Hookshot', player)) # this is due to the fact the the door opposite is blocked
|
||||||
set_rule(world.get_entrance('Mire 2 NE', player), lambda state: state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player)) # need to defeat wizzrobes, bombs don't work ...
|
set_rule(world.get_entrance('Mire 2 NE', player), lambda state: state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player)) # need to defeat wizzrobes, bombs don't work ...
|
||||||
set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player) and (state.has_Boots(player) or state.has('Hookshot', player)))
|
set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player) and (state.has_Boots(player) or state.has('Hookshot', player)))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Misery Mire - Big Chest', player), 'Big Key (Misery Mire)', player)
|
|
||||||
set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player))
|
set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player))
|
||||||
set_rule(world.get_entrance('Mire BK Door Room N', player), lambda state: state.has('Big Key (Misery Mire)', player))
|
set_rule(world.get_entrance('Mire BK Door Room N', player), lambda state: state.has('Big Key (Misery Mire)', player))
|
||||||
set_rule(world.get_entrance('Mire Square Rail NW', player), lambda state: state.has('Big Key (Misery Mire)', player))
|
set_rule(world.get_entrance('Mire Square Rail NW', player), lambda state: state.has('Big Key (Misery Mire)', player))
|
||||||
@@ -413,8 +395,6 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('TR Hub NE', player), lambda state: state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('TR Hub NE', player), lambda state: state.has('Cane of Somaria', player))
|
||||||
set_rule(world.get_entrance('TR Torches NW', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
|
set_rule(world.get_entrance('TR Torches NW', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
|
||||||
set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player))
|
set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Turtle Rock - Big Chest', player), 'Big Key (Turtle Rock)', player)
|
|
||||||
set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
||||||
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
|
||||||
set_rule(world.get_entrance('TR Dodgers NE', player), lambda state: state.has('Big Key (Turtle Rock)', player))
|
set_rule(world.get_entrance('TR Dodgers NE', player), lambda state: state.has('Big Key (Turtle Rock)', player))
|
||||||
@@ -443,8 +423,6 @@ def global_rules(world, player):
|
|||||||
set_rule(world.get_entrance('GT Firesnake Room Hook Path', player), lambda state: state.has('Hookshot', player))
|
set_rule(world.get_entrance('GT Firesnake Room Hook Path', player), lambda state: state.has('Hookshot', player))
|
||||||
# I am tempted to stick an invincibility rule for getting across falling bridge
|
# I am tempted to stick an invincibility rule for getting across falling bridge
|
||||||
set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player))
|
set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player))
|
||||||
if world.accessibility == 'locations':
|
|
||||||
forbid_item(world.get_location('Ganons Tower - Big Chest', player), 'Big Key (Ganons Tower)', player)
|
|
||||||
set_rule(world.get_entrance('GT Ice Armos NE', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state))
|
set_rule(world.get_entrance('GT Ice Armos NE', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state))
|
||||||
set_rule(world.get_entrance('GT Ice Armos WS', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state))
|
set_rule(world.get_entrance('GT Ice Armos WS', player), lambda state: world.get_region('GT Ice Armos', player).dungeon.bosses['bottom'].can_defeat(state))
|
||||||
|
|
||||||
@@ -1700,12 +1678,10 @@ def set_inverted_bunny_rules(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def add_key_logic_rules(world, player):
|
def add_key_logic_rules(world, player):
|
||||||
logger = logging.getLogger('')
|
|
||||||
key_logic = world.key_logic[player]
|
key_logic = world.key_logic[player]
|
||||||
for d_name, d_logic in key_logic.items():
|
for d_name, d_logic in key_logic.items():
|
||||||
for door_name, keys in d_logic.door_rules.items():
|
for door_name, keys in d_logic.door_rules.items():
|
||||||
logger.debug(' %s needs %s keys', door_name, keys)
|
add_rule(world.get_entrance(door_name, player), create_advanced_key_rule(d_logic, player, keys))
|
||||||
add_rule(world.get_entrance(door_name, player), create_key_rule(d_logic.small_key_name, player, keys))
|
|
||||||
for location in d_logic.bk_restricted:
|
for location in d_logic.bk_restricted:
|
||||||
if location.name not in key_only_locations.keys():
|
if location.name not in key_only_locations.keys():
|
||||||
forbid_item(location, d_logic.bk_name, player)
|
forbid_item(location, d_logic.bk_name, player)
|
||||||
@@ -1717,5 +1693,31 @@ def create_key_rule(small_key_name, player, keys):
|
|||||||
return lambda state: state.has_key(small_key_name, player, keys)
|
return lambda state: state.has_key(small_key_name, player, keys)
|
||||||
|
|
||||||
|
|
||||||
def create_forced_small_rule(small_key_name, player):
|
def create_key_rule_allow_small(small_key_name, player, keys, location):
|
||||||
return lambda item: item.name == small_key_name and item.player == player
|
loc = location.name
|
||||||
|
return lambda state: state.has_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_key(small_key_name, player, keys-1))
|
||||||
|
|
||||||
|
|
||||||
|
def create_key_rule_bk_exception(small_key_name, big_key_name, player, keys, bk_keys, bk_locs):
|
||||||
|
chest_names = [x.name for x in bk_locs]
|
||||||
|
return lambda state: state.has_key(small_key_name, player, keys) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_key(small_key_name, player, bk_keys))
|
||||||
|
|
||||||
|
|
||||||
|
def create_key_rule_bk_exception_or_allow(small_key_name, big_key_name, player, keys, location, bk_keys, bk_locs):
|
||||||
|
loc = location.name
|
||||||
|
chest_names = [x.name for x in bk_locs]
|
||||||
|
return lambda state: state.has_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_key(small_key_name, player, keys-1)) or (item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names))) and state.has_key(small_key_name, player, bk_keys))
|
||||||
|
|
||||||
|
|
||||||
|
def create_advanced_key_rule(key_logic, player, rule):
|
||||||
|
if not rule.allow_small and rule.alternate_small_key is None:
|
||||||
|
return create_key_rule(key_logic.small_key_name, player, rule.small_key_num)
|
||||||
|
if rule.allow_small and rule.alternate_small_key is None:
|
||||||
|
return create_key_rule_allow_small(key_logic.small_key_name, player, rule.small_key_num, rule.small_location)
|
||||||
|
if not rule.allow_small and rule.alternate_small_key is not None:
|
||||||
|
return create_key_rule_bk_exception(key_logic.small_key_name, key_logic.bk_name, player, rule.small_key_num,
|
||||||
|
rule.alternate_small_key, rule.alternate_big_key_loc)
|
||||||
|
if rule.allow_small and rule.alternate_small_key is not None:
|
||||||
|
return create_key_rule_bk_exception_or_allow(key_logic.small_key_name, key_logic.bk_name, player,
|
||||||
|
rule.small_key_num, rule.small_location, rule.alternate_small_key,
|
||||||
|
rule.alternate_big_key_loc)
|
||||||
|
|||||||
Reference in New Issue
Block a user