Implement basic HMG logic
* Ice Palace Lobby Clip * Kikiskip to Pod * Mire -> Hera -> Swamp - Mire small door to rupee room is removed - Hera and Swamp keys are placed without logic - Swamp locked by vanilla rules or having all mire smalls * Above as connectors in ER
This commit is contained in:
173
UnderworldGlitchRules.py
Normal file
173
UnderworldGlitchRules.py
Normal file
@@ -0,0 +1,173 @@
|
||||
from BaseClasses import Entrance
|
||||
import Rules
|
||||
from OverworldGlitchRules import create_no_logic_connections
|
||||
|
||||
|
||||
def get_kikiskip_spots():
|
||||
"""
|
||||
Spectacle Rock Cave (Bottom) -> Palace of Darkness Exit, a.k.a. Kiki Skip
|
||||
"""
|
||||
yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal")
|
||||
|
||||
|
||||
# We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions
|
||||
def get_kikiskip_connectors(world, player):
|
||||
if world.fix_palaceofdarkness_exit[player] or world.fix_fake_world[player]:
|
||||
yield ("Kiki Skip", "Spectacle Rock Cave (Bottom)", world.get_entrance("Palace of Darkness Exit", player).connected_region)
|
||||
|
||||
|
||||
def get_mireheraswamp_connectors(world, player):
|
||||
if world.fix_palaceofdarkness_exit[player] or world.fix_fake_world[player]:
|
||||
yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Tower of Hera Exit", player).connected_region)
|
||||
yield ("Mire to Hera Clip", "Mire Torches Top", world.get_entrance("Swamp Palace Exit", player).connected_region)
|
||||
|
||||
|
||||
def get_mireheraswamp_spots():
|
||||
"""
|
||||
"Mire Torches Top -> Tower of Hera Exit, a.k.a. Mire to Hera Clip
|
||||
"Mire Torches Top -> Swamp Palace Exit, a.k.a. Hera to Swamp Clip
|
||||
"""
|
||||
|
||||
yield ("Mire to Hera Clip", "Mire Torches Top", "Hera Portal")
|
||||
yield ("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal")
|
||||
|
||||
|
||||
def get_icepalace_spots():
|
||||
"""
|
||||
"Ice Palace Exit -> Ice Palace Exit, a.k.a. Ice Palace Clip
|
||||
"""
|
||||
yield ("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop")
|
||||
|
||||
|
||||
# Create connections between dungeons
|
||||
def create_hybridmajor_connections(world, player):
|
||||
create_no_logic_connections(player, world, get_kikiskip_spots())
|
||||
create_no_logic_connections(player, world, get_mireheraswamp_spots())
|
||||
create_no_logic_connections(player, world, get_icepalace_spots())
|
||||
|
||||
|
||||
# Turn dungeons into connectors 4
|
||||
def create_hybridmajor_connectors(world, player):
|
||||
create_no_logic_connections(player, world, get_kikiskip_connectors(world, player))
|
||||
create_no_logic_connections(player, world, get_mireheraswamp_connectors(world, player))
|
||||
|
||||
|
||||
# For some entrances, we need to fake having pearl, because we're in fake DW/LW.
|
||||
# This creates a copy of the input state that has Moon Pearl.
|
||||
def fake_pearl_state(state, player):
|
||||
if state.has("Moon Pearl", player):
|
||||
return state
|
||||
fake_state = state.copy()
|
||||
fake_state.prog_items["Moon Pearl", player] += 1
|
||||
return fake_state
|
||||
|
||||
|
||||
# Sets the rules on where we can actually go using this clip.
|
||||
# Behavior differs based on what type of ER shuffle we're playing.
|
||||
def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, dungeon_exit: str):
|
||||
fix_dungeon_exits = world.fix_palaceofdarkness_exit[player]
|
||||
fix_fake_worlds = world.fix_fake_world[player]
|
||||
|
||||
dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0]
|
||||
if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix
|
||||
# Dungeons are only shuffled among themselves. We need to check SW, MM, and AT because they can't be reentered trivially.
|
||||
|
||||
# entrance doesn't exist until you fire rod it from the other side
|
||||
if dungeon_entrance.name == "Skull Woods Final Section":
|
||||
Rules.set_rule(clip, lambda state: False)
|
||||
|
||||
elif dungeon_entrance.name == "Misery Mire":
|
||||
if world.swords[player] == "swordless":
|
||||
Rules.add_rule(clip, lambda state: state.has_misery_mire_medallion(player))
|
||||
else:
|
||||
Rules.add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player))
|
||||
|
||||
elif dungeon_entrance.name == "Agahnims Tower":
|
||||
Rules.add_rule(clip, lambda state: state.has("Cape", player) or state.has_beam_sword(player) or state.has("Beat Agahnim 1", player))
|
||||
|
||||
# Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally.
|
||||
Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
|
||||
elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix
|
||||
# Entry requires the entrance's requirements plus a fake pearl, but you don't gain logical access to the surrounding region.
|
||||
Rules.add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player)))
|
||||
# exiting restriction
|
||||
Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
|
||||
|
||||
# Otherwise, the shuffle type is lean, crossed, or insanity; all of these do not need additional rules on where we can go,
|
||||
# since the clip links directly to the exterior region.
|
||||
|
||||
|
||||
def underworld_glitches_rules(world, player):
|
||||
# Ice Palace Entrance Clip
|
||||
Rules.add_rule(world.get_entrance("Ice Lobby SE", player), lambda state: state.can_bomb_clip(world.get_region("Ice Lobby", player), player), combine="or")
|
||||
|
||||
# Kiki Skip
|
||||
kks = world.get_entrance("Kiki Skip", player)
|
||||
Rules.set_rule(kks, lambda state: state.can_bomb_clip(kks.parent_region, player))
|
||||
dungeon_reentry_rules(world, player, kks, "Palace of Darkness Portal", "Palace of Darkness Exit")
|
||||
|
||||
# Mire -> Hera -> Swamp
|
||||
def mire_clip(state):
|
||||
return state.can_reach("Mire Torches Top", "Region", player) and state.can_bomb_clip(world.get_region("Mire Torches Top", player), player) and state.has_fire_source(player)
|
||||
|
||||
def hera_clip(state):
|
||||
return state.can_reach("Hera 4F", "Region", player) and state.can_bomb_clip(world.get_region("Hera 4F", player), player)
|
||||
|
||||
Rules.add_rule(world.get_entrance("Hera Startile Corner NW", player), lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), combine="or")
|
||||
|
||||
# We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots
|
||||
# Flippers required for the doors going _into_ the dungeon
|
||||
for door in [
|
||||
"Swamp Entrance Down Stairs",
|
||||
"Swamp Pot Row WS",
|
||||
"Swamp Trench 1 Key Ledge NW",
|
||||
"Swamp Hub WN",
|
||||
"Swamp Hub North Ledge N",
|
||||
"Swamp Crystal Switch EN",
|
||||
"Swamp Push Statue S",
|
||||
"Swamp Waterway NW",
|
||||
"Swamp T SW",
|
||||
]:
|
||||
Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6) and state.has('Flippers', player), combine="or")
|
||||
|
||||
# These doors let us go backwards so we don't require flippers
|
||||
for door in [ "Swamp Trench 1 Approach ES", "Swamp Hammer Switch SW",]:
|
||||
Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has("Small Key (Misery Mire)", player, count=6), combine="or")
|
||||
|
||||
Rules.add_rule(world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or")
|
||||
|
||||
# Build the rule for SP moat.
|
||||
# We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT.
|
||||
# First we require a certain type of entrance shuffle, then build the rule from its pieces.
|
||||
if not world.swamp_patch_required[player]:
|
||||
if world.shuffle[player] in [
|
||||
"vanilla",
|
||||
"dungeonssimple",
|
||||
"dungeonsfull",
|
||||
"dungeonscrossed",
|
||||
]:
|
||||
rule_map = {
|
||||
"Mire Portal": (lambda state: True),
|
||||
"Hera Portal": (lambda state: state.can_reach("Hera Startile Corner NW", "Entrance", player)),
|
||||
}
|
||||
inverted = world.mode[player] == "inverted"
|
||||
|
||||
def hera_rule(state):
|
||||
return (state.has("Moon Pearl", player) or not inverted) and rule_map.get(world.get_entrance("Tower of Hera", player).connected_region.name, lambda state: False)(state)
|
||||
|
||||
def gt_rule(state):
|
||||
return (state.has("Moon Pearl", player) or inverted) and rule_map.get(
|
||||
world.get_entrance(("Ganons Tower" if not inverted else "Inverted Ganons Tower"), player).connected_region.name, lambda state: False)(state)
|
||||
|
||||
def mirrorless_moat_rule(state):
|
||||
return state.can_reach("Old Man S&Q", "Entrance", player) and mire_clip(state) and (hera_rule(state) or gt_rule(state))
|
||||
|
||||
Rules.add_rule(world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or")
|
||||
|
||||
# Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys
|
||||
mire_to_hera = world.get_entrance("Mire to Hera Clip", player)
|
||||
mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player)
|
||||
Rules.set_rule(mire_to_hera, mire_clip)
|
||||
Rules.set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has("Flippers", player))
|
||||
dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit")
|
||||
dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit")
|
||||
Reference in New Issue
Block a user