Logic added for openable trap doors

This commit is contained in:
aerinon
2023-08-03 15:06:54 -06:00
parent c0c3204fd5
commit f442cff061
6 changed files with 271 additions and 15 deletions

View File

@@ -1760,6 +1760,7 @@ class Door(object):
self.dest = None self.dest = None
self.blocked = False # Indicates if the door is normally blocked off as an exit. (Sanc door or always closed) self.blocked = False # Indicates if the door is normally blocked off as an exit. (Sanc door or always closed)
self.blocked_orig = False self.blocked_orig = False
self.trapped = False
self.stonewall = False # Indicate that the door cannot be enter until exited (Desert Torches, PoD Eye Statue) self.stonewall = False # Indicate that the door cannot be enter until exited (Desert Torches, PoD Eye Statue)
self.smallKey = False # There's a small key door on this side self.smallKey = False # There's a small key door on this side
self.bigKey = False # There's a big key door on this side self.bigKey = False # There's a big key door on this side
@@ -1870,7 +1871,7 @@ class Door(object):
return self return self
def no_exit(self): def no_exit(self):
self.blocked = self.blocked_orig = True self.blocked = self.blocked_orig = self.trapped = True
return self return self
def no_entrance(self): def no_entrance(self):

View File

@@ -88,8 +88,7 @@ def link_doors_prep(world, player):
find_inaccessible_regions(world, player) find_inaccessible_regions(world, player)
if world.doorShuffle[player] != 'vanilla': create_dungeon_pool(world, player)
create_dungeon_pool(world, player)
if world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla': if world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla':
choose_portals(world, player) choose_portals(world, player)
else: else:
@@ -1844,12 +1843,12 @@ def shuffle_trap_doors(door_type_pools, paths, start_regions_map, all_custom, wo
builder.candidates.trap = filter_key_door_pool(builder.candidates.trap, all_custom[dungeon]) builder.candidates.trap = filter_key_door_pool(builder.candidates.trap, all_custom[dungeon])
remaining -= len(custom_trap_doors[dungeon]) remaining -= len(custom_trap_doors[dungeon])
ttl += len(builder.candidates.trap) ttl += len(builder.candidates.trap)
if ttl == 0: if ttl == 0 and all(len(custom_trap_doors[dungeon]) == 0 for dungeon in pool):
continue continue
for dungeon in pool: for dungeon in pool:
builder = world.dungeon_layouts[player][dungeon] builder = world.dungeon_layouts[player][dungeon]
proportion = len(builder.candidates.trap) proportion = len(builder.candidates.trap)
calc = int(round(proportion * door_type_pool.traps/ttl)) calc = 0 if ttl == 0 else int(round(proportion * door_type_pool.traps/ttl))
suggested = min(proportion, calc) suggested = min(proportion, calc)
remaining -= suggested remaining -= suggested
suggestion_map[dungeon] = suggested suggestion_map[dungeon] = suggested
@@ -1981,7 +1980,10 @@ def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, all_
remaining = max(0, remaining) remaining = max(0, remaining)
for dungeon in pool: for dungeon in pool:
builder = world.dungeon_layouts[player][dungeon] builder = world.dungeon_layouts[player][dungeon]
calculated = int(round(builder.key_doors_num*total_keys/ttl)) if ttl == 0:
calculated = 0
else:
calculated = int(round(builder.key_doors_num*total_keys/ttl))
max_keys = max(0, builder.location_cnt - calc_used_dungeon_items(builder, world, player)) max_keys = max(0, builder.location_cnt - calc_used_dungeon_items(builder, world, player))
cand_len = max(0, len(builder.candidates.small) - builder.key_drop_cnt) cand_len = max(0, len(builder.candidates.small) - builder.key_drop_cnt)
limit = min(max_keys, cand_len, max_computation) limit = min(max_keys, cand_len, max_computation)
@@ -2211,9 +2213,10 @@ def find_valid_trap_combination(builder, suggested, start_regions, paths, world,
sample_list = build_sample_list(combinations, 1000) sample_list = build_sample_list(combinations, 1000)
proposal = kth_combination(sample_list[itr], trap_door_pool, trap_doors_needed) proposal = kth_combination(sample_list[itr], trap_door_pool, trap_doors_needed)
proposal.extend(custom_trap_doors) proposal.extend(custom_trap_doors)
filtered_proposal = [x for x in proposal if x.name not in trap_door_exceptions]
start_regions, event_starts = filter_start_regions(builder, start_regions, world, player) start_regions, event_starts = filter_start_regions(builder, start_regions, world, player)
while not validate_trap_layout(proposal, builder, start_regions, paths, world, player): while not validate_trap_layout(filtered_proposal, builder, start_regions, paths, world, player):
itr += 1 itr += 1
if itr >= len(sample_list): if itr >= len(sample_list):
if not drop: if not drop:
@@ -2248,6 +2251,12 @@ def filter_start_regions(builder, start_regions, world, player):
portal_entrance_region = portal.door.entrance.parent_region.name portal_entrance_region = portal.door.entrance.parent_region.name
if portal_entrance_region not in builder.path_entrances: if portal_entrance_region not in builder.path_entrances:
excluded[region] = None excluded[region] = None
if not portal:
drop_region = next((x.parent_region for x in region.entrances
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]
or x.parent_region.name == 'Sewer Drop'), None)
if drop_region and drop_region.name in world.inaccessible_regions[player]:
excluded[region] = None
if std_flag and (not portal or portal.find_portal_entrance().parent_region.name != 'Hyrule Castle Courtyard'): if std_flag and (not portal or portal.find_portal_entrance().parent_region.name != 'Hyrule Castle Courtyard'):
excluded[region] = None excluded[region] = None
if portal is None: if portal is None:
@@ -2343,10 +2352,12 @@ def reassign_trap_doors(trap_map, world, player):
elif kind in [DoorKind.Trap2, DoorKind.TrapTriggerable]: elif kind in [DoorKind.Trap2, DoorKind.TrapTriggerable]:
room.change(d.doorListPos, DoorKind.Normal) room.change(d.doorListPos, DoorKind.Normal)
d.blocked = False d.blocked = False
d.trapped = False
# connect_one_way(world, d.name, d.dest.name, player) # connect_one_way(world, d.name, d.dest.name, player)
elif d.type is DoorType.Normal and d not in traps: elif d.type is DoorType.Normal and d not in traps:
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal) world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
d.blocked = False d.blocked = False
d.trapped = False
for d in traps: for d in traps:
change_door_to_trap(d, world, player) change_door_to_trap(d, world, player)
world.spoiler.set_door_type(f'{d.name} ({d.dungeon_name()})', 'Trap Door', player) world.spoiler.set_door_type(f'{d.name} ({d.dungeon_name()})', 'Trap Door', player)
@@ -2384,24 +2395,45 @@ def change_door_to_trap(d, world, player):
elif d.direction in [Direction.North, Direction.West]: elif d.direction in [Direction.North, Direction.West]:
new_kind = DoorKind.TrapTriggerable new_kind = DoorKind.TrapTriggerable
if new_kind: if new_kind:
d.blocked = True d.blocked = is_trap_door_blocked(d)
d.trapped = True
pos = 3 if d.type == DoorType.Normal else 4 pos = 3 if d.type == DoorType.Normal else 4
verify_door_list_pos(d, room, world, player, pos) verify_door_list_pos(d, room, world, player, pos)
d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1, 3: 0x8}[d.doorListPos] d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1, 3: 0x8}[d.doorListPos]
room.change(d.doorListPos, new_kind) room.change(d.doorListPos, new_kind)
if d.entrance.connected_region is not None: if d.entrance.connected_region is not None and d.blocked:
d.entrance.connected_region.entrances.remove(d.entrance) d.entrance.connected_region.entrances.remove(d.entrance)
d.entrance.connected_region = None d.entrance.connected_region = None
elif d.type is DoorType.Normal: elif d.type is DoorType.Normal:
d.blocked = True d.blocked = is_trap_door_blocked(d)
d.trapped = True
verify_door_list_pos(d, room, world, player, pos=3) verify_door_list_pos(d, room, world, player, pos=3)
d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1}[d.doorListPos] d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1}[d.doorListPos]
room.change(d.doorListPos, DoorKind.Trap) room.change(d.doorListPos, DoorKind.Trap)
if d.entrance.connected_region is not None: if d.entrance.connected_region is not None and d.blocked:
d.entrance.connected_region.entrances.remove(d.entrance) d.entrance.connected_region.entrances.remove(d.entrance)
d.entrance.connected_region = None d.entrance.connected_region = None
trap_door_exceptions = {
'PoD Mimics 2 SW', 'TR Twin Pokeys NW', 'Thieves Blocked Entry SW', 'Hyrule Dungeon Armory Interior Key Door N',
'Desert Compass Key Door WN', 'TR Tile Room SE', 'Mire Cross SW', 'Tower Circle of Pots ES',
'Eastern Single Eyegore ES', 'Eastern Duo Eyegores SE', 'Swamp Push Statue S',
'Skull 2 East Lobby WS', 'GT Hope Room WN', 'Eastern Courtyard Ledge S', 'Ice Lobby SE', 'GT Speed Torch WN',
'Ice Switch Room ES', 'Ice Switch Room NE', 'Skull Torch Room WS', 'GT Speed Torch NE', 'GT Speed Torch WS',
'GT Torch Cross WN', 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Torches WN', 'PoD Lobby N', 'PoD Middle Cage S',
'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'GT Conveyor Cross EN', 'Eastern Pot Switch WN',
'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Eastern Dark Square Key Door WN', 'Eastern Lobby NW',
'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Desert Back Lobby S', 'Desert West S',
'Desert West Lobby ES', 'Mire Hidden Shooters SE', 'Mire Hidden Shooters ES', 'Mire Hidden Shooters WS',
'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS',
}
def is_trap_door_blocked(door):
return door.name not in trap_door_exceptions
def find_big_key_candidates(builder, start_regions, used, world, player): def find_big_key_candidates(builder, start_regions, used, world, player):
if world.door_type_mode[player] != 'original': # big, all, chaos if world.door_type_mode[player] != 'original': # big, all, chaos
# traverse dungeon and find candidates # traverse dungeon and find candidates

