Hybrid Major Glitches connections and logic

This commit is contained in:
espeon65536 2021-06-07 20:19:03 -05:00
parent fae14ad283
commit eb9ee9f41e
4 changed files with 125 additions and 3 deletions

View File

@ -1,6 +1,6 @@
# ToDo: With shuffle_ganon option, prevent gtower from linking to an exit only location through a 2 entrance cave. # ToDo: With shuffle_ganon option, prevent gtower from linking to an exit only location through a 2 entrance cave.
from collections import defaultdict from collections import defaultdict
from worlds.alttp.UnderworldGlitchRules import underworld_glitch_connections
def link_entrances(world, player): def link_entrances(world, player):
connect_two_way(world, 'Links House', 'Links House Exit', player) # unshuffled. For now connect_two_way(world, 'Links House', 'Links House Exit', player) # unshuffled. For now
@ -17,6 +17,10 @@ def link_entrances(world, player):
for exitname, regionname in mandatory_connections: for exitname, regionname in mandatory_connections:
connect_simple(world, exitname, regionname, player) connect_simple(world, exitname, regionname, player)
# mandatory hybrid major glitches connections
if world.logic[player] in ['hybridglitches', 'nologic']:
underworld_glitch_connections(world, player)
# if we do not shuffle, set default connections # if we do not shuffle, set default connections
if world.shuffle[player] == 'vanilla': if world.shuffle[player] == 'vanilla':
for exitname, regionname in default_connections: for exitname, regionname in default_connections:
@ -1096,6 +1100,10 @@ def link_inverted_entrances(world, player):
for exitname, regionname in inverted_mandatory_connections: for exitname, regionname in inverted_mandatory_connections:
connect_simple(world, exitname, regionname, player) connect_simple(world, exitname, regionname, player)
# mandatory hybrid major glitches connections
if world.logic[player] in ['hybridglitches', 'nologic']:
underworld_glitch_connections(world, player)
# if we do not shuffle, set default connections # if we do not shuffle, set default connections
if world.shuffle[player] == 'vanilla': if world.shuffle[player] == 'vanilla':
for exitname, regionname in inverted_default_connections: for exitname, regionname in inverted_default_connections:

View File

@ -126,7 +126,7 @@ def create_regions(world, player):
create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']), create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']), create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']),
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit', 'Kiki Skip']),
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']), create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
create_cave_region(player, 'Hookshot Fairy', 'fairies deep in a cave'), create_cave_region(player, 'Hookshot Fairy', 'fairies deep in a cave'),
@ -250,7 +250,7 @@ def create_regions(world, player):
create_dungeon_region(player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']), create_dungeon_region(player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
create_dungeon_region(player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby', create_dungeon_region(player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']), 'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']),
create_dungeon_region(player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']), create_dungeon_region(player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest'], ['Mire to Hera Clip', 'Hera to Swamp Clip']),
create_dungeon_region(player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']), create_dungeon_region(player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
create_dungeon_region(player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']), create_dungeon_region(player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']), create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),

View File

@ -4,6 +4,7 @@ from worlds.alttp import OverworldGlitchRules
from BaseClasses import RegionType, MultiWorld, Entrance from BaseClasses import RegionType, MultiWorld, Entrance
from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups
from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
from worlds.alttp.UnderworldGlitchRules import underworld_glitches_rules
from worlds.alttp.Bosses import GanonDefeatRule from worlds.alttp.Bosses import GanonDefeatRule
from worlds.generic.Rules import set_rule, add_rule, forbid_item, add_item_rule, item_in_locations, \ from worlds.generic.Rules import set_rule, add_rule, forbid_item, add_item_rule, item_in_locations, \
item_name item_name

View File

