Logic added for openable trap doors
This commit is contained in:
@@ -1760,6 +1760,7 @@ class Door(object):
|
||||
self.dest = None
|
||||
self.blocked = False # Indicates if the door is normally blocked off as an exit. (Sanc door or always closed)
|
||||
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.smallKey = False # There's a small 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
|
||||
|
||||
def no_exit(self):
|
||||
self.blocked = self.blocked_orig = True
|
||||
self.blocked = self.blocked_orig = self.trapped = True
|
||||
return self
|
||||
|
||||
def no_entrance(self):
|
||||
|
||||
@@ -88,8 +88,7 @@ def link_doors_prep(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':
|
||||
choose_portals(world, player)
|
||||
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])
|
||||
remaining -= len(custom_trap_doors[dungeon])
|
||||
ttl += len(builder.candidates.trap)
|
||||
if ttl == 0:
|
||||
if ttl == 0 and all(len(custom_trap_doors[dungeon]) == 0 for dungeon in pool):
|
||||
continue
|
||||
for dungeon in pool:
|
||||
builder = world.dungeon_layouts[player][dungeon]
|
||||
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)
|
||||
remaining -= 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)
|
||||
for dungeon in pool:
|
||||
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))
|
||||
cand_len = max(0, len(builder.candidates.small) - builder.key_drop_cnt)
|
||||
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)
|
||||
proposal = kth_combination(sample_list[itr], trap_door_pool, trap_doors_needed)
|
||||
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)
|
||||
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
|
||||
if itr >= len(sample_list):
|
||||
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
|
||||
if portal_entrance_region not in builder.path_entrances:
|
||||
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'):
|
||||
excluded[region] = None
|
||||
if portal is None:
|
||||
@@ -2343,10 +2352,12 @@ def reassign_trap_doors(trap_map, world, player):
|
||||
elif kind in [DoorKind.Trap2, DoorKind.TrapTriggerable]:
|
||||
room.change(d.doorListPos, DoorKind.Normal)
|
||||
d.blocked = False
|
||||
d.trapped = False
|
||||
# connect_one_way(world, d.name, d.dest.name, player)
|
||||
elif d.type is DoorType.Normal and d not in traps:
|
||||
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
|
||||
d.blocked = False
|
||||
d.trapped = False
|
||||
for d in traps:
|
||||
change_door_to_trap(d, world, 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]:
|
||||
new_kind = DoorKind.TrapTriggerable
|
||||
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
|
||||
verify_door_list_pos(d, room, world, player, pos)
|
||||
d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1, 3: 0x8}[d.doorListPos]
|
||||
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 = None
|
||||
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)
|
||||
d.trapFlag = {0: 0x4, 1: 0x2, 2: 0x1}[d.doorListPos]
|
||||
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 = 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):
|
||||
if world.door_type_mode[player] != 'original': # big, all, chaos
|
||||
# traverse dungeon and find candidates
|
||||
|
||||
2
Main.py
2
Main.py
@@ -34,7 +34,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new
|
||||
from source.tools.BPS import create_bps_from_data
|
||||
from source.classes.CustomSettings import CustomSettings
|
||||
|
||||
version_number = '1.2.0.19'
|
||||
version_number = '1.2.0.20'
|
||||
version_branch = '-u'
|
||||
__version__ = f'{version_number}{version_branch}'
|
||||
|
||||
|
||||
@@ -109,6 +109,9 @@ These are now independent of retro mode and have three options: None, Random, an
|
||||
|
||||
# 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
|
||||
* Added min/max for triforce pool, goal, and difference for CLI and Customizer. (Thanks Catobat)
|
||||
* Fixed a bug with dungeon generation
|
||||
|
||||
101
Rules.py
101
Rules.py
@@ -276,11 +276,18 @@ def global_rules(world, player):
|
||||
# Start of door rando rules
|
||||
# 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
|
||||
# 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 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))
|
||||
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.
|
||||
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 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 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_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 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))
|
||||
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 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))
|
||||
@@ -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 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))
|
||||
|
||||
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_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_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))
|
||||
@@ -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))
|
||||
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))
|
||||
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 - Prize', player))
|
||||
|
||||
@@ -431,8 +453,15 @@ def global_rules(world, 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 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 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 - 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 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))
|
||||
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 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))
|
||||
@@ -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_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 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:
|
||||
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 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))
|
||||
@@ -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 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))
|
||||
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))
|
||||
# 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
|
||||
@@ -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 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:
|
||||
if bombable:
|
||||
add_rule(world.get_entrance(killdoor, player), lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player)))
|
||||
else:
|
||||
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('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
|
||||
('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('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))
|
||||
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('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':
|
||||
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 = {
|
||||
'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
|
||||
@@ -1187,6 +1250,18 @@ std_kill_rooms = {
|
||||
'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work
|
||||
} # 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):
|
||||
parent = world.get_region(parent_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:
|
||||
for ent in std_kill_rooms[region.name]:
|
||||
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_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):
|
||||
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 doors_to_check if x.type in [DoorType.Normal, DoorType.Interior] and not x.blocked]
|
||||
for door in doors_to_check:
|
||||
@@ -1997,6 +2081,17 @@ bunny_impassible_doors = {
|
||||
'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):
|
||||
key_logic = world.key_logic[player]
|
||||
|
||||
125
test/dungeons/trap_test.yaml
Normal file
125
test/dungeons/trap_test.yaml
Normal 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
|
||||
Reference in New Issue
Block a user