View File

@@ -34,7 +34,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new
from source.tools.BPS import create_bps_from_data from source.tools.BPS import create_bps_from_data
from source.classes.CustomSettings import CustomSettings from source.classes.CustomSettings import CustomSettings
version_number = '1.2.0.19' version_number = '1.2.0.20'
version_branch = '-u' version_branch = '-u'
__version__ = f'{version_number}{version_branch}' __version__ = f'{version_number}{version_branch}'

View File

@@ -109,6 +109,9 @@ These are now independent of retro mode and have three options: None, Random, an
# Bug Fixes and Notes # Bug Fixes and Notes
* 1.2.0.20u
* Added logic for trap doors that could be opened using existing room triggers
* Added a notes field for user added notes either via CLI or Customizer
* 1.2.0.19u * 1.2.0.19u
* Added min/max for triforce pool, goal, and difference for CLI and Customizer. (Thanks Catobat) * Added min/max for triforce pool, goal, and difference for CLI and Customizer. (Thanks Catobat)
* Fixed a bug with dungeon generation * Fixed a bug with dungeon generation

101
Rules.py
View File

@@ -276,11 +276,18 @@ def global_rules(world, player):
# Start of door rando rules # Start of door rando rules
# TODO: Do these need to flag off when door rando is off? - some of them, yes # TODO: Do these need to flag off when door rando is off? - some of them, yes
def is_trapped(entrance):
return world.get_entrance(entrance, player).door.trapped
# Eastern Palace # Eastern Palace
# Eyegore room needs a bow # Eyegore room needs a bow
set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(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))
if is_trapped('Eastern Single Eyegore ES'):
set_rule(world.get_entrance('Eastern Single Eyegore ES', player), lambda state: state.can_shoot_arrows(player))
if is_trapped('Eastern Duo Eyegores SE'):
set_rule(world.get_entrance('Eastern Duo Eyegores SE', player), lambda state: state.can_shoot_arrows(player))
# Boss rules. Same as below but no BK or arrow requirement. # Boss rules. Same as below but no BK or arrow requirement.
set_defeat_dungeon_boss_rule(world.get_location('Eastern Palace - Prize', player)) set_defeat_dungeon_boss_rule(world.get_location('Eastern Palace - Prize', player))
@@ -305,13 +312,18 @@ def global_rules(world, player):
set_rule(world.get_entrance('Tower Red Spears WN', player), lambda state: state.can_kill_most_things(player)) set_rule(world.get_entrance('Tower Red Spears WN', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('Tower Red Guards EN', player), lambda state: state.can_kill_most_things(player)) set_rule(world.get_entrance('Tower Red Guards EN', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('Tower Red Guards SW', player), lambda state: state.can_kill_most_things(player)) set_rule(world.get_entrance('Tower Red Guards SW', player), lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('Tower Circle of Pots NW', player), lambda state: state.can_kill_most_things(player))
if is_trapped('Tower Circle of Pots ES'):
set_rule(world.get_entrance('Tower Circle of Pots ES', player),
lambda state: state.can_kill_most_things(player))
set_rule(world.get_entrance('Tower Altar NW', player), lambda state: state.has_sword(player)) set_rule(world.get_entrance('Tower Altar NW', player), lambda state: state.has_sword(player))
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player)) set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player))
set_rule(world.get_entrance('PoD Arena Landing Bonk Path', player), lambda state: state.has_Boots(player)) set_rule(world.get_entrance('PoD Arena Landing Bonk Path', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player))
set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player))
if is_trapped('PoD Mimics 2 SW'):
set_rule(world.get_entrance('PoD Mimics 2 SW', 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 Bow Statue Down Ladder', player), lambda state: state.can_shoot_arrows(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 Landing to Right', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('PoD Dark Pegs Landing to Right', player), lambda state: state.has('Hammer', player))
@@ -360,6 +372,8 @@ def global_rules(world, 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_entrance('Skull Torch Room WN', player), lambda state: state.has('Fire Rod', player)) set_rule(world.get_entrance('Skull Torch Room WN', player), lambda state: state.has('Fire Rod', player))
if is_trapped('Skull Torch Room WS'):
set_rule(world.get_entrance('Skull Torch Room WS', 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))
hidden_pits_door = world.get_door('Skull Small Hall WS', player) hidden_pits_door = world.get_door('Skull Small Hall WS', player)
@@ -397,6 +411,8 @@ def global_rules(world, player):
set_rule(world.get_location('Thieves\' Town - Prize', player), lambda state: state.has('Maiden Unmasked', player) and world.get_location('Thieves\' Town - Prize', player).parent_region.dungeon.boss.can_defeat(state)) set_rule(world.get_location('Thieves\' Town - Prize', player), lambda state: state.has('Maiden Unmasked', player) and world.get_location('Thieves\' Town - Prize', player).parent_region.dungeon.boss.can_defeat(state))
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))
if is_trapped('Ice Lobby SE'):
set_rule(world.get_entrance('Ice Lobby SE', player), lambda state: state.can_melt_things(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))
@@ -411,6 +427,12 @@ def global_rules(world, player):
set_rule(world.get_entrance('Ice Hookshot Balcony Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Ice Hookshot Balcony Path', player), lambda state: state.has('Hookshot', player))
if not world.get_door('Ice Switch Room SE', player).entranceFlag: if not world.get_door('Ice Switch Room SE', player).entranceFlag:
set_rule(world.get_entrance('Ice Switch Room SE', player), lambda state: state.has('Cane of Somaria', player) or state.has('Convenient Block', player)) set_rule(world.get_entrance('Ice Switch Room SE', player), lambda state: state.has('Cane of Somaria', player) or state.has('Convenient Block', player))
if is_trapped('Ice Switch Room ES'):
set_rule(world.get_entrance('Ice Switch Room ES', player),
lambda state: state.has('Cane of Somaria', player) or state.has('Convenient Block', player))
if is_trapped('Ice Switch Room NE'):
set_rule(world.get_entrance('Ice Switch Room NE', player),
lambda state: state.has('Cane of Somaria', player) or state.has('Convenient Block', player))
set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Prize', player)) set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Prize', player))
@@ -431,8 +453,15 @@ def global_rules(world, player):
or state.has('Cane of Byrna', player) or state.has('Cape', player)) or state.has('Cane of Byrna', player) or state.has('Cape', player))
set_rule(world.get_entrance('Mire Left Bridge Hook Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Mire Left Bridge Hook Path', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('Mire Tile Room NW', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('Mire Tile Room NW', player), lambda state: state.has_fire_source(player))
if is_trapped('Mire Tile Room SW'):
set_rule(world.get_entrance('Mire Tile Room SW', player), lambda state: state.has_fire_source(player))
if is_trapped('Mire Tile Room ES'):
set_rule(world.get_entrance('Mire Tile Room ES', player), lambda state: state.has_fire_source(player))
set_rule(world.get_entrance('Mire Attic Hint Hole', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('Mire Attic Hint Hole', player), lambda state: state.has_fire_source(player))
set_rule(world.get_entrance('Mire Dark Shooters SW', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Mire Dark Shooters SW', player), lambda state: state.has('Cane of Somaria', player))
if is_trapped('Mire Dark Shooters SE'):
set_rule(world.get_entrance('Mire Dark Shooters SE', player),
lambda state: state.has('Cane of Somaria', player))
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player)) set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player))
@@ -448,6 +477,9 @@ def global_rules(world, player):
set_rule(world.get_entrance('TR Hub Path', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Hub Path', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Hub Ledges Path', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Hub Ledges Path', 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))
if is_trapped('TR Torches WN'):
set_rule(world.get_entrance('TR Torches WN', player),
lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', 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_Boots(player)) set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has_Boots(player))
set_rule(world.get_entrance('TR Dark Ride Up Stairs', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Dark Ride Up Stairs', player), lambda state: state.has('Cane of Somaria', player))
@@ -467,10 +499,20 @@ def global_rules(world, player):
set_rule(world.get_location('Ganons Tower - Bob\'s Torch', player), lambda state: state.has_Boots(player)) set_rule(world.get_location('Ganons Tower - Bob\'s Torch', player), lambda state: state.has_Boots(player))
set_rule(world.get_entrance('GT Hope Room EN', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('GT Hope Room EN', player), lambda state: state.has('Cane of Somaria', player))
if is_trapped('GT Hope Room WN'):
set_rule(world.get_entrance('GT Hope Room WN', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('GT Conveyor Cross Hammer Path', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('GT Conveyor Cross Hammer Path', player), lambda state: state.has('Hammer', player))
set_rule(world.get_entrance('GT Conveyor Cross Hookshot Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('GT Conveyor Cross Hookshot Path', player), lambda state: state.has('Hookshot', player))
if is_trapped('GT Conveyor Cross EN'):
set_rule(world.get_entrance('GT Conveyor Cross EN', player), lambda state: state.has('Hammer', player))
if not world.get_door('GT Speed Torch SE', player).entranceFlag: if not world.get_door('GT Speed Torch SE', player).entranceFlag:
set_rule(world.get_entrance('GT Speed Torch SE', player), lambda state: state.has('Fire Rod', player)) set_rule(world.get_entrance('GT Speed Torch SE', player), lambda state: state.has('Fire Rod', player))
if is_trapped('GT Speed Torch NE'):
set_rule(world.get_entrance('GT Speed Torch NE', player), lambda state: state.has('Fire Rod', player))
if is_trapped('GT Speed Torch WS'):
set_rule(world.get_entrance('GT Speed Torch WS', player), lambda state: state.has('Fire Rod', player))
if is_trapped('GT Speed Torch WN'):
set_rule(world.get_entrance('GT Speed Torch WN', player), lambda state: state.has('Fire Rod', player))
set_rule(world.get_entrance('GT Hookshot South-Mid Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('GT Hookshot South-Mid Path', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('GT Hookshot Mid-North Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('GT Hookshot Mid-North Path', player), lambda state: state.has('Hookshot', player))
set_rule(world.get_entrance('GT Hookshot East-Mid Path', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player)) set_rule(world.get_entrance('GT Hookshot East-Mid Path', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player))
@@ -505,6 +547,8 @@ def global_rules(world, player):
set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state)) set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
set_rule(world.get_entrance('GT Lanmolas 2 NW', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state)) set_rule(world.get_entrance('GT Lanmolas 2 NW', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player))
if is_trapped('GT Torch Cross WN'):
set_rule(world.get_entrance('GT Torch Cross WN', player), lambda state: state.has_fire_source(player))
set_rule(world.get_entrance('GT Falling Torches NE', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('GT Falling Torches NE', player), lambda state: state.has_fire_source(player))
# todo: the following only applies to crystal state propagation from this supertile # todo: the following only applies to crystal state propagation from this supertile
# you can also reset the supertile, but I'm not sure how to model that # you can also reset the supertile, but I'm not sure how to model that
@@ -760,13 +804,29 @@ def bomb_rules(world, player):
('GT Petting Zoo SE', False), # Dont make anyone do this room with bombs and/or pots. ('GT Petting Zoo SE', False), # Dont make anyone do this room with bombs and/or pots.
('GT DMs Room SW', False) # Four red stalfos ('GT DMs Room SW', False) # Four red stalfos
] ]
conditional_kill_traps = [
('Hyrule Dungeon Armory Interior Key Door N', True),
('Desert Compass Key Door WN', True),
('Thieves Blocked Entry SW', True),
('TR Tongue Pull WS', True),
('TR Twin Pokeys NW', False),
]
for killdoor,bombable in easy_kill_rooms: for killdoor,bombable in easy_kill_rooms:
if bombable: if bombable:
add_rule(world.get_entrance(killdoor, player), lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player))) add_rule(world.get_entrance(killdoor, player), lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player)))
else: else:
add_rule(world.get_entrance(killdoor, player), lambda state: state.can_kill_most_things(player)) add_rule(world.get_entrance(killdoor, player), lambda state: state.can_kill_most_things(player))
for kill_door, bombable in conditional_kill_traps:
if world.get_entrance(kill_door, player).door.trapped:
if bombable:
add_rule(world.get_entrance(kill_door, player),
lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player)))
else:
add_rule(world.get_entrance(kill_door, player), lambda state: state.can_kill_most_things(player))
add_rule(world.get_entrance('Ice Stalfos Hint SE', player), lambda state: state.can_use_bombs(player)) # Need bombs for big stalfos knights add_rule(world.get_entrance('Ice Stalfos Hint SE', player), lambda state: state.can_use_bombs(player)) # Need bombs for big stalfos knights
add_rule(world.get_entrance('Mire Cross ES', player), lambda state: state.can_kill_most_things(player)) # 4 Sluggulas. Bombs don't work // or (state.can_use_bombs(player) and state.has('Magic Powder'), player) add_rule(world.get_entrance('Mire Cross ES', player), lambda state: state.can_kill_most_things(player)) # 4 Sluggulas. Bombs don't work // or (state.can_use_bombs(player) and state.has('Magic Powder'), player)
if world.get_entrance('Mire Cross SW', player).door.trapped:
add_rule(world.get_entrance('Mire Cross SW', player), lambda state: state.can_kill_most_things(player))
enemy_kill_drops = [ # Location, bool-bombable enemy_kill_drops = [ # Location, bool-bombable
('Hyrule Castle - Map Guard Key Drop', True), ('Hyrule Castle - Map Guard Key Drop', True),
@@ -1143,6 +1203,9 @@ def swordless_rules(world, player):
set_rule(world.get_entrance('Tower Altar NW', player), lambda state: True) set_rule(world.get_entrance('Tower Altar NW', player), lambda state: True)
set_rule(world.get_entrance('Skull Vines NW', player), lambda state: True) set_rule(world.get_entrance('Skull Vines NW', player), lambda state: True)
set_rule(world.get_entrance('Ice Lobby WS', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) set_rule(world.get_entrance('Ice Lobby WS', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player))
if world.get_entrance('Ice Lobby SE', player).door.trapped:
set_rule(world.get_entrance('Ice Lobby SE', player),
lambda state: state.has('Fire Rod', player) or state.has('Bombos', player))
set_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) set_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player))
set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
@@ -1156,7 +1219,7 @@ def swordless_rules(world, player):
if world.mode[player] != 'inverted': if world.mode[player] != 'inverted':
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player)) set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player))
# todo: new traps
std_kill_rooms = { std_kill_rooms = {
'Hyrule Dungeon Armory Main': ['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], # One green guard 'Hyrule Dungeon Armory Main': ['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], # One green guard
'Hyrule Dungeon Armory Boomerang': ['Hyrule Dungeon Armory Boomerang WS'], # One blue guard 'Hyrule Dungeon Armory Boomerang': ['Hyrule Dungeon Armory Boomerang WS'], # One blue guard
@@ -1187,6 +1250,18 @@ std_kill_rooms = {
'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work 'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work
} # all trap rooms? } # all trap rooms?
std_kill_doors_if_trapped = {
'Hyrule Dungeon Armory Main': 'Hyrule Dungeon Armory Interior Key Door N',
# 'Eastern Single Eyegore ES', # arrow rule is sufficient
# 'Eastern Duo Eyegores S', # arrow rule is sufficient
'TR Twin Pokeys': 'TR Twin Pokeys NW',
'Thieves Basement Block': 'Thieves Blocked Entry SW',
'Desert Compass Room': 'Desert Compass Key Door WN',
'Mire Cross': 'Mire Cross SW',
'Tower Circle of Pots': 'Tower Circle of Pots ES',
# 'Ice Lobby S' # can melt rule is sufficient
}
def add_connection(parent_name, target_name, entrance_name, world, player): def add_connection(parent_name, target_name, entrance_name, world, player):
parent = world.get_region(parent_name, player) parent = world.get_region(parent_name, player)
target = world.get_region(target_name, player) target = world.get_region(target_name, player)
@@ -1241,6 +1316,10 @@ def standard_rules(world, player):
if region.name in std_kill_rooms: if region.name in std_kill_rooms:
for ent in std_kill_rooms[region.name]: for ent in std_kill_rooms[region.name]:
add_rule(world.get_entrance(ent, player), lambda state: standard_escape_rule(state)) add_rule(world.get_entrance(ent, player), lambda state: standard_escape_rule(state))
if region.name in std_kill_doors_if_trapped:
ent = world.get_entrance(std_kill_doors_if_trapped[region.name], player)
if ent.door.trapped:
add_rule(ent, lambda state: standard_escape_rule(state))
set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', player)) set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', player))
set_rule(world.get_entrance('Hyrule Castle Throne Room Tapestry', player), lambda state: state.has('Zelda Herself', player)) set_rule(world.get_entrance('Hyrule Castle Throne Room Tapestry', player), lambda state: state.has('Zelda Herself', player))
@@ -1866,6 +1945,11 @@ def set_bunny_rules(world, player, inverted):
if is_bunny(bunny_exit.parent_region): if is_bunny(bunny_exit.parent_region):
add_rule(bunny_exit, get_rule_to_add(bunny_exit.parent_region)) add_rule(bunny_exit, get_rule_to_add(bunny_exit.parent_region))
for ent_name in bunny_impassible_if_trapped:
bunny_exit = world.get_entrance(ent_name, player)
if bunny_exit.door.trapped and is_bunny(bunny_exit.parent_region):
add_rule(bunny_exit, get_rule_to_add(bunny_exit.parent_region))
doors_to_check = [x for x in world.doors if x.player == player and x not in bunny_impassible_doors] doors_to_check = [x for x in world.doors if x.player == player and x not in bunny_impassible_doors]
doors_to_check = [x for x in doors_to_check if x.type in [DoorType.Normal, DoorType.Interior] and not x.blocked] doors_to_check = [x for x in doors_to_check if x.type in [DoorType.Normal, DoorType.Interior] and not x.blocked]
for door in doors_to_check: for door in doors_to_check:
@@ -1997,6 +2081,17 @@ bunny_impassible_doors = {
'GT Validation Block Path' 'GT Validation Block Path'
} }
bunny_impassible_if_trapped = {
'Hyrule Dungeon Armory Interior Key Door N', 'Eastern Pot Switch WN', 'Eastern Lobby NW',
'Eastern Lobby NE', 'Desert Compass Key Door WN', 'Tower Circle of Pots ES', 'PoD Mimics 2 SW',
'PoD Middle Cage S', 'Swamp Push Statue S', 'Skull 2 East Lobby WS', 'Skull Torch Room WS',
'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Thieves Blocked Entry SW', 'Ice Bomb Jump NW',
'Ice Tall Hint EN', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Mire Cross SW',
'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Twin Pokeys NW', 'TR Torches WN', 'GT Hope Room WN',
'GT Speed Torch NE', 'GT Speed Torch WS', 'GT Torch Cross WN', 'GT Hidden Spikes SE', 'GT Conveyor Cross EN',
'GT Speed Torch WN', 'Ice Lobby SE'
}
def add_key_logic_rules(world, player): def add_key_logic_rules(world, player):
key_logic = world.key_logic[player] key_logic = world.key_logic[player]