@ -0,0 +1,113 @@
from BaseClasses import Entrance
from worlds.generic.Rules import set_rule, add_rule
# We actually need the logic to properly "mark" these regions as Light or Dark world.
# Therefore we need to make these connections during the normal link_entrances stage, rather than during set_rules.
def underworld_glitch_connections(world, player):
kikiskip = world.get_entrance('Kiki Skip', player)
mire_to_hera = world.get_entrance('Mire to Hera Clip', player)
mire_to_swamp = world.get_entrance('Hera to Swamp Clip', player)
if world.fix_fake_world[player]:
kikiskip.connect(world.get_entrance('Palace of Darkness Exit', player).connected_region)
mire_to_hera.connect(world.get_entrance('Tower of Hera Exit').connected_region)
mire_to_swamp.connect(world.get_entrance('Swamp Palace Exit').connected_region)
else:
kikiskip.connect(world.get_region('Palace of Darkness (Entrance)', player))
mire_to_hera.connect(world.get_region('Tower of Hera (Bottom)', player))
mire_to_swamp.connect(world.get_region('Swamp Palace (Entrance)', player))
def underworld_glitches_rules(world, player):
fix_dungeon_exits = world.fix_palaceofdarkness_exit[player]
fix_fake_worlds = world.fix_fake_world[player]
# Ice Palace Entrance Clip
# This is the easiest one since it's a simple internal clip. Just need to also add melting to freezor chest since it's otherwise assumed.
add_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_bomb_clip(world.get_region('Ice Palace (Entrance)', player), player), combine='or')
add_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: state.can_melt_things(player))
# Kiki Skip
kikiskip = world.get_entrance('Kiki Skip', player)
set_rule(kikiskip, lambda state: state.can_bomb_clip(kikiskip.parent_region, player))
# Behavior differs based on what type of ER shuffle we're playing.
if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple (this should always have no FWF)
# Dungeons are only shuffled among themselves. We need to check SW and MM because they can't be reentered easily.
pod = world.get_region('Palace of Darkness (Entrance)', player)
if pod.entrances[0].name == 'Skull Woods Final Section':
set_rule(kikiskip, lambda state: False)
elif pod.entrances[0].name == 'Misery Mire':
add_rule(kikiskip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player))
# Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally.
add_rule(world.get_entrance('Palace of Darkness Exit', player), lambda state: pod.entrances[0].can_reach(state))
elif not fix_fake_worlds: # full, dungeonsfull; has fixed exits but no FWF
# Entry requires the entrance's requirements, but you don't have logical access to the surrounding region.
pod = world.get_region('Palace of Darkness (Entrance)', player)
add_rule(kikiskip, pod.entrances[0].access_rule)
# exiting restriction
add_rule(world.get_entrance('Palace of Darkness Exit', player), lambda state: pod.entrances[0].can_reach(state))
# Mire -> Hera -> Swamp
# Using mire keys on other dungeon doors
mire = world.get_region('Misery Mire (West)', player)
mire_clip = lambda state: state.can_reach('Misery Mire (West)', 'Region', player) and state.can_bomb_clip(mire, player) and state.has_fire_source(player)
hera_clip = lambda state: state.can_reach('Tower of Hera (Top)', 'Region', player) and state.can_bomb_clip(world.get_region('Tower of Hera (Top)', player), player)
add_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: mire_clip(state) and state.has('Big Key (Misery Mire)', player), combine='or')
add_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: mire_clip(state), combine='or')
add_rule(world.get_entrance('Swamp Palace (Center)', 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]:
mirrorless_moat_rules = []
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
mirrorless_moat_rules.append(lambda state: state.can_reach('Old Man S&Q', 'Entrance', player) and mire_clip(state))
rule_map = {
'Misery Mire (Entrance)': (lambda state: True),
'Tower of Hera (Bottom)': (lambda state: state.can_reach('Tower of Hera Big Key Door', 'Entrance', player))
}
inverted = world.mode[player] == 'inverted'
hera_rule = lambda state: (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)
gt_rule = lambda state: (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)
mirrorless_moat_rules.append(lambda state: hera_rule(state) or gt_rule(state))
else:
mirrorless_moat_rules.append(lambda state: False) # all function returns True on empty list
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player) or all([rule(state) for rule in mirrorless_moat_rules]))
# 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)
set_rule(mire_to_hera, mire_clip)
set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has('Flippers', player))
if not fix_dungeon_exits:
hera = world.get_region('Tower of Hera (Bottom)', player)
if hera.entrances[0].name == 'Skull Woods Final Section':
set_rule(mire_to_hera, lambda state: False)
elif hera.entrances[0].name == 'Misery Mire':
add_rule(mire_to_hera, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player))
add_rule(world.get_entrance('Tower of Hera Exit', player), lambda state: hera.entrances[0].can_reach(state))
swamp = world.get_region('Swamp Palace (Entrance)', player)
if swamp.entrances[0].name == 'Skull Woods Final Section':
set_rule(mire_to_swamp, lambda state: False)
elif swamp.entrances[0].name == 'Misery Mire':
add_rule(mire_to_swamp, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player))
add_rule(world.get_entrance('Swamp Palace Exit', player), lambda state: swamp.entrances[0].can_reach(state))
elif not fix_fake_worlds:
hera = world.get_region('Tower of Hera (Bottom)', player)
add_rule(mire_to_hera, hera.entrances[0].access_rule)
add_rule(world.get_entrance('Tower of Hera Exit', player), lambda state: hera.entrances[0].can_reach(state))
swamp = world.get_region('Swamp Palace (Entrance)', player)
add_rule(mire_to_swamp, swamp.entrances[0].access_rule)
add_rule(world.get_entrance('Swamp Palace Exit', player), lambda state: swamp.entrances[0].can_reach(state))