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:
@@ -111,7 +111,7 @@ class World(object):
|
||||
set_player_attr('can_access_trock_front', None)
|
||||
set_player_attr('can_access_trock_big_chest', None)
|
||||
set_player_attr('can_access_trock_middle', None)
|
||||
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic']
|
||||
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'hybridglitches', 'nologic']
|
||||
or shuffle[player] in ['lean', 'swapped', 'crossed', 'insanity'])
|
||||
set_player_attr('mapshuffle', False)
|
||||
set_player_attr('compassshuffle', False)
|
||||
@@ -1100,6 +1100,9 @@ class CollectionState(object):
|
||||
def can_lift_rocks(self, player):
|
||||
return self.has('Power Glove', player) or self.has('Titans Mitts', player)
|
||||
|
||||
def can_bomb_clip(self, region, player: int) -> bool:
|
||||
return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) and self.can_use_bombs(player)
|
||||
|
||||
def has_bottle(self, player):
|
||||
return self.bottle_count(player) > 0
|
||||
|
||||
@@ -2973,7 +2976,7 @@ er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4,
|
||||
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, 'swapped': 10}
|
||||
|
||||
# byte 1: LLLW WSS? (logic, mode, sword)
|
||||
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4}
|
||||
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5}
|
||||
world_mode = {"open": 0, "standard": 1, "inverted": 2}
|
||||
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
|
||||
|
||||
|
||||
6
Doors.py
6
Doors.py
@@ -938,8 +938,10 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire Dark Shooters Up Stairs', Sprl).dir(Up, 0x93, 0, LTH).ss(A, 0x32, 0xec),
|
||||
create_door(player, 'Mire Dark Shooters SW', Intr).dir(So, 0x93, Left, High).pos(0),
|
||||
create_door(player, 'Mire Block X NW', Intr).dir(No, 0x93, Left, High).pos(0),
|
||||
create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).pos(1) if world.logic[player] == 'hybridglitches' else create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).pos(1) if world.logic[player] == 'hybridglitches' else create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1),
|
||||
# create_door(player, 'Mire Dark Shooters SE', Intr).dir(So, 0x93, Right, High).small_key().pos(1),
|
||||
# create_door(player, 'Mire Key Rupees NE', Intr).dir(No, 0x93, Right, High).small_key().pos(1),
|
||||
create_door(player, 'Mire Block X WS', Nrml).dir(We, 0x93, Bot, High).pos(2),
|
||||
create_door(player, 'Mire Tall Dark and Roomy ES', Nrml).dir(Ea, 0x92, Bot, High).pos(4),
|
||||
create_door(player, 'Mire Tall Dark and Roomy WN', Intr).dir(We, 0x92, Top, High).pos(0),
|
||||
|
||||
@@ -1112,7 +1112,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
|
||||
invalid_connections = Must_Exit_Invalid_Connections.copy()
|
||||
invalid_cave_connections = defaultdict(set)
|
||||
|
||||
if world.logic[player] in ['owglitches', 'nologic']:
|
||||
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
|
||||
import OverworldGlitchRules
|
||||
for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'):
|
||||
invalid_connections[entrance] = set()
|
||||
|
||||
5
Fill.py
5
Fill.py
@@ -175,6 +175,9 @@ def valid_key_placement(item, location, key_pool, collection_state, world):
|
||||
if dungeon:
|
||||
if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name):
|
||||
return True
|
||||
# Small key and big key in Swamp and Hera are placed without logic
|
||||
if world.logic[item.player] == 'hybridglitches' and dungeon.name in ['Tower of Hera', 'Swamp Palace'] and dungeon.name in item.name:
|
||||
return True
|
||||
key_logic = world.key_logic[item.player][dungeon.name]
|
||||
unplaced_keys = len([x for x in key_pool if x.name == key_logic.small_key_name and x.player == item.player])
|
||||
prize_loc = None
|
||||
@@ -398,7 +401,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
||||
# fill in gtower locations with trash first
|
||||
for player in range(1, world.players + 1):
|
||||
if (not gftower_trash or not world.ganonstower_vanilla[player]
|
||||
or world.logic[player] in ['owglitches', 'nologic']):
|
||||
or world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']):
|
||||
continue
|
||||
gt_count, total_count = calc_trash_locations(world, player)
|
||||
scale_factor = .75 * (world.crystals_needed_for_gt[player] / 7)
|
||||
|
||||
@@ -881,7 +881,7 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt
|
||||
return random.choice([True, False]) if progressive == 'random' else progressive == 'on'
|
||||
|
||||
# provide boots to boots glitch dependent modes
|
||||
if logic in ['owglitches', 'nologic']:
|
||||
if logic in ['owglitches', 'hybridglitches', 'nologic']:
|
||||
precollected_items.append('Pegasus Boots')
|
||||
pool.remove('Pegasus Boots')
|
||||
pool.extend(['Rupees (20)'])
|
||||
@@ -1174,7 +1174,7 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
|
||||
pool.extend(['Nothing'] * nothings)
|
||||
|
||||
start_inventory = [x for x in world.precollected_items if x.player == player]
|
||||
if world.logic[player] in ['owglitches', 'nologic'] and all(x.name !=' Pegasus Boots' for x in start_inventory):
|
||||
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name !=' Pegasus Boots' for x in start_inventory):
|
||||
precollected_items.append('Pegasus Boots')
|
||||
if 'Pegasus Boots' in pool:
|
||||
pool.remove('Pegasus Boots')
|
||||
@@ -1316,7 +1316,7 @@ def make_customizer_pool(world, player):
|
||||
sphere_0 = world.customizer.get_start_inventory()
|
||||
no_start_inventory = not sphere_0 or not sphere_0[player]
|
||||
init_equip = [] if no_start_inventory else sphere_0[player]
|
||||
if (world.logic[player] in ['owglitches', 'nologic']
|
||||
if (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']
|
||||
and (no_start_inventory or all(x != 'Pegasus Boots' for x in init_equip))):
|
||||
precollected_items.append('Pegasus Boots')
|
||||
if 'Pegasus Boots' in pool:
|
||||
|
||||
@@ -2106,6 +2106,22 @@ def validate_key_placement(key_layout, world, player):
|
||||
if i.player == player and i.name == smallkey_name:
|
||||
keys_outside += 1
|
||||
|
||||
if world.logic[player] == 'hybridglitches':
|
||||
# Swamp keylogic
|
||||
if smallkey_name.endswith('(Swamp Palace)'):
|
||||
swamp_entrance = world.get_location('Swamp Palace - Entrance', player)
|
||||
# Swamp small not vanilla
|
||||
if swamp_entrance.item is None or (swamp_entrance.item.name != smallkey_name or swamp_entrance.item.player != player):
|
||||
mire_keylayout = world.key_layout[player]['Misery Mire']
|
||||
mire_smallkey_name = dungeon_keys[mire_keylayout.sector.name]
|
||||
# Check if any mire keys are in swamp (excluding entrance), if none then add one to keys_outside
|
||||
mire_keys_in_swamp = sum([1 if x.item.name == mire_smallkey_name else 0 for x in key_layout.item_locations if x.item is not None and x != swamp_entrance])
|
||||
if mire_keys_in_swamp == 0:
|
||||
keys_outside +=1
|
||||
# Mire keylogic
|
||||
if smallkey_name.endswith('(Tower of Hera)'):
|
||||
# TODO: Make sure that mire medallion isn't in hera basement, or if it it, the small key is available downstairs
|
||||
big_key_outside = True
|
||||
for code, counter in key_layout.key_counters.items():
|
||||
if len(counter.child_doors) == 0:
|
||||
continue
|
||||
|
||||
14
Main.py
14
Main.py
@@ -27,6 +27,7 @@ from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dunge
|
||||
from Fill import dungeon_tracking
|
||||
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations
|
||||
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items
|
||||
from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors
|
||||
from Utils import output_path, parse_player_names
|
||||
|
||||
from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config
|
||||
@@ -205,7 +206,7 @@ def main(args, seed=None, fish=None):
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
create_regions(world, player)
|
||||
if world.logic[player] in ('owglitches', 'nologic'):
|
||||
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
|
||||
create_owg_connections(world, player)
|
||||
create_dungeon_regions(world, player)
|
||||
create_shops(world, player)
|
||||
@@ -216,6 +217,8 @@ def main(args, seed=None, fish=None):
|
||||
world.data_tables[player] = init_data_tables(world, player)
|
||||
place_bosses(world, player)
|
||||
randomize_enemies(world, player)
|
||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
||||
create_hybridmajor_connections(world, player)
|
||||
adjust_locations(world, player)
|
||||
|
||||
if world.customizer and world.customizer.get_start_inventory():
|
||||
@@ -245,6 +248,8 @@ def main(args, seed=None, fish=None):
|
||||
link_entrances_new(world, player)
|
||||
else:
|
||||
link_entrances(world, player)
|
||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
||||
create_hybridmajor_connectors(world, player)
|
||||
|
||||
logger.info(world.fish.translate("cli", "cli", "shuffling.prep"))
|
||||
for player in range(1, world.players + 1):
|
||||
@@ -497,8 +502,11 @@ def copy_world(world):
|
||||
create_shops(ret, player)
|
||||
create_rooms(ret, player)
|
||||
create_dungeons(ret, player)
|
||||
if world.logic[player] in ('owglitches', 'nologic'):
|
||||
if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'):
|
||||
create_owg_connections(ret, player)
|
||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
||||
create_hybridmajor_connections(ret, player)
|
||||
|
||||
|
||||
# there are region references here they must be migrated to preserve integrity
|
||||
# ret.exp_cache = world.exp_cache.copy()
|
||||
@@ -579,6 +587,8 @@ def copy_world(world):
|
||||
ret.sanc_portal = world.sanc_portal
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
if world.logic[player] in ('nologic', 'hybridglitches'):
|
||||
create_hybridmajor_connectors(ret, player)
|
||||
set_rules(ret, player)
|
||||
|
||||
return ret
|
||||
|
||||
18
Rules.py
18
Rules.py
@@ -8,6 +8,7 @@ from BaseClasses import PotFlags
|
||||
from Dungeons import dungeon_table
|
||||
from RoomData import DoorKind
|
||||
from OverworldGlitchRules import overworld_glitches_rules
|
||||
from UnderworldGlitchRules import underworld_glitches_rules
|
||||
|
||||
from source.logic.Rule import RuleFactory
|
||||
from source.dungeon.EnemyList import EnemySprite, Sprite
|
||||
@@ -48,7 +49,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.
|
||||
@@ -73,7 +74,7 @@ def set_rules(world, player):
|
||||
|
||||
if world.mode[player] != 'inverted':
|
||||
set_big_bomb_rules(world, player)
|
||||
if world.logic[player] == 'owglitches' and world.shuffle[player] != 'insanity':
|
||||
if world.logic[player] in ['owglitches', 'hybridglitches'] and world.shuffle[player] != 'insanity':
|
||||
path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player)
|
||||
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.world.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or')
|
||||
else:
|
||||
@@ -85,7 +86,11 @@ def set_rules(world, player):
|
||||
|
||||
set_bunny_rules(world, player, world.mode[player] == 'inverted')
|
||||
|
||||
if world.mode[player] != 'inverted' and world.logic[player] == 'owglitches':
|
||||
# These rules go here because the overwrite/add to some of the above rules
|
||||
if world.logic[player] == 'hybridglitches':
|
||||
underworld_glitches_rules(world, player)
|
||||
|
||||
if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||
add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.world.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or')
|
||||
|
||||
|
||||
@@ -1898,7 +1903,7 @@ def set_bunny_rules(world, player, inverted):
|
||||
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 not is_link(region):
|
||||
@@ -1928,7 +1933,7 @@ def set_bunny_rules(world, player, inverted):
|
||||
new_path = path + [entrance.access_rule]
|
||||
new_seen = seen.union({new_region})
|
||||
if not is_link(new_region):
|
||||
if world.logic[player] == 'owglitches':
|
||||
if world.logic[player] in ['owglitches', 'hybridglitches']:
|
||||
if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon:
|
||||
if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
|
||||
continue
|
||||
@@ -2154,6 +2159,9 @@ def add_key_logic_rules(world, player):
|
||||
add_rule(dep.entrance, eval_func(door_name, d_name, player))
|
||||
for location in d_logic.bk_restricted:
|
||||
if not location.forced_item:
|
||||
# Skip BK restricted locations in hybrid glitches. Bad, but necessary for now.
|
||||
if world.logic[player] == 'hybridglitches' and d_name in ['Tower of Hera', 'Swamp Palace']:
|
||||
continue
|
||||
forbid_item(location, d_logic.bk_name, player)
|
||||
for location in d_logic.sm_restricted:
|
||||
forbid_item(location, d_logic.small_key_name, player)
|
||||
|
||||
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")
|
||||
@@ -23,6 +23,7 @@
|
||||
"noglitches",
|
||||
"minorglitches",
|
||||
"owglitches",
|
||||
"hybridglitches",
|
||||
"nologic"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -63,11 +63,13 @@
|
||||
"bps": [ "Output BPS patches instead of ROMs"],
|
||||
"logic": [
|
||||
"Select Enforcement of Item Requirements. (default: %(default)s)",
|
||||
"No Glitches: No Glitch knowledge required.",
|
||||
"Minor Glitches: May require Fake Flippers, Bunny Revival",
|
||||
" and Dark Room Navigation.",
|
||||
"No Logic: Distribute items without regard for",
|
||||
" item requirements."
|
||||
"No Glitches: No Glitch knowledge required.",
|
||||
"Minor Glitches: May require Fake Flippers, Bunny Revival",
|
||||
" and Dark Room Navigation.",
|
||||
"Overworld Glitches: May require overworld clips and teleports.",
|
||||
"Hybrid Major Glitches: May require underworld clips.",
|
||||
"No Logic: Distribute items without regard for",
|
||||
" item requirements."
|
||||
],
|
||||
"mode": [
|
||||
"Select game mode. (default: %(default)s)",
|
||||
|
||||
@@ -237,6 +237,7 @@
|
||||
"randomizer.item.logiclevel.noglitches": "No Glitches",
|
||||
"randomizer.item.logiclevel.minorglitches": "Minor Glitches",
|
||||
"randomizer.item.logiclevel.owglitches": "Overworld Glitches",
|
||||
"randomizer.item.logiclevel.hybridglitches": "Hybrid Major Glitches",
|
||||
"randomizer.item.logiclevel.nologic": "No Logic",
|
||||
|
||||
"randomizer.item.goal": "Goal",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"noglitches",
|
||||
"minorglitches",
|
||||
"owglitches",
|
||||
"hybridglitches",
|
||||
"nologic"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -957,7 +957,7 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
||||
invalid_connections = Must_Exit_Invalid_Connections.copy()
|
||||
invalid_cave_connections = defaultdict(set)
|
||||
|
||||
if avail.world.logic[avail.player] in ['owglitches', 'nologic']:
|
||||
if avail.world.logic[avail.player] in ['owglitches', 'hybridglitches', 'nologic']:
|
||||
import OverworldGlitchRules
|
||||
for entrance in OverworldGlitchRules.get_non_mandatory_exits(avail.inverted):
|
||||
invalid_connections[entrance] = set()
|
||||
|
||||
@@ -80,7 +80,8 @@ def roll_settings(weights):
|
||||
|
||||
ret.algorithm = get_choice('algorithm')
|
||||
|
||||
glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'owglitches': 'owglitches',
|
||||
glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'hybridglitches': 'hybridglitches',
|
||||
'hmg': 'hybridglitches', 'owglitches': 'owglitches',
|
||||
'owg': 'owglitches', 'minorglitches': 'minorglitches'}
|
||||
glitches_required = get_choice('glitches_required')
|
||||
if glitches_required is not None:
|
||||
|
||||
35
test/customizer/hmg/fireless_ice.yaml
Normal file
35
test/customizer/hmg/fireless_ice.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
meta:
|
||||
players: 1
|
||||
settings:
|
||||
1:
|
||||
accessibility: items
|
||||
door_shuffle: vanilla
|
||||
logic: hybridglitches
|
||||
mode: open
|
||||
shuffle: vanilla
|
||||
doors:
|
||||
1:
|
||||
doors:
|
||||
Hera Beetles WS: Hera Startile Corner ES
|
||||
Hera Startile Corner ES: Hera Beetles WS
|
||||
Hera Startile Corner NW: Hera Startile Wide SW
|
||||
Hera Startile Wide SW: Hera Startile Corner NW
|
||||
Skull 1 Lobby ES: Skull Map Room WS
|
||||
Skull 2 West Lobby ES: Skull Small Hall WS
|
||||
Skull 2 West Lobby NW: Skull X Room SW
|
||||
Skull Big Chest N: Skull Pull Switch S
|
||||
Skull Compass Room WS: Skull Left Drop ES
|
||||
Skull Final Drop WS: Skull Spike Corner ES
|
||||
Skull Left Drop ES: Skull Compass Room WS
|
||||
Skull Map Room WS: Skull 1 Lobby ES
|
||||
Skull Pot Circle WN: Skull Pull Switch EN
|
||||
Skull Pull Switch EN: Skull Pot Circle WN
|
||||
Skull Pull Switch S: Skull Big Chest N
|
||||
Skull Small Hall WS: Skull 2 West Lobby ES
|
||||
Skull Spike Corner ES: Skull Final Drop WS
|
||||
Skull X Room SW: Skull 2 West Lobby NW
|
||||
lobbies: {}
|
||||
placements:
|
||||
1:
|
||||
Ice Palace - Compass Chest: Fire Rod
|
||||
Ice Palace - Freezor Chest: Bombos
|
||||
42
test/customizer/hmg/hammer_in_swamp.yaml
Normal file
42
test/customizer/hmg/hammer_in_swamp.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
meta:
|
||||
players: 1
|
||||
settings:
|
||||
1:
|
||||
accessibility: items
|
||||
door_shuffle: vanilla
|
||||
logic: hybridglitches
|
||||
mode: open
|
||||
shuffle: vanilla
|
||||
start_inventory:
|
||||
1:
|
||||
- Flippers
|
||||
- Lamp
|
||||
- Tempered Sword
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
doors:
|
||||
1:
|
||||
doors:
|
||||
Hera Beetles WS: Hera Startile Corner ES
|
||||
Hera Startile Corner ES: Hera Beetles WS
|
||||
Hera Startile Corner NW: Hera Startile Wide SW
|
||||
Hera Startile Wide SW: Hera Startile Corner NW
|
||||
Skull 1 Lobby ES: Skull Map Room WS
|
||||
Skull 2 West Lobby ES: Skull Small Hall WS
|
||||
Skull 2 West Lobby NW: Skull X Room SW
|
||||
Skull Big Chest N: Skull Pull Switch S
|
||||
Skull Compass Room WS: Skull Left Drop ES
|
||||
Skull Final Drop WS: Skull Spike Corner ES
|
||||
Skull Left Drop ES: Skull Compass Room WS
|
||||
Skull Map Room WS: Skull 1 Lobby ES
|
||||
Skull Pot Circle WN: Skull Pull Switch EN
|
||||
Skull Pull Switch EN: Skull Pot Circle WN
|
||||
Skull Pull Switch S: Skull Big Chest N
|
||||
Skull Small Hall WS: Skull 2 West Lobby ES
|
||||
Skull Spike Corner ES: Skull Final Drop WS
|
||||
Skull X Room SW: Skull 2 West Lobby NW
|
||||
lobbies: {}
|
||||
placements:
|
||||
1:
|
||||
Swamp Palace - Big Chest: Hammer
|
||||
34
test/customizer/hmg/mearl_in_pod.yaml
Normal file
34
test/customizer/hmg/mearl_in_pod.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
meta:
|
||||
players: 1
|
||||
settings:
|
||||
1:
|
||||
accessibility: items
|
||||
door_shuffle: vanilla
|
||||
logic: hybridglitches
|
||||
mode: open
|
||||
shuffle: vanilla
|
||||
doors:
|
||||
1:
|
||||
doors:
|
||||
Hera Beetles WS: Hera Startile Corner ES
|
||||
Hera Startile Corner ES: Hera Beetles WS
|
||||
Hera Startile Corner NW: Hera Startile Wide SW
|
||||
Hera Startile Wide SW: Hera Startile Corner NW
|
||||
Skull 1 Lobby ES: Skull Map Room WS
|
||||
Skull 2 West Lobby ES: Skull Small Hall WS
|
||||
Skull 2 West Lobby NW: Skull X Room SW
|
||||
Skull Big Chest N: Skull Pull Switch S
|
||||
Skull Compass Room WS: Skull Left Drop ES
|
||||
Skull Final Drop WS: Skull Spike Corner ES
|
||||
Skull Left Drop ES: Skull Compass Room WS
|
||||
Skull Map Room WS: Skull 1 Lobby ES
|
||||
Skull Pot Circle WN: Skull Pull Switch EN
|
||||
Skull Pull Switch EN: Skull Pot Circle WN
|
||||
Skull Pull Switch S: Skull Big Chest N
|
||||
Skull Small Hall WS: Skull 2 West Lobby ES
|
||||
Skull Spike Corner ES: Skull Final Drop WS
|
||||
Skull X Room SW: Skull 2 West Lobby NW
|
||||
lobbies: {}
|
||||
placements:
|
||||
1:
|
||||
Palace of Darkness - Shooter Room: Moon Pearl
|
||||
42
test/customizer/hmg/mirrorless_swamp.yaml
Normal file
42
test/customizer/hmg/mirrorless_swamp.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
meta:
|
||||
players: 1
|
||||
settings:
|
||||
1:
|
||||
accessibility: items
|
||||
door_shuffle: vanilla
|
||||
logic: hybridglitches
|
||||
mode: open
|
||||
shuffle: vanilla
|
||||
start_inventory:
|
||||
1:
|
||||
- Flippers
|
||||
- Lamp
|
||||
- Tempered Sword
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
doors:
|
||||
1:
|
||||
doors:
|
||||
Hera Beetles WS: Hera Startile Corner ES
|
||||
Hera Startile Corner ES: Hera Beetles WS
|
||||
Hera Startile Corner NW: Hera Startile Wide SW
|
||||
Hera Startile Wide SW: Hera Startile Corner NW
|
||||
Skull 1 Lobby ES: Skull Map Room WS
|
||||
Skull 2 West Lobby ES: Skull Small Hall WS
|
||||
Skull 2 West Lobby NW: Skull X Room SW
|
||||
Skull Big Chest N: Skull Pull Switch S
|
||||
Skull Compass Room WS: Skull Left Drop ES
|
||||
Skull Final Drop WS: Skull Spike Corner ES
|
||||
Skull Left Drop ES: Skull Compass Room WS
|
||||
Skull Map Room WS: Skull 1 Lobby ES
|
||||
Skull Pot Circle WN: Skull Pull Switch EN
|
||||
Skull Pull Switch EN: Skull Pot Circle WN
|
||||
Skull Pull Switch S: Skull Big Chest N
|
||||
Skull Small Hall WS: Skull 2 West Lobby ES
|
||||
Skull Spike Corner ES: Skull Final Drop WS
|
||||
Skull X Room SW: Skull 2 West Lobby NW
|
||||
lobbies: {}
|
||||
placements:
|
||||
1:
|
||||
Swamp Palace - Big Chest: Magic Mirror
|
||||
44
test/customizer/hmg/pod_as_connector.yaml
Normal file
44
test/customizer/hmg/pod_as_connector.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
meta:
|
||||
players: 1
|
||||
settings:
|
||||
1:
|
||||
accessibility: items
|
||||
door_shuffle: vanilla
|
||||
logic: hybridglitches
|
||||
mode: open
|
||||
shuffle: crossed
|
||||
doors:
|
||||
1:
|
||||
doors:
|
||||
Hera Beetles WS: Hera Startile Corner ES
|
||||
Hera Startile Corner ES: Hera Beetles WS
|
||||
Hera Startile Corner NW: Hera Startile Wide SW
|
||||
Hera Startile Wide SW: Hera Startile Corner NW
|
||||
Skull 1 Lobby ES: Skull Map Room WS
|
||||
Skull 2 West Lobby ES: Skull Small Hall WS
|
||||
Skull 2 West Lobby NW: Skull X Room SW
|
||||
Skull Big Chest N: Skull Pull Switch S
|
||||
Skull Compass Room WS: Skull Left Drop ES
|
||||
Skull Final Drop WS: Skull Spike Corner ES
|
||||
Skull Left Drop ES: Skull Compass Room WS
|
||||
Skull Map Room WS: Skull 1 Lobby ES
|
||||
Skull Pot Circle WN: Skull Pull Switch EN
|
||||
Skull Pull Switch EN: Skull Pot Circle WN
|
||||
Skull Pull Switch S: Skull Big Chest N
|
||||
Skull Small Hall WS: Skull 2 West Lobby ES
|
||||
Skull Spike Corner ES: Skull Final Drop WS
|
||||
Skull X Room SW: Skull 2 West Lobby NW
|
||||
lobbies: {}
|
||||
entrances:
|
||||
1:
|
||||
entrances:
|
||||
Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave
|
||||
exits:
|
||||
Links House: Chris Houlihan Room Exit
|
||||
two-way:
|
||||
Dark Lake Hylia Ledge Fairy: Palace of Darkness Exit
|
||||
Lake Hylia Fortune Teller: Spectacle Rock Cave Exit
|
||||
Links House: Links House Exit
|
||||
placements:
|
||||
1:
|
||||
Peg Cave: Moon Pearl
|
||||
55
test/customizer/hmg/swamp_as_connector.yaml
Normal file
55
test/customizer/hmg/swamp_as_connector.yaml
Normal file
@@ -0,0 +1,55 @@
|
||||
meta:
|
||||
players: 1
|
||||
settings:
|
||||
1:
|
||||
accessibility: items
|
||||
door_shuffle: vanilla
|
||||
logic: hybridglitches
|
||||
mode: open
|
||||
shuffle: crossed
|
||||
start_inventory:
|
||||
1:
|
||||
- Hookshot
|
||||
- Lamp
|
||||
- Hammer
|
||||
- Magic Mirror
|
||||
- Tempered Sword
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
doors:
|
||||
1:
|
||||
doors:
|
||||
Hera Beetles WS: Hera Startile Corner ES
|
||||
Hera Startile Corner ES: Hera Beetles WS
|
||||
Hera Startile Corner NW: Hera Startile Wide SW
|
||||
Hera Startile Wide SW: Hera Startile Corner NW
|
||||
Skull 1 Lobby ES: Skull Map Room WS
|
||||
Skull 2 West Lobby ES: Skull Small Hall WS
|
||||
Skull 2 West Lobby NW: Skull X Room SW
|
||||
Skull Big Chest N: Skull Pull Switch S
|
||||
Skull Compass Room WS: Skull Left Drop ES
|
||||
Skull Final Drop WS: Skull Spike Corner ES
|
||||
Skull Left Drop ES: Skull Compass Room WS
|
||||
Skull Map Room WS: Skull 1 Lobby ES
|
||||
Skull Pot Circle WN: Skull Pull Switch EN
|
||||
Skull Pull Switch EN: Skull Pot Circle WN
|
||||
Skull Pull Switch S: Skull Big Chest N
|
||||
Skull Small Hall WS: Skull 2 West Lobby ES
|
||||
Skull Spike Corner ES: Skull Final Drop WS
|
||||
Skull X Room SW: Skull 2 West Lobby NW
|
||||
lobbies: {}
|
||||
entrances:
|
||||
1:
|
||||
entrances:
|
||||
Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave
|
||||
exits:
|
||||
Links House: Chris Houlihan Room Exit
|
||||
two-way:
|
||||
Dark Lake Hylia Ledge Fairy: Swamp Palace Exit
|
||||
Lake Hylia Fortune Teller: Misery Mire Exit
|
||||
Links House: Links House Exit
|
||||
placements:
|
||||
1:
|
||||
Peg Cave: Moon Pearl
|
||||
|
||||
44
test/customizer/hmg/swamp_small_in_swamp_back.yaml
Normal file
44
test/customizer/hmg/swamp_small_in_swamp_back.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
meta:
|
||||
players: 1
|
||||
settings:
|
||||
1:
|
||||
accessibility: items
|
||||
door_shuffle: vanilla
|
||||
logic: hybridglitches
|
||||
mode: open
|
||||
shuffle: vanilla
|
||||
start_inventory:
|
||||
1:
|
||||
- Hookshot
|
||||
- Lamp
|
||||
- Hammer
|
||||
- Magic Mirror
|
||||
- Tempered Sword
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
- Boss Heart Container
|
||||
doors:
|
||||
1:
|
||||
doors:
|
||||
Hera Beetles WS: Hera Startile Corner ES
|
||||
Hera Startile Corner ES: Hera Beetles WS
|
||||
Hera Startile Corner NW: Hera Startile Wide SW
|
||||
Hera Startile Wide SW: Hera Startile Corner NW
|
||||
Skull 1 Lobby ES: Skull Map Room WS
|
||||
Skull 2 West Lobby ES: Skull Small Hall WS
|
||||
Skull 2 West Lobby NW: Skull X Room SW
|
||||
Skull Big Chest N: Skull Pull Switch S
|
||||
Skull Compass Room WS: Skull Left Drop ES
|
||||
Skull Final Drop WS: Skull Spike Corner ES
|
||||
Skull Left Drop ES: Skull Compass Room WS
|
||||
Skull Map Room WS: Skull 1 Lobby ES
|
||||
Skull Pot Circle WN: Skull Pull Switch EN
|
||||
Skull Pull Switch EN: Skull Pot Circle WN
|
||||
Skull Pull Switch S: Skull Big Chest N
|
||||
Skull Small Hall WS: Skull 2 West Lobby ES
|
||||
Skull Spike Corner ES: Skull Final Drop WS
|
||||
Skull X Room SW: Skull 2 West Lobby NW
|
||||
lobbies: {}
|
||||
# placements:
|
||||
# 1:
|
||||
# Swamp Palace - Entrance: Boss Heart Container
|
||||
Reference in New Issue
Block a user