View File

@@ -0,0 +1,125 @@
meta:
players: 1
settings:
1:
door_shuffle: basic
intensity: 3
door_type_mode: all
doors:
1:
doors:
PoD Mimics 2 SW:
type: Trap Door
# TR Twin Pokeys NW: # not possible due to trap flags
# type: Trap Door
Thieves Blocked Entry SW:
type: Trap Door
Hyrule Dungeon Armory Interior Key Door N:
type: Trap Door
Desert Compass Key Door WN:
type: Trap Door
TR Tile Room SE:
type: Trap Door
# Mire Cross SW: # not possible due to trap flags
# type: Trap Door
Tower Circle of Pots ES:
type: Trap Door
Eastern Single Eyegore ES:
type: Trap Door
Eastern Duo Eyegores SE:
type: Trap Door
Swamp Push Statue S:
type: Trap Door
# Skull 2 East Lobby WS: # currently not possible due to trap flags
# type: Trap Door
GT Hope Room WN :
type: Trap Door
# Eastern Courtyard Ledge S: # currently not possible due to trap flags
# type: Trap Door
Ice Switch Room ES :
type: Trap Door
Ice Switch Room NE :
type: Trap Door
Skull Torch Room WS :
type: Trap Door
GT Speed Torch NE :
type: Trap Door
GT Speed Torch WS :
type: Trap Door
GT Torch Cross WN :
type: Trap Door
Mire Tile Room SW :
type: Trap Door
Mire Tile Room ES :
type: Trap Door
TR Torches WN :
type: Trap Door
PoD Lobby N:
type: Trap Door
PoD Middle Cage S:
type: Trap Door
Ice Bomb Jump NW:
type: Trap Door
GT Hidden Spikes SE:
type: Trap Door
Ice Tall Hint EN:
type: Trap Door
GT Conveyor Cross EN:
type: Trap Door
Eastern Pot Switch WN:
type: Trap Door
Thieves Conveyor Maze WN:
type: Trap Door
# Thieves Conveyor Maze SW: #not possible due to 4 door limit
# type: Trap Door
Eastern Dark Square Key Door WN:
type: Trap Door
Eastern Lobby NW:
type: Trap Door
Eastern Lobby NE:
type: Trap Door
# Ice Cross Bottom SE: # not possible due to trap flags
# type: Trap Door
Desert Back Lobby S:
type: Trap Door
# Desert West S: need enough lobbies for basic, should otherwise work
# type: Trap Door
Desert West Lobby ES:
type: Trap Door
# Mire Hidden Shooters SE: # not possible due to trap flags
# type: Trap Door
# Mire Hidden Shooters ES: # not possible due to trap flags
# type: Trap Door
Mire Hidden Shooters WS:
type: Trap Door
Tower Dark Pits EN:
type: Trap Door
Tower Dark Maze ES:
type: Trap Door
TR Tongue Pull WS:
type: Trap Door
# Lower layer: not valid
# Sewers Pull Switch N:
# type: Trap Door
# PoD Sexy Statue W: # not possible due to trap flags and low layer too, so likely not an exception
# type: Trap Door
# Not valid due to disappearing somaria block
# Mire Dark Shooters SE:
# type: Trap Door
# These triggers don't open doors
# Ice Compass Room NE:
# type: Trap Door
# Hera Torches NE:
# type: Trap Door
# Mire Spikes WS:
# type: Trap Door
# Mire Spikes SW:
# type: Trap Door
# Mire Spikes NW:
# type: Trap Door
# Tower Room 03 WN:
# type: Trap Door