Merge branch 'OriginOverworldShuffle' into OverworldShuffle
This commit is contained in:
227
Rules.py
227
Rules.py
@@ -9,6 +9,7 @@ from Dungeons import dungeon_table
|
||||
from RoomData import DoorKind
|
||||
from OWEdges import OWExitTypes
|
||||
from OverworldGlitchRules import overworld_glitches_rules
|
||||
from UnderworldGlitchRules import underworld_glitches_rules
|
||||
|
||||
|
||||
def set_rules(world, player):
|
||||
@@ -38,7 +39,7 @@ def set_rules(world, player):
|
||||
logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.')
|
||||
no_glitches_rules(world, player)
|
||||
fake_flipper_rules(world, player)
|
||||
elif world.logic[player] == 'owglitches':
|
||||
elif world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||
logging.getLogger('').info('There is a chance OWG has bugged edge case rulesets, especially in inverted. Definitely file a report on GitHub if you see anything strange.')
|
||||
# Initially setting no_glitches_rules to set the baseline rules for some
|
||||
# entrances. The overworld_glitches_rules set is primarily additive.
|
||||
@@ -90,6 +91,9 @@ def set_rules(world, player):
|
||||
if not world.is_copied_world:
|
||||
set_bunny_rules(world, player, world.mode[player] == 'inverted')
|
||||
|
||||
# These rules go here because they overwrite/add to some of the above rules
|
||||
if world.logic[player] == 'hybridglitches':
|
||||
underworld_glitches_rules(world, player)
|
||||
|
||||
def mirrorless_path_to_location(world, startName, targetName, player):
|
||||
# If Agahnim is defeated then the courtyard needs to be accessible without using the mirror for the mirror offset glitch.
|
||||
@@ -134,9 +138,11 @@ def mirrorless_path_to_castle_courtyard(world, player):
|
||||
queue.append((entrance.connected_region, new_path))
|
||||
seen.add(entrance.connected_region)
|
||||
|
||||
|
||||
def set_rule(spot, rule):
|
||||
spot.access_rule = rule
|
||||
|
||||
|
||||
def set_defeat_dungeon_boss_rule(location):
|
||||
# Lambda required to defer evaluation of dungeon.boss since it will change later if boos shuffle is used
|
||||
set_rule(location, lambda state: location.parent_region.dungeon.boss.can_defeat(state))
|
||||
@@ -145,6 +151,7 @@ def set_defeat_dungeon_boss_rule(location):
|
||||
def set_always_allow(spot, rule):
|
||||
spot.always_allow = rule
|
||||
|
||||
|
||||
def add_rule(spot, rule, combine='and'):
|
||||
old_rule = spot.access_rule
|
||||
if combine == 'or':
|
||||
@@ -173,22 +180,26 @@ def forbid_item(location, item, player):
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda i: (i.name != item or i.player != player) and old_rule(i)
|
||||
|
||||
|
||||
def add_item_rule(location, rule):
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda item: rule(item) and old_rule(item)
|
||||
|
||||
|
||||
def item_in_locations(state, item, player, locations):
|
||||
for location in locations:
|
||||
if item_name(state, location[0], location[1]) == (item, player):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def item_name(state, location, player):
|
||||
location = state.world.get_location(location, player)
|
||||
if location.item is None:
|
||||
return None
|
||||
return (location.item.name, location.item.player)
|
||||
|
||||
|
||||
def global_rules(world, player):
|
||||
# ganon can only carry triforce
|
||||
add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player)
|
||||
@@ -604,10 +615,10 @@ def global_rules(world, player):
|
||||
set_rule(location, lambda state: state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('TR Final Abyss Balcony Path', player), lambda state: state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('TR Final Abyss Ledge Path', player), lambda state: state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.can_avoid_lasers(player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.can_avoid_lasers(player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.can_avoid_lasers(player))
|
||||
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.can_avoid_lasers(player))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player))
|
||||
|
||||
@@ -810,9 +821,9 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('TR Crystaroller Bottom to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Crystaroller Bottom', player), player))) # or state.has_sword(player, 2)
|
||||
set_rule(world.get_entrance('TR Crystaroller Middle to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('TR Crystaroller Middle', player), player))) # or state.has_sword(player, 2)
|
||||
set_rule(world.get_entrance('TR Crystaroller Middle to Bottom Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player))
|
||||
set_rule(world.get_entrance('TR Crystal Maze End to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player)) # or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) // These work by clipping the rang through the two stone blocks, which works sometimes.
|
||||
set_rule(world.get_entrance('TR Crystal Maze Interior to End Bypass', player), lambda state: state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # Beam sword does NOT work
|
||||
set_rule(world.get_entrance('TR Crystal Maze Interior to Start Bypass', player), lambda state: True) # Can always grab a pot from the interior and walk it to the start region and throw it there
|
||||
set_rule(world.get_entrance('TR Crystal Maze End to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player)) # or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) // These work by clipping the rang through the two stone blocks, which works sometimes.
|
||||
set_rule(world.get_entrance('TR Crystal Maze Interior to End Bypass', player), lambda state: state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # Beam sword does NOT work
|
||||
set_rule(world.get_entrance('TR Crystal Maze Interior to Start Bypass', player), lambda state: True) # Can always grab a pot from the interior and walk it to the start region and throw it there
|
||||
|
||||
set_rule(world.get_entrance('GT Hookshot Platform Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Hookshot South Platform', player), player))
|
||||
set_rule(world.get_entrance('GT Hookshot Entry Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Hookshot South Entry', player), player))
|
||||
@@ -845,6 +856,9 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('GT Crystal Circles to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or state.has_blunt_weapon(player) or state.has('Cane of Byrna', player)) # or state.has_sword(player, 2)
|
||||
|
||||
add_key_logic_rules(world, player)
|
||||
|
||||
if world.logic[player] == 'hybridglitches':
|
||||
add_hmg_key_logic_rules(world, player)
|
||||
# End of door rando rules.
|
||||
|
||||
if world.restrict_boss_items[player] != 'none':
|
||||
@@ -876,6 +890,9 @@ def global_rules(world, player):
|
||||
(state.has('Lamp', player) or state.can_extend_magic(player, 12))))
|
||||
# need to light torch a sufficient amount of times
|
||||
|
||||
if world.goal[player] != 'ganonhunt':
|
||||
add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
||||
|
||||
set_rule(
|
||||
world.get_entrance('Ganon Drop', player),
|
||||
lambda state: state.has_real_sword(player, 2) or state.has_special_weapon_level(player, 3))
|
||||
@@ -941,7 +958,7 @@ def bomb_rules(world, player):
|
||||
('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:
|
||||
add_rule(world.get_entrance(killdoor, player), lambda state: (state.can_kill_with_bombs(player) or state.can_kill_most_things(player)))
|
||||
else:
|
||||
@@ -958,25 +975,25 @@ def bomb_rules(world, 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 - Boomerang Guard Key Drop', True),
|
||||
('Hyrule Castle - Key Rat Key Drop', True),
|
||||
# ('Hyrule Castle - Big Key Drop', True), # Pots are available
|
||||
# ('Eastern Palace - Dark Eyegore Key Drop', True), # Pots are available
|
||||
# ('Hyrule Castle - Big Key Drop', True), # Pots are available
|
||||
# ('Eastern Palace - Dark Eyegore Key Drop', True), # Pots are available
|
||||
('Castle Tower - Dark Archer Key Drop', True),
|
||||
# ('Castle Tower - Circle of Pots Key Drop', True), # Pots are available
|
||||
# ('Skull Woods - Spike Corner Key Drop', True), # Pots are available
|
||||
# ('Castle Tower - Circle of Pots Key Drop', True), # Pots are available
|
||||
# ('Skull Woods - Spike Corner Key Drop', True), # Pots are available
|
||||
('Ice Palace - Jelly Key Drop', True),
|
||||
('Ice Palace - Conveyor Key Drop', True),
|
||||
('Misery Mire - Conveyor Crystal Key Drop', True),
|
||||
('Turtle Rock - Pokey 1 Key Drop', True),
|
||||
('Turtle Rock - Pokey 2 Key Drop', True),
|
||||
# ('Ganons Tower - Mini Helmasaur Key Drop', True) # Pots are available
|
||||
('Castle Tower - Room 03', True), # Two spring soliders
|
||||
('Ice Palace - Compass Chest', True) # Pengators
|
||||
# ('Ganons Tower - Mini Helmasaur Key Drop', True) # Pots are available
|
||||
('Castle Tower - Room 03', True), # Two spring soliders
|
||||
('Ice Palace - Compass Chest', True) # Pengators
|
||||
]
|
||||
for location,bombable in enemy_kill_drops:
|
||||
for location, bombable in enemy_kill_drops:
|
||||
if bombable:
|
||||
add_rule(world.get_location(location, player), lambda state: state.can_kill_with_bombs(player) or state.can_kill_most_things(player))
|
||||
else:
|
||||
@@ -988,17 +1005,17 @@ def bomb_rules(world, player):
|
||||
add_rule(world.get_entrance(entrance, player), lambda state: state.can_use_bombs(player))
|
||||
|
||||
if world.doorShuffle[player] == 'vanilla':
|
||||
add_rule(world.get_entrance('TR Lazy Eyes SE', player), lambda state: state.can_use_bombs(player)) # ToDo: Add always true for inverted, cross-entrance, and door-variants and so on.
|
||||
add_rule(world.get_entrance('Turtle Rock Ledge Exit (West)', player), lambda state: state.can_use_bombs(player)) # Is this the same as above?
|
||||
add_rule(world.get_entrance('TR Lazy Eyes SE', player), lambda state: state.can_use_bombs(player)) # ToDo: Add always true for inverted, cross-entrance, and door-variants and so on.
|
||||
add_rule(world.get_entrance('Turtle Rock Ledge Exit (West)', player), lambda state: state.can_use_bombs(player)) # Is this the same as above?
|
||||
|
||||
dungeon_bonkable = ['Sewers Rat Path WS', 'Sewers Rat Path WN',
|
||||
'PoD Warp Hint SE', 'PoD Jelly Hall NW', 'PoD Jelly Hall NE', 'PoD Mimics 1 SW',
|
||||
'Thieves Ambush E', 'Thieves Rail Ledge W',
|
||||
'TR Dash Room NW', 'TR Crystaroller SW', 'TR Dash Room ES',
|
||||
'GT Four Torches NW','GT Fairy Abyss SW'
|
||||
'GT Four Torches NW', 'GT Fairy Abyss SW'
|
||||
]
|
||||
dungeon_bombable = ['PoD Map Balcony WS', 'PoD Arena Ledge ES', 'PoD Dark Maze E', 'PoD Big Chest Balcony W',
|
||||
'Swamp Pot Row WN','Swamp Map Ledge EN', 'Swamp Hammer Switch WN', 'Swamp Hub Dead Ledge EN', 'Swamp Waterway N', 'Swamp I S',
|
||||
'Swamp Pot Row WN', 'Swamp Map Ledge EN', 'Swamp Hammer Switch WN', 'Swamp Hub Dead Ledge EN', 'Swamp Waterway N', 'Swamp I S',
|
||||
'Skull Pot Circle WN', 'Skull Pull Switch EN', 'Skull Big Key EN', 'Skull Lone Pot WN',
|
||||
'Thieves Rail Ledge NW', 'Thieves Pot Alcove Bottom SW',
|
||||
'Ice Bomb Drop Hole', 'Ice Freezors Bomb Hole',
|
||||
@@ -1072,7 +1089,7 @@ def ow_inverted_rules(world, player):
|
||||
else:
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_sword(player, 2) or state.has_beaten_aga(player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
||||
set_rule(world.get_entrance('GT Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
|
||||
set_rule(world.get_entrance('GT Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player) or state.world.shuffle[player] in ('restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity'))
|
||||
set_rule(world.get_entrance('GT Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player) or state.world.shuffle[player] in ('restricted', 'full', 'lite', 'lean', 'district', 'swapped', 'crossed', 'insanity'))
|
||||
|
||||
if world.is_tile_swapped(0x03, player):
|
||||
set_rule(world.get_entrance('Spectacle Rock Approach', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches'] and state.has_Pearl(player))
|
||||
@@ -1168,12 +1185,12 @@ def ow_bunny_rules(world, player):
|
||||
add_bunny_rule(world.get_entrance('20 Rupee Cave', player), player)
|
||||
add_bunny_rule(world.get_entrance('50 Rupee Cave', player), player)
|
||||
|
||||
add_bunny_rule(world.get_entrance('Skull Woods First Section Hole (North)', player), player) # bunny cannot lift bush
|
||||
add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player) # bunny cannot lift bush
|
||||
add_bunny_rule(world.get_entrance('Skull Woods Final Section', player), player) # bunny cannot use fire rod
|
||||
add_bunny_rule(world.get_entrance('Skull Woods First Section Hole (North)', player), player) # bunny cannot lift bush
|
||||
add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player) # bunny cannot lift bush
|
||||
add_bunny_rule(world.get_entrance('Skull Woods Final Section', player), player) # bunny cannot use fire rod
|
||||
add_bunny_rule(world.get_entrance('Hookshot Cave', player), player)
|
||||
add_bunny_rule(world.get_entrance('Thieves Town', player), player) # bunny cannot pull
|
||||
add_bunny_rule(world.get_entrance('Palace of Darkness', player), player) # kiki needs pearl
|
||||
add_bunny_rule(world.get_entrance('Thieves Town', player), player) # bunny cannot pull
|
||||
add_bunny_rule(world.get_entrance('Palace of Darkness', player), player) # kiki needs pearl
|
||||
add_bunny_rule(world.get_entrance('Hammer Peg Cave', player), player)
|
||||
add_bunny_rule(world.get_entrance('Bonk Fairy (Dark)', player), player)
|
||||
add_bunny_rule(world.get_entrance('Misery Mire', player), player)
|
||||
@@ -1400,6 +1417,7 @@ def forbid_bomb_jump_requirements(world, player):
|
||||
set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False)
|
||||
set_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), lambda state: False)
|
||||
|
||||
|
||||
def add_conditional_lamps(world, player):
|
||||
def add_conditional_lamp(spot, spottype='Location'):
|
||||
if spottype == 'Location':
|
||||
@@ -1530,33 +1548,33 @@ def pseudo_sword_mode_rules(world, 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
|
||||
'Eastern Stalfos Spawn': ['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], # Can use pots
|
||||
'Desert Compass Room': ['Desert Compass NE'], # Three popos
|
||||
'Desert Four Statues': ['Desert Four Statues NW', 'Desert Four Statues ES'], # Four popos
|
||||
'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
|
||||
'Eastern Stalfos Spawn': ['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], # Can use pots
|
||||
'Desert Compass Room': ['Desert Compass NE'], # Three popos
|
||||
'Desert Four Statues': ['Desert Four Statues NW', 'Desert Four Statues ES'], # Four popos
|
||||
'Hera Beetles': ['Hera Beetles WS'], # Three blue beetles and only two pots, and bombs don't work.
|
||||
'Tower Gold Knights': ['Tower Gold Knights SW', 'Tower Gold Knights EN'], # Two ball and chain
|
||||
'Tower Gold Knights': ['Tower Gold Knights SW', 'Tower Gold Knights EN'], # Two ball and chain
|
||||
'Tower Dark Archers': ['Tower Dark Archers WN'], # Not a kill room
|
||||
'Tower Red Spears': ['Tower Red Spears WN'], # Two spear soldiers
|
||||
'Tower Red Guards': ['Tower Red Guards EN', 'Tower Red Guards SW'], # Two usain bolts
|
||||
'Tower Circle of Pots': ['Tower Circle of Pots NW'], # Two spear soldiers. Plenty of pots.
|
||||
'Tower Red Spears': ['Tower Red Spears WN'], # Two spear soldiers
|
||||
'Tower Red Guards': ['Tower Red Guards EN', 'Tower Red Guards SW'], # Two usain bolts
|
||||
'Tower Circle of Pots': ['Tower Circle of Pots NW'], # Two spear soldiers. Plenty of pots.
|
||||
'PoD Turtle Party': ['PoD Turtle Party ES', 'PoD Turtle Party NW'], # Lots of turtles.
|
||||
'Thieves Basement Block': ['Thieves Basement Block WN'], # One blue and one red zazak and one Stalfos. Two pots. Need weapon.
|
||||
'Ice Stalfos Hint': ['Ice Stalfos Hint SE'], # Need bombs for big stalfos knights
|
||||
'Ice Pengator Trap': ['Ice Pengator Trap NE'], # Five pengators. Bomb-doable?
|
||||
'Mire 2': ['Mire 2 NE'], # Wizzrobes. Bombs dont work.
|
||||
'Mire Cross': ['Mire Cross ES'], # 4 Sluggulas. Bombs don't work
|
||||
'TR Twin Pokeys': ['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], # Two pokeys
|
||||
'GT Petting Zoo': ['GT Petting Zoo SE'], # Dont make anyone do this room with bombs.
|
||||
'GT DMs Room': ['GT DMs Room SW'], # Four red stalfos
|
||||
'GT Gauntlet 1': ['GT Gauntlet 1 WN'], # Stalfos/zazaks
|
||||
'GT Gauntlet 2': ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], # Red stalfos
|
||||
'GT Gauntlet 3': ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], # Blue zazaks
|
||||
'GT Gauntlet 4': ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], # Red zazaks
|
||||
'GT Gauntlet 5': ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], # Stalfos and zazak
|
||||
'GT Wizzrobes 1': ['GT Wizzrobes 1 SW'], # Wizzrobes. Bombs don't work
|
||||
'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work
|
||||
'Thieves Basement Block': ['Thieves Basement Block WN'], # One blue and one red zazak and one Stalfos. Two pots. Need weapon.
|
||||
'Ice Stalfos Hint': ['Ice Stalfos Hint SE'], # Need bombs for big stalfos knights
|
||||
'Ice Pengator Trap': ['Ice Pengator Trap NE'], # Five pengators. Bomb-doable?
|
||||
'Mire 2': ['Mire 2 NE'], # Wizzrobes. Bombs dont work.
|
||||
'Mire Cross': ['Mire Cross ES'], # 4 Sluggulas. Bombs don't work
|
||||
'TR Twin Pokeys': ['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], # Two pokeys
|
||||
'GT Petting Zoo': ['GT Petting Zoo SE'], # Dont make anyone do this room with bombs.
|
||||
'GT DMs Room': ['GT DMs Room SW'], # Four red stalfos
|
||||
'GT Gauntlet 1': ['GT Gauntlet 1 WN'], # Stalfos/zazaks
|
||||
'GT Gauntlet 2': ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], # Red stalfos
|
||||
'GT Gauntlet 3': ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], # Blue zazaks
|
||||
'GT Gauntlet 4': ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], # Red zazaks
|
||||
'GT Gauntlet 5': ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], # Stalfos and zazak
|
||||
'GT Wizzrobes 1': ['GT Wizzrobes 1 SW'], # Wizzrobes. Bombs don't work
|
||||
'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work
|
||||
} # all trap rooms?
|
||||
|
||||
std_kill_doors_if_trapped = {
|
||||
@@ -1571,6 +1589,7 @@ std_kill_doors_if_trapped = {
|
||||
# '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)
|
||||
@@ -1584,6 +1603,7 @@ def standard_rules(world, player):
|
||||
world.get_entrance('Uncle S&Q', player).hide_path = True
|
||||
set_rule(world.get_entrance('Links House S&Q', player), lambda state: state.has('Zelda Delivered', player))
|
||||
set_rule(world.get_entrance('Sanctuary S&Q', player), lambda state: state.has('Zelda Delivered', player))
|
||||
add_rule(world.get_entrance('Old Man S&Q', player), lambda state: state.has('Zelda Delivered', player))
|
||||
# these are because of rails
|
||||
if world.shuffle[player] != 'vanilla':
|
||||
# where ever these happen to be
|
||||
@@ -1629,13 +1649,16 @@ def standard_rules(world, player):
|
||||
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))
|
||||
set_rule(world.get_entrance('Hyrule Castle Tapestry Backwards', player), lambda state: state.has('Zelda Herself', player))
|
||||
|
||||
def check_rule_list(state, r_list):
|
||||
return True if len(r_list) <= 0 else r_list[0](state) and check_rule_list(state, r_list[1:])
|
||||
|
||||
rule_list, debug_path = find_rules_for_zelda_delivery(world, player)
|
||||
set_rule(world.get_location('Zelda Drop Off', player), lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list))
|
||||
set_rule(world.get_entrance('Hyrule Castle Throne Room Tapestry', player),
|
||||
lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list))
|
||||
set_rule(world.get_location('Zelda Drop Off', player),
|
||||
lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list))
|
||||
|
||||
for entrance in ['Links House SC', 'Links House ES', 'Central Bonk Rocks SW', 'Hyrule Castle WN', 'Hyrule Castle ES',
|
||||
'Bonk Fairy (Light)', 'Hyrule Castle Main Gate (South)', 'Hyrule Castle Main Gate (North)', 'Hyrule Castle Ledge Drop']:
|
||||
@@ -1669,18 +1692,19 @@ def find_rules_for_zelda_delivery(world, player):
|
||||
if not rule(blank_state):
|
||||
rule_list.append(rule)
|
||||
next_path.append(ext.name)
|
||||
if connect.name == 'Sanctuary':
|
||||
if connect.name == 'Hyrule Castle Throne Room':
|
||||
return rule_list, next_path
|
||||
else:
|
||||
visited.add(connect)
|
||||
queue.append((connect, rule_list, next_path))
|
||||
raise Exception('No path to Sanctuary found')
|
||||
raise Exception('No path to Throne Room found')
|
||||
|
||||
|
||||
def set_bunny_rules(world, player, inverted):
|
||||
|
||||
# regions for the exits of multi-entrace caves/drops that bunny cannot pass
|
||||
# Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing.
|
||||
all_single_exit_dungeons = ['Eastern Palace', 'Tower of Hera', 'Castle Tower', 'Palace of Darkness', 'Swamp Palace', 'Thieves Town', 'Ice Palace', 'Misery Mire', 'Ganons Tower']
|
||||
hmg_single_exit_dungeons = [d for d in all_single_exit_dungeons if d not in ['Tower of Hera', 'Misery Mire', 'Thieves Town']]
|
||||
bunny_impassable_caves = ['Bumper Cave (top)', 'Bumper Cave (bottom)', 'Two Brothers House',
|
||||
'Hookshot Cave (Middle)', 'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)']
|
||||
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
|
||||
@@ -1705,6 +1729,9 @@ def set_bunny_rules(world, player, inverted):
|
||||
'Take - Any # 3 Item 1', 'Take - Any # 3 Item 2',
|
||||
'Take - Any # 4 Item 1', 'Take - Any # 4 Item 2',
|
||||
]
|
||||
bunny_pocket_entrances = ['Skull Woods Final Section', 'Bush Yard Pegs (Inner)', 'Bush Yard Pegs (Outer)',
|
||||
'DM Hammer Bridge (West)', 'DM Hammer Bridge (East)', 'Blacksmith Ledge Peg (West)'
|
||||
]
|
||||
|
||||
def path_to_access_rule(path, entrance):
|
||||
return lambda state: state.can_reach(entrance) and all(rule_func(state) for rule_func in path)
|
||||
@@ -1718,18 +1745,38 @@ def set_bunny_rules(world, player, inverted):
|
||||
return region.is_light_world
|
||||
else:
|
||||
return region.is_dark_world
|
||||
|
||||
def is_link(region):
|
||||
if inverted:
|
||||
return region.is_dark_world
|
||||
else:
|
||||
return region.is_light_world
|
||||
|
||||
# Is it possible to do bunny pocket here
|
||||
def can_bunny_pocket_to(world, entrance_name, player):
|
||||
def can_activate_bunny_pocket(region):
|
||||
explored_regions.append(region.name)
|
||||
for ent in region.entrances:
|
||||
if (is_link(ent.parent_region) or
|
||||
(ent.parent_region.type == RegionType.Dungeon and ent.parent_region.name in bunny_revivable_entrances)):
|
||||
return True
|
||||
for ent in region.entrances:
|
||||
if ent.spot_type in ['OWEdge', 'Ledge', 'OpenTerrain'] and ent.parent_region.name not in explored_regions:
|
||||
if can_activate_bunny_pocket(ent.parent_region):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_rule_to_add(region, location = None, connecting_entrance = None):
|
||||
entrance = world.get_entrance(entrance_name, player)
|
||||
explored_regions = []
|
||||
return can_activate_bunny_pocket(entrance.parent_region)
|
||||
|
||||
|
||||
def get_rule_to_add(region, location=None, connecting_entrance=None):
|
||||
# In OWG, a location can potentially be superbunny-mirror accessible or
|
||||
# bunny revival accessible.
|
||||
if world.logic[player] == 'owglitches':
|
||||
if world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||
if region.type != RegionType.Dungeon \
|
||||
and (location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations()) \
|
||||
and (location is None or location.name not in OverworldGlitchRules.superbunny_accessible_locations) \
|
||||
and not is_link(region):
|
||||
return lambda state: state.has_Pearl(player)
|
||||
else:
|
||||
@@ -1747,25 +1794,33 @@ def set_bunny_rules(world, player, inverted):
|
||||
# for each such entrance a new option is added that consist of:
|
||||
# a) being able to reach it, and
|
||||
# b) being able to access all entrances from there to `region`
|
||||
seen = {region}
|
||||
queue = deque([(region, [])])
|
||||
queue = deque([(region, [], {region}, [region])])
|
||||
seen_sets = set([frozenset({region})])
|
||||
while queue:
|
||||
(current, path) = queue.popleft()
|
||||
(current, path, seen, region_path) = queue.popleft()
|
||||
for entrance in current.entrances:
|
||||
if entrance.door and entrance.door.blocked:
|
||||
continue
|
||||
new_region = entrance.parent_region
|
||||
if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_region in seen:
|
||||
new_seen = seen.union({new_region})
|
||||
if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_seen in seen_sets:
|
||||
continue
|
||||
new_path = path + [entrance.access_rule]
|
||||
seen.add(new_region)
|
||||
new_region_path = region_path + [new_region]
|
||||
seen_sets.add(frozenset(new_seen))
|
||||
if not is_link(new_region):
|
||||
if world.logic[player] == 'owglitches':
|
||||
if world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||
# Is this a bunny pocketable entrance?
|
||||
if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon:
|
||||
if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
|
||||
if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances:
|
||||
continue
|
||||
if entrance.name in bunny_pocket_entrances and not can_bunny_pocket_to(world, entrance.name, player):
|
||||
continue
|
||||
if entrance.name in drop_dungeon_entrances:
|
||||
lobby = entrance.connected_region
|
||||
else:
|
||||
lobby = next(exit.connected_region for exit in current.exits if exit.connected_region.type == RegionType.Dungeon)
|
||||
portal_regions = [world.get_region(reg, player) for reg in region.dungeon.regions if reg.endswith('Portal')]
|
||||
lobby = next(reg.connected_region for portal_reg in portal_regions for reg in portal_reg.exits if reg.name.startswith('Enter '))
|
||||
if lobby.name in bunny_revivable_entrances:
|
||||
possible_options.append(path_to_access_rule(new_path, entrance))
|
||||
elif lobby.name in superbunny_revivable_entrances:
|
||||
@@ -1774,26 +1829,29 @@ def set_bunny_rules(world, player, inverted):
|
||||
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance))
|
||||
continue
|
||||
elif region.type == RegionType.Cave and new_region.type != RegionType.Cave:
|
||||
if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
|
||||
if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances:
|
||||
continue
|
||||
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
|
||||
if entrance.name in bunny_pocket_entrances and not can_bunny_pocket_to(world, entrance.name, player):
|
||||
continue
|
||||
if region.name in OverworldGlitchRules.sword_required_superbunny_mirror_regions:
|
||||
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance))
|
||||
elif region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions():
|
||||
elif region.name in OverworldGlitchRules.boots_required_superbunny_mirror_regions:
|
||||
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance))
|
||||
elif location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations():
|
||||
if location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations():
|
||||
elif location and location.name in OverworldGlitchRules.superbunny_accessible_locations:
|
||||
if location.name in OverworldGlitchRules.boots_required_superbunny_mirror_locations:
|
||||
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance))
|
||||
elif region.name == 'Kakariko Well (top)':
|
||||
possible_options.append(path_to_access_rule(new_path, entrance))
|
||||
else:
|
||||
possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player)], entrance))
|
||||
continue
|
||||
elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations():
|
||||
elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.superbunny_accessible_locations:
|
||||
possible_options.append(path_to_access_rule(new_path, entrance))
|
||||
else:
|
||||
continue
|
||||
if is_bunny(new_region):
|
||||
queue.append((new_region, new_path))
|
||||
# todo: if not owg or hmg and entrance is in bunny_impassible_doors, then skip this nonsense?
|
||||
queue.append((new_region, new_path, new_seen, new_region_path))
|
||||
else:
|
||||
# we have reached pure light world, so we have a new possible option
|
||||
possible_options.append(path_to_access_rule(new_path, entrance))
|
||||
@@ -1801,7 +1859,6 @@ def set_bunny_rules(world, player, inverted):
|
||||
|
||||
# Add requirements for bunny-impassible caves if they occur in the light world
|
||||
for region in [world.get_region(name, player) for name in bunny_impassable_caves]:
|
||||
|
||||
if not is_bunny(region):
|
||||
continue
|
||||
rule = get_rule_to_add(region)
|
||||
@@ -1836,6 +1893,12 @@ def set_bunny_rules(world, player, inverted):
|
||||
continue
|
||||
add_rule(location, get_rule_to_add(region, location))
|
||||
|
||||
if world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||
for ent_name in bunny_pocket_entrances:
|
||||
bunny_exit = world.get_entrance(ent_name, player)
|
||||
if bunny_exit.connected_region and is_bunny(bunny_exit.parent_region) and not can_bunny_pocket_to(world, ent_name, player):
|
||||
add_rule(bunny_exit, lambda state: state.has_Pearl(player))
|
||||
|
||||
|
||||
drop_dungeon_entrances = {
|
||||
"Sewer Drop",
|
||||
@@ -1845,7 +1908,6 @@ drop_dungeon_entrances = {
|
||||
"Skull Back Drop"
|
||||
}
|
||||
|
||||
|
||||
bunny_revivable_entrances = {
|
||||
"Sewers Pull Switch", "TR Dash Room", "Swamp Boss", "Hera Boss",
|
||||
"Tower Agahnim 1", "Ice Lobby", "Sewers Rat Path", "PoD Falling Bridge",
|
||||
@@ -1947,7 +2009,7 @@ bunny_impassible_doors = {
|
||||
'GT Double Switch Exit to Blue Barrier', 'GT Firesnake Room Hook Path', 'GT Falling Bridge WN', 'GT Falling Bridge WS',
|
||||
'GT Ice Armos NE', 'GT Ice Armos WS', 'GT Crystal Paths SW', 'GT Mimics 1 NW', 'GT Mimics 1 ES', 'GT Mimics 2 WS',
|
||||
'GT Mimics 2 NE', 'GT Hidden Spikes EN', 'GT Cannonball Bridge SE', 'GT Gauntlet 1 WN', 'GT Gauntlet 2 EN',
|
||||
'GT Gauntlet 2 SW', 'GT Gauntlet 3 NW', 'GT Gauntlet 3 SW', 'GT Gauntlet 4 NW', 'GT Gauntlet 4 SW',
|
||||
'GT Gauntlet 2 SW', 'GT Gauntlet 3 NW', 'GT Gauntlet 3 SW', 'GT Gauntlet 4 NW', 'GT Gauntlet 4 SW',
|
||||
'GT Gauntlet 5 NW', 'GT Gauntlet 5 WS', 'GT Lanmolas 2 ES', 'GT Lanmolas 2 NW', 'GT Wizzrobes 1 SW',
|
||||
'GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE', 'GT Torch Cross ES', 'GT Falling Torches NE', 'GT Moldorm Gap',
|
||||
'GT Validation Block Path'
|
||||
@@ -1964,6 +2026,11 @@ bunny_impassible_if_trapped = {
|
||||
'GT Speed Torch WN', 'Ice Lobby SE'
|
||||
}
|
||||
|
||||
def add_hmg_key_logic_rules(world, player):
|
||||
for toh_loc in world.key_logic[player]['Tower of Hera'].bk_restricted:
|
||||
set_always_allow(world.get_location(toh_loc.name, player), allow_big_key_in_big_chest('Big Key (Tower of Hera)', player))
|
||||
set_always_allow(world.get_location('Swamp Palace - Entrance', player), allow_big_key_in_big_chest('Big Key (Swamp Palace)', player))
|
||||
|
||||
|
||||
def add_key_logic_rules(world, player):
|
||||
key_logic = world.key_logic[player]
|
||||
@@ -2097,7 +2164,7 @@ def create_key_rule(small_key_name, player, keys):
|
||||
|
||||
def create_key_rule_allow_small(small_key_name, player, keys, location):
|
||||
loc = location.name
|
||||
return lambda state: state.has_sm_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_key(small_key_name, player, keys-1))
|
||||
return lambda state: state.has_sm_key(small_key_name, player, keys) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_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):
|
||||
@@ -2108,7 +2175,7 @@ def create_key_rule_bk_exception(small_key_name, big_key_name, player, keys, bk_
|
||||
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_sm_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_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_sm_key(small_key_name, player, bk_keys))
|
||||
return lambda state: (state.has_sm_key(small_key_name, player, keys) and not item_in_locations(state, big_key_name, player, zip(chest_names, [player] * len(chest_names)))) or (item_name(state, loc, player) in [(small_key_name, player)] and state.has_sm_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_sm_key(small_key_name, player, bk_keys))
|
||||
|
||||
|
||||
def create_advanced_key_rule(key_logic, player, rule):
|
||||
|
||||
Reference in New Issue
Block a user