New Logic:
Enemy Shuffle awareness Easy Item Functionality awareness Dark Room Logic option Boss Item Shuffle option Silverless Ganon is a Minor Glitch Faster Dungeon Item Fill
This commit is contained in:
parent
a855fc4133
commit
b5d91af752
|
@ -5,7 +5,7 @@ from enum import Enum, unique
|
|||
import logging
|
||||
import json
|
||||
from collections import OrderedDict, Counter, deque
|
||||
from typing import Union, Optional, List, Set
|
||||
from typing import Union, Optional, List, Set, Dict
|
||||
import secrets
|
||||
import random
|
||||
|
||||
|
@ -20,6 +20,8 @@ class World(object):
|
|||
_region_cache: dict
|
||||
difficulty_requirements: dict
|
||||
required_medallions: dict
|
||||
dark_room_logic: Dict[int, str]
|
||||
restrict_dungeon_item_on_boss: Dict[int, bool]
|
||||
|
||||
def __init__(self, players: int, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer,
|
||||
progressive,
|
||||
|
@ -126,6 +128,8 @@ class World(object):
|
|||
set_player_attr('shop_shuffle', 'off')
|
||||
set_player_attr('shuffle_prizes', "g")
|
||||
set_player_attr('sprite_pool', [])
|
||||
set_player_attr('dark_room_logic', "lamp")
|
||||
set_player_attr('restrict_dungeon_item_on_boss', False)
|
||||
|
||||
def secure(self):
|
||||
self.random = secrets.SystemRandom()
|
||||
|
@ -357,6 +361,9 @@ class World(object):
|
|||
location.player == player and not location.item]
|
||||
return [location for location in self.get_locations() if not location.item]
|
||||
|
||||
def get_unfilled_dungeon_locations(self):
|
||||
return [location for location in self.get_locations() if not location.item and location.parent_region.dungeon]
|
||||
|
||||
def get_filled_locations(self, player=None) -> list:
|
||||
if player is not None:
|
||||
return [location for location in self.get_locations() if
|
||||
|
@ -601,7 +608,6 @@ class CollectionState(object):
|
|||
|
||||
def can_shoot_arrows(self, player: int) -> bool:
|
||||
if self.world.retro[player]:
|
||||
# TODO: Progressive and Non-Progressive silvers work differently (progressive is not usable until the shop arrow is bought)
|
||||
return (self.has('Bow', player) or self.has('Silver Bow', player)) and self.can_buy('Single Arrow', player)
|
||||
return self.has('Bow', player) or self.has('Silver Bow', player)
|
||||
|
||||
|
@ -615,6 +621,11 @@ class CollectionState(object):
|
|||
self.is_not_bunny(cave, player)
|
||||
)
|
||||
|
||||
def can_retrieve_tablet(self, player:int) -> bool:
|
||||
return self.has('Book of Mudora', player) and (self.has_beam_sword(player) or
|
||||
((self.world.swords[player] == "swordless" or self.world.difficulty_adjustments[player] == "easy") and
|
||||
self.has("Hammer", player)))
|
||||
|
||||
def has_sword(self, player: int) -> bool:
|
||||
return self.has('Fighter Sword', player) \
|
||||
or self.has('Master Sword', player) \
|
||||
|
@ -644,7 +655,10 @@ class CollectionState(object):
|
|||
return self.has('Flute', player) and lw.can_reach(self) and self.is_not_bunny(lw, player)
|
||||
|
||||
def can_melt_things(self, player: int) -> bool:
|
||||
return self.has('Fire Rod', player) or (self.has('Bombos', player) and (self.has_sword(player) or self.world.swords[player] == "swordless"))
|
||||
return self.has('Fire Rod', player) or \
|
||||
(self.has('Bombos', player) and
|
||||
(self.world.difficulty_adjustments[player] == "easy" or self.world.swords[player] == "swordless" or
|
||||
self.has_sword(player)))
|
||||
|
||||
def can_avoid_lasers(self, player: int) -> bool:
|
||||
return self.has('Mirror Shield', player) or self.has('Cane of Byrna', player) or self.has('Cape', player)
|
||||
|
@ -1260,6 +1274,7 @@ class Spoiler(object):
|
|||
from Utils import __version__ as ERVersion
|
||||
self.metadata = {'version': ERVersion,
|
||||
'logic': self.world.logic,
|
||||
'dark_room_logic': self.world.dark_room_logic,
|
||||
'mode': self.world.mode,
|
||||
'retro': self.world.retro,
|
||||
'weapons': self.world.swords,
|
||||
|
@ -1293,7 +1308,8 @@ class Spoiler(object):
|
|||
'triforce_pieces_required': self.world.triforce_pieces_required,
|
||||
'shop_shuffle': self.world.shop_shuffle,
|
||||
'shuffle_prizes': self.world.shuffle_prizes,
|
||||
'sprite_pool': self.world.sprite_pool
|
||||
'sprite_pool': self.world.sprite_poolm,
|
||||
'restrict_dungeon_item_on_boss': self.world.restrict_dungeon_item_on_boss
|
||||
}
|
||||
|
||||
def to_json(self):
|
||||
|
@ -1337,6 +1353,9 @@ class Spoiler(object):
|
|||
f"Hash - {self.world.player_names[player][team]} (Team {team + 1}): " if self.world.teams > 1 else 'Hash: ',
|
||||
self.hashes[player, team]))
|
||||
outfile.write('Logic: %s\n' % self.metadata['logic'][player])
|
||||
outfile.write('Dark Room Logic: %s\n' % self.metadata['dark_room_logic'][player])
|
||||
outfile.write('Restricted Boss Drops: %s\n' %
|
||||
bool_to_text(self.metadata['restrict_dungeon_item_on_boss'][player]))
|
||||
if self.world.players > 1:
|
||||
outfile.write('Progression Balanced: %s\n' % (
|
||||
'Yes' if self.metadata['progression_balancing'][player] else 'No'))
|
||||
|
|
21
Bosses.py
21
Bosses.py
|
@ -116,6 +116,27 @@ def AgahnimDefeatRule(state, player: int):
|
|||
return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player)
|
||||
|
||||
|
||||
def GanonDefeatRule(state, player: int):
|
||||
if state.world.swords[player] == "swordless":
|
||||
return state.has('Hammer', player) and \
|
||||
state.has_fire_source(player) and \
|
||||
state.has('Silver Bow', player) and \
|
||||
state.can_shoot_arrows(player)
|
||||
easy_hammer = state.world.difficulty_adjustments[player] == "easy" and state.has("Hammer", player) and \
|
||||
state.has('Silver Bow', player) and state.can_shoot_arrows(player)
|
||||
can_hurt = state.has_beam_sword(player) or easy_hammer
|
||||
common = can_hurt and state.has_fire_source(player)
|
||||
# silverless ganon may be needed in minor glitches
|
||||
if state.world.logic[player] in {"owglitches", "minorglitches", "none"}:
|
||||
# need to light torch a sufficient amount of times
|
||||
return common and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (
|
||||
state.has('Silver Bow', player) and state.can_shoot_arrows(player)) or
|
||||
state.has('Lamp', player) or state.can_extend_magic(player, 12))
|
||||
|
||||
else:
|
||||
return common and state.has('Silver Bow', player) and state.can_shoot_arrows(player)
|
||||
|
||||
|
||||
boss_table = {
|
||||
'Armos Knights': ('Armos', ArmosKnightsDefeatRule),
|
||||
'Lanmolas': ('Lanmola', LanmolasDefeatRule),
|
||||
|
|
22
Dungeons.py
22
Dungeons.py
|
@ -2,6 +2,7 @@ from BaseClasses import Dungeon
|
|||
from Bosses import BossFactory
|
||||
from Fill import fill_restrictive
|
||||
from Items import ItemFactory
|
||||
from Regions import lookup_boss_drops
|
||||
|
||||
|
||||
def create_dungeons(world, player):
|
||||
|
@ -116,7 +117,14 @@ def fill_dungeons(world):
|
|||
def get_dungeon_item_pool(world):
|
||||
return [item for dungeon in world.dungeons for item in dungeon.all_items]
|
||||
|
||||
def fill_dungeons_restrictive(world, shuffled_locations):
|
||||
def fill_dungeons_restrictive(world):
|
||||
"""Places dungeon-native items into their dungeons, places nothing if everything is shuffled outside."""
|
||||
restricted_players = {player for player, restricted in world.restrict_dungeon_item_on_boss.items() if restricted}
|
||||
|
||||
locations = [location for location in world.get_unfilled_dungeon_locations()
|
||||
if not (location.player in restricted_players and location.name in lookup_boss_drops)] # filter boss
|
||||
|
||||
world.random.shuffle(locations)
|
||||
all_state_base = world.get_all_state()
|
||||
|
||||
# with shuffled dungeon items they are distributed as part of the normal item pool
|
||||
|
@ -131,13 +139,11 @@ def fill_dungeons_restrictive(world, shuffled_locations):
|
|||
or (item.bigkey and not world.bigkeyshuffle[item.player])
|
||||
or (item.map and not world.mapshuffle[item.player])
|
||||
or (item.compass and not world.compassshuffle[item.player]))]
|
||||
|
||||
# sort in the order Big Key, Small Key, Other before placing dungeon items
|
||||
sort_order = {"BigKey": 3, "SmallKey": 2}
|
||||
dungeon_items.sort(key=lambda item: sort_order.get(item.type, 1))
|
||||
|
||||
fill_restrictive(world, all_state_base, shuffled_locations, dungeon_items, True)
|
||||
|
||||
if dungeon_items:
|
||||
# sort in the order Big Key, Small Key, Other before placing dungeon items
|
||||
sort_order = {"BigKey": 3, "SmallKey": 2}
|
||||
dungeon_items.sort(key=lambda item: sort_order.get(item.type, 1))
|
||||
fill_restrictive(world, all_state_base, locations, dungeon_items, True)
|
||||
|
||||
|
||||
dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
|
||||
|
|
|
@ -317,6 +317,11 @@ def parse_arguments(argv, no_defaults=False):
|
|||
parser.add_argument('--shuffle_prizes', default=defval('g'), choices=['', 'g', 'b', 'gb'])
|
||||
parser.add_argument('--sprite_pool', help='''\
|
||||
Specifies a colon separated list of sprites used for random/randomonevent. If not specified, the full sprite pool is used.''')
|
||||
parser.add_argument('--dark_room_logic', default=('Lamp'), choices=["lamp", "torches", "none"], help='''\
|
||||
For unlit dark rooms, require the Lamp to be considered in logic by default.
|
||||
Torches means additionally easily accessible Torches that can be lit with Fire Rod are considered doable.
|
||||
None means full traversal through dark rooms without tools is considered doable.''')
|
||||
parser.add_argument('--restrict_dungeon_item_on_boss', default=defval(False), action="store_true")
|
||||
parser.add_argument('--remote_items', default=defval(False), action='store_true')
|
||||
parser.add_argument('--multi', default=defval(1), type=lambda value: min(max(int(value), 1), 255))
|
||||
parser.add_argument('--names', default=defval(''))
|
||||
|
@ -362,7 +367,7 @@ def parse_arguments(argv, no_defaults=False):
|
|||
'heartbeep', "skip_progression_balancing", "triforce_pieces_available",
|
||||
"triforce_pieces_required", "shop_shuffle",
|
||||
'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves',
|
||||
'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool']:
|
||||
'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic', 'restrict_dungeon_item_on_boss']:
|
||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||
if player == 1:
|
||||
setattr(ret, name, {1: value})
|
||||
|
|
13
Gui.py
13
Gui.py
|
@ -256,6 +256,15 @@ def guiMain(args=None):
|
|||
logicLabel = Label(logicFrame, text='Game logic')
|
||||
logicLabel.pack(side=LEFT)
|
||||
|
||||
darklogicFrame = Frame(drowDownFrame)
|
||||
darklogicVar = StringVar()
|
||||
darklogicVar.set('Lamp')
|
||||
darklogicOptionMenu = OptionMenu(darklogicFrame, darklogicVar, 'Lamp', 'Lamp or easy Fire Rod torches',
|
||||
'dark traversal')
|
||||
darklogicOptionMenu.pack(side=RIGHT)
|
||||
darklogicLabel = Label(darklogicFrame, text='Dark Room Logic')
|
||||
darklogicLabel.pack(side=LEFT)
|
||||
|
||||
goalFrame = Frame(drowDownFrame)
|
||||
goalVar = StringVar()
|
||||
goalVar.set('ganon')
|
||||
|
@ -366,6 +375,7 @@ def guiMain(args=None):
|
|||
|
||||
modeFrame.pack(expand=True, anchor=E)
|
||||
logicFrame.pack(expand=True, anchor=E)
|
||||
darklogicFrame.pack(expand=True, anchor=E)
|
||||
goalFrame.pack(expand=True, anchor=E)
|
||||
crystalsGTFrame.pack(expand=True, anchor=E)
|
||||
crystalsGanonFrame.pack(expand=True, anchor=E)
|
||||
|
@ -488,6 +498,9 @@ def guiMain(args=None):
|
|||
guiargs.count = int(countVar.get()) if countVar.get() != '1' else None
|
||||
guiargs.mode = modeVar.get()
|
||||
guiargs.logic = logicVar.get()
|
||||
guiargs.dark_room_logic = {"Lamp": "lamp",
|
||||
"Lamp or easy Fire Rod torches": "torches",
|
||||
"dark traversal": "none"}[darklogicVar.get()]
|
||||
guiargs.goal = goalVar.get()
|
||||
guiargs.crystals_gt = crystalsGTVar.get()
|
||||
guiargs.crystals_ganon = crystalsGanonVar.get()
|
||||
|
|
|
@ -608,13 +608,13 @@ def get_pool_core(world, player: int):
|
|||
|
||||
if want_progressives():
|
||||
pool.extend(diff.progressivebow)
|
||||
elif swords != 'swordless':
|
||||
pool.extend(diff.basicbow)
|
||||
else:
|
||||
elif swords == 'swordless' or logic == 'noglitches':
|
||||
swordless_bows = ['Bow', 'Silver Bow']
|
||||
if difficulty == "easy":
|
||||
swordless_bows *= 2
|
||||
pool.extend(swordless_bows)
|
||||
else:
|
||||
pool.extend(diff.basicbow)
|
||||
|
||||
if swords == 'swordless':
|
||||
pool.extend(diff.swordless)
|
||||
|
|
12
Main.py
12
Main.py
|
@ -82,6 +82,8 @@ def main(args, seed=None):
|
|||
world.progression_balancing = {player: not balance for player, balance in args.skip_progression_balancing.items()}
|
||||
world.shuffle_prizes = args.shuffle_prizes.copy()
|
||||
world.sprite_pool = args.sprite_pool.copy()
|
||||
world.dark_room_logic = args.dark_room_logic.copy()
|
||||
world.restrict_dungeon_item_on_boss = args.restrict_dungeon_item_on_boss.copy()
|
||||
|
||||
world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in range(1, world.players + 1)}
|
||||
|
||||
|
@ -100,9 +102,6 @@ def main(args, seed=None):
|
|||
for player in range(1, world.players + 1):
|
||||
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
|
||||
|
||||
if world.mode[player] == 'standard' and world.enemy_shuffle[player] != 'none':
|
||||
world.escape_assist[player].append('bombs') # enemized escape assumes infinite bombs available and will likely be unbeatable without it
|
||||
|
||||
for tok in filter(None, args.startinventory[player].split(',')):
|
||||
item = ItemFactory(tok.strip(), player)
|
||||
if item:
|
||||
|
@ -151,9 +150,7 @@ def main(args, seed=None):
|
|||
shuffled_locations = None
|
||||
if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) +
|
||||
list(args.keyshuffle.values()) + list(args.bigkeyshuffle.values())):
|
||||
shuffled_locations = world.get_unfilled_locations()
|
||||
world.random.shuffle(shuffled_locations)
|
||||
fill_dungeons_restrictive(world, shuffled_locations)
|
||||
fill_dungeons_restrictive(world)
|
||||
else:
|
||||
fill_dungeons(world)
|
||||
|
||||
|
@ -373,6 +370,9 @@ def copy_world(world):
|
|||
ret.beemizer = world.beemizer.copy()
|
||||
ret.timer = world.timer.copy()
|
||||
ret.shufflepots = world.shufflepots.copy()
|
||||
ret.shuffle_prizes = world.shuffle_prizes.copy()
|
||||
ret.dark_room_logic = world.dark_room_logic.copy()
|
||||
ret.restrict_dungeon_item_on_boss = world.restrict_dungeon_item_on_boss.copy()
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
if world.mode[player] != 'inverted':
|
||||
|
|
11
Mystery.py
11
Mystery.py
|
@ -275,6 +275,17 @@ def roll_settings(weights):
|
|||
ret.logic = {None: 'noglitches', 'none': 'noglitches', 'no_logic': 'nologic', 'overworld_glitches': 'owglitches',
|
||||
'minor_glitches': 'minorglitches'}[
|
||||
glitches_required]
|
||||
|
||||
ret.dark_room_logic = get_choice("dark_room_logic", weights, "lamp")
|
||||
if not ret.dark_room_logic: # None/False
|
||||
ret.dark_room_logic = "none"
|
||||
if ret.dark_room_logic == "sconces":
|
||||
ret.dark_room_logic = "torches"
|
||||
if ret.dark_room_logic not in {"lamp", "torches", "none"}:
|
||||
raise ValueError(f"Unknown Dark Room Logic: \"{ret.dark_room_logic}\"")
|
||||
|
||||
ret.restrict_dungeon_item_on_boss = get_choice('restrict_dungeon_item_on_boss', weights, False)
|
||||
|
||||
ret.progression_balancing = get_choice('progression_balancing', weights, True)
|
||||
# item_placement = get_choice('item_placement')
|
||||
# not supported in ER
|
||||
|
|
|
@ -746,3 +746,6 @@ lookup_vanilla_location_to_entrance = {1572883: 'Kings Grave Inner Rocks', 19125
|
|||
60127: 'Ganons Tower', 60118: 'Ganons Tower', 60148: 'Ganons Tower',
|
||||
60151: 'Ganons Tower', 60145: 'Ganons Tower', 60157: 'Ganons Tower',
|
||||
60160: 'Ganons Tower', 60163: 'Ganons Tower', 60166: 'Ganons Tower'}
|
||||
|
||||
lookup_prizes = {location for location in location_table if location.endswith(" - Prize")}
|
||||
lookup_boss_drops = {location for location in location_table if location.endswith(" - Boss")}
|
4
Rom.py
4
Rom.py
|
@ -802,7 +802,7 @@ def patch_rom(world, rom, player, team, enemized):
|
|||
#Work around for json patch ordering issues - write bow limit separately so that it is replaced in the patch
|
||||
rom.write_bytes(0x180098, [difficulty.progressive_bow_limit, overflow_replacement])
|
||||
|
||||
if difficulty.progressive_bow_limit < 2 and world.swords[player] == 'swordless':
|
||||
if difficulty.progressive_bow_limit < 2 and (world.swords[player] == 'swordless' or world.logic[player] == 'noglitches'):
|
||||
rom.write_bytes(0x180098, [2, overflow_replacement])
|
||||
rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon
|
||||
rom.write_byte(0x180182, 0x00) # Don't auto equip silvers on pickup
|
||||
|
@ -1880,7 +1880,7 @@ def write_strings(rom, world, player, team):
|
|||
|
||||
prog_bow_locs = world.find_items('Progressive Bow', player)
|
||||
distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None)
|
||||
progressive_silvers = world.difficulty_requirements[player].progressive_bow_limit >= 2 or world.swords[player] == 'swordless'
|
||||
progressive_silvers = world.difficulty_requirements[player].progressive_bow_limit >= 2 or (world.swords[player] == 'swordless' or world.logic[player] == 'noglitches')
|
||||
if distinguished_prog_bow_loc:
|
||||
prog_bow_locs.remove(distinguished_prog_bow_loc)
|
||||
silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!'
|
||||
|
|
136
Rules.py
136
Rules.py
|
@ -4,6 +4,7 @@ import OverworldGlitchRules
|
|||
from BaseClasses import RegionType, World, Entrance
|
||||
from Items import ItemFactory, progression_items, item_name_groups
|
||||
from OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
|
||||
from Bosses import GanonDefeatRule
|
||||
|
||||
|
||||
def set_rules(world, player):
|
||||
|
@ -19,7 +20,8 @@ def set_rules(world, player):
|
|||
exit.hide_path = True
|
||||
return
|
||||
else:
|
||||
# Set access rules according to max glitches for multiworld progression. Set accessibility to none, and shuffle assuming the no logic players can always win
|
||||
# Set access rules according to max glitches for multiworld progression.
|
||||
# Set accessibility to none, and shuffle assuming the no logic players can always win
|
||||
world.accessibility[player] = 'none'
|
||||
world.progression_balancing[player] = False
|
||||
|
||||
|
@ -122,8 +124,18 @@ def add_rule(spot, rule, combine='and'):
|
|||
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
||||
|
||||
|
||||
def add_lamp_requirement(spot, player):
|
||||
add_rule(spot, lambda state: state.has('Lamp', player))
|
||||
def add_lamp_requirement(world: World, spot, player: int, has_accessible_torch: bool = False):
|
||||
if world.dark_room_logic[player] == "lamp":
|
||||
add_rule(spot, lambda state: state.has('Lamp', player))
|
||||
elif world.dark_room_logic[player] == "torch": # implicitly lamp as well
|
||||
if has_accessible_torch:
|
||||
add_rule(spot, lambda state: state.has('Lamp', player) or state.has('Fire Rod', player))
|
||||
else:
|
||||
add_rule(spot, lambda state: state.has('Lamp', player))
|
||||
elif world.dark_room_logic[player] == "none":
|
||||
pass
|
||||
else:
|
||||
raise ValueError(f"Unknown Dark Room Logic: {world.dark_room_logic[player]}")
|
||||
|
||||
|
||||
def forbid_item(location, item, player: int):
|
||||
|
@ -131,10 +143,15 @@ def forbid_item(location, item, player: int):
|
|||
location.item_rule = lambda i: (i.name != item or i.player != player) and old_rule(i)
|
||||
|
||||
|
||||
def forbid_items(location, items: set, player: int):
|
||||
def forbid_items_for_player(location, items: set, player: int):
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda i: (i.player != player or i.name not in items) and old_rule(i)
|
||||
|
||||
def forbid_items(location, items: set):
|
||||
"""unused, but kept as a debugging tool."""
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda i: i.name not in items and old_rule(i)
|
||||
|
||||
|
||||
def add_item_rule(location, rule):
|
||||
old_rule = location.item_rule
|
||||
|
@ -161,7 +178,7 @@ def locality_rules(world, player):
|
|||
if world.local_items[player]:
|
||||
for location in world.get_locations():
|
||||
if location.player != player:
|
||||
forbid_items(location, world.local_items[player], player)
|
||||
forbid_items_for_player(location, world.local_items[player], player)
|
||||
|
||||
|
||||
non_crossover_items = (item_name_groups["Small Keys"] | item_name_groups["Big Keys"] | progression_items) - {
|
||||
|
@ -182,7 +199,7 @@ def global_rules(world, player):
|
|||
set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player))
|
||||
set_rule(world.get_location('Purple Chest', player),
|
||||
lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest
|
||||
set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player))
|
||||
set_rule(world.get_location('Ether Tablet', player), lambda state: state.can_retrieve_tablet(player))
|
||||
set_rule(world.get_location('Master Sword Pedestal', player), lambda state: state.has('Red Pendant', player) and state.has('Blue Pendant', player) and state.has('Green Pendant', player))
|
||||
|
||||
set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith
|
||||
|
@ -225,13 +242,16 @@ def global_rules(world, player):
|
|||
set_rule(world.get_location('Eastern Palace - Big Chest', player),
|
||||
lambda state: state.has('Big Key (Eastern Palace)', player))
|
||||
set_rule(world.get_location('Eastern Palace - Boss', player),
|
||||
lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)',
|
||||
player) and state.world.get_location(
|
||||
lambda state: state.has('Big Key (Eastern Palace)', player) and state.world.get_location(
|
||||
'Eastern Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
set_rule(world.get_location('Eastern Palace - Prize', player),
|
||||
lambda state: state.can_shoot_arrows(player) and state.has('Big Key (Eastern Palace)',
|
||||
player) and state.world.get_location(
|
||||
lambda state: state.has('Big Key (Eastern Palace)', player) and state.world.get_location(
|
||||
'Eastern Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state))
|
||||
if not world.enemy_shuffle[player]:
|
||||
add_rule(world.get_location('Eastern Palace - Boss', player),
|
||||
lambda state: state.can_shoot_arrows(player))
|
||||
add_rule(world.get_location('Eastern Palace - Prize', player),
|
||||
lambda state: state.can_shoot_arrows(player))
|
||||
|
||||
set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player))
|
||||
set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has_Boots(player))
|
||||
|
@ -328,7 +348,8 @@ def global_rules(world, 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))
|
||||
|
||||
set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: state.can_shoot_arrows(player))
|
||||
if not world.enemy_shuffle[player]:
|
||||
set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: state.can_shoot_arrows(player))
|
||||
set_rule(world.get_entrance('Palace of Darkness Hammer Peg Drop', player), lambda state: state.has('Hammer', player))
|
||||
set_rule(world.get_entrance('Palace of Darkness Bridge Room', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 1)) # If we can reach any other small key door, we already have back door access to this area
|
||||
set_rule(world.get_entrance('Palace of Darkness Big Key Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6) and state.has('Big Key (Palace of Darkness)', player) and state.can_shoot_arrows(player) and state.has('Hammer', player))
|
||||
|
@ -377,23 +398,33 @@ def global_rules(world, player):
|
|||
|
||||
set_rule(world.get_location('Ganons Tower - Big Chest', player), lambda state: state.has('Big Key (Ganons Tower)', player))
|
||||
|
||||
set_rule(world.get_location('Ganons Tower - Big Key Room - Left', player), lambda state: state.world.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
|
||||
set_rule(world.get_location('Ganons Tower - Big Key Chest', player), lambda state: state.world.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
|
||||
set_rule(world.get_location('Ganons Tower - Big Key Room - Right', player), lambda state: state.world.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
|
||||
|
||||
set_rule(world.get_entrance('Ganons Tower Big Key Door', player), lambda state: state.has('Big Key (Ganons Tower)', player) and state.can_shoot_arrows(player))
|
||||
set_rule(world.get_entrance('Ganons Tower Torch Rooms', player), lambda state: state.has_fire_source(player) and state.world.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state))
|
||||
set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3))
|
||||
set_rule(world.get_entrance('Ganons Tower Moldorm Door', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4))
|
||||
set_rule(world.get_entrance('Ganons Tower Moldorm Gap', player), lambda state: state.has('Hookshot', player) and state.world.get_entrance('Ganons Tower Moldorm Gap', player).parent_region.dungeon.bosses['top'].can_defeat(state))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
|
||||
|
||||
if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
|
||||
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_triforce_pieces(world.treasure_hunt_count[player], player)
|
||||
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Bow', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
||||
set_rule(world.get_location('Ganons Tower - Big Key Room - Left', player),
|
||||
lambda state: state.world.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
|
||||
set_rule(world.get_location('Ganons Tower - Big Key Chest', player),
|
||||
lambda state: state.world.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
|
||||
set_rule(world.get_location('Ganons Tower - Big Key Room - Right', player),
|
||||
lambda state: state.world.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
|
||||
if world.enemy_shuffle[player]:
|
||||
set_rule(world.get_entrance('Ganons Tower Big Key Door', player),
|
||||
lambda state: state.has('Big Key (Ganons Tower)', player))
|
||||
else:
|
||||
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)
|
||||
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Bow', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
||||
set_rule(world.get_entrance('Ganons Tower Big Key Door', player),
|
||||
lambda state: state.has('Big Key (Ganons Tower)', player) and state.can_shoot_arrows(player))
|
||||
set_rule(world.get_entrance('Ganons Tower Torch Rooms', player),
|
||||
lambda state: state.has_fire_source(player) and state.world.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state))
|
||||
set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player),
|
||||
lambda state: state.has_key('Small Key (Ganons Tower)', player, 3))
|
||||
set_rule(world.get_entrance('Ganons Tower Moldorm Door', player),
|
||||
lambda state: state.has_key('Small Key (Ganons Tower)', player, 4))
|
||||
set_rule(world.get_entrance('Ganons Tower Moldorm Gap', player),
|
||||
lambda state: state.has('Hookshot', player) and state.world.get_entrance('Ganons Tower Moldorm Gap', player).parent_region.dungeon.bosses['top'].can_defeat(state))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
|
||||
ganon = world.get_location('Ganon', player)
|
||||
set_rule(ganon, lambda state: GanonDefeatRule(state, player))
|
||||
if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
|
||||
add_rule(ganon, lambda state: state.has_triforce_pieces(world.treasure_hunt_count[player], player))
|
||||
else:
|
||||
add_rule(ganon, lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
||||
|
||||
|
||||
|
@ -452,7 +483,7 @@ def default_rules(world, player):
|
|||
set_rule(world.get_entrance('Hyrule Castle Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Hyrule Castle Main Gate', player), lambda state: state.has_Mirror(player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: (state.has_Pearl(player) and state.has('Flippers', player) or state.has_Mirror(player))) # Overworld Bunny Revival
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player))
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.can_retrieve_tablet(player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # ToDo any fake flipper set up?
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: state.has_Pearl(player)) # bomb required
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player))
|
||||
|
@ -586,7 +617,7 @@ def inverted_rules(world, player):
|
|||
set_rule(world.get_entrance('Bonk Fairy (Dark)', player), lambda state: state.has_Boots(player))
|
||||
set_rule(world.get_entrance('West Dark World Gap', player), lambda state: state.has('Hookshot', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player))
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player))
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.can_retrieve_tablet(player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has('Flippers', player)) # ToDo any fake flipper set up?
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Ledge Pier', player), lambda state: state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player))
|
||||
|
@ -740,41 +771,44 @@ def add_conditional_lamps(world, player):
|
|||
# Light cones in standard depend on which world we actually are in, not which one the location would normally be
|
||||
# We add Lamp requirements only to those locations which lie in the dark world (or everything if open
|
||||
|
||||
def add_conditional_lamp(spot, region, spottype='Location'):
|
||||
if spottype == 'Location':
|
||||
spot = world.get_location(spot, player)
|
||||
else:
|
||||
spot = world.get_entrance(spot, player)
|
||||
if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))):
|
||||
add_lamp_requirement(spot, player)
|
||||
def add_conditional_lamp(spot, region, spottype='Location', accessible_torch=False):
|
||||
if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (
|
||||
not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))):
|
||||
if spottype == 'Location':
|
||||
spot = world.get_location(spot, player)
|
||||
else:
|
||||
spot = world.get_entrance(spot, player)
|
||||
add_lamp_requirement(world, spot, player, accessible_torch)
|
||||
|
||||
add_conditional_lamp('Misery Mire (Vitreous)', 'Misery Mire (Entrance)', 'Entrance')
|
||||
add_conditional_lamp('Turtle Rock (Dark Room) (North)', 'Turtle Rock (Entrance)', 'Entrance')
|
||||
add_conditional_lamp('Turtle Rock (Dark Room) (South)', 'Turtle Rock (Entrance)', 'Entrance')
|
||||
add_conditional_lamp('Palace of Darkness Big Key Door', 'Palace of Darkness (Entrance)', 'Entrance')
|
||||
add_conditional_lamp('Palace of Darkness Maze Door', 'Palace of Darkness (Entrance)', 'Entrance')
|
||||
add_conditional_lamp('Palace of Darkness - Dark Basement - Left', 'Palace of Darkness (Entrance)', 'Location')
|
||||
add_conditional_lamp('Palace of Darkness - Dark Basement - Right', 'Palace of Darkness (Entrance)', 'Location')
|
||||
add_conditional_lamp('Palace of Darkness - Dark Basement - Left', 'Palace of Darkness (Entrance)',
|
||||
'Location', True)
|
||||
add_conditional_lamp('Palace of Darkness - Dark Basement - Right', 'Palace of Darkness (Entrance)',
|
||||
'Location', True)
|
||||
if world.mode[player] != 'inverted':
|
||||
add_conditional_lamp('Agahnim 1', 'Agahnims Tower', 'Entrance')
|
||||
add_conditional_lamp('Castle Tower - Dark Maze', 'Agahnims Tower', 'Location')
|
||||
add_conditional_lamp('Castle Tower - Dark Maze', 'Agahnims Tower')
|
||||
else:
|
||||
add_conditional_lamp('Agahnim 1', 'Inverted Agahnims Tower', 'Entrance')
|
||||
add_conditional_lamp('Castle Tower - Dark Maze', 'Inverted Agahnims Tower', 'Location')
|
||||
add_conditional_lamp('Old Man', 'Old Man Cave', 'Location')
|
||||
add_conditional_lamp('Castle Tower - Dark Maze', 'Inverted Agahnims Tower')
|
||||
add_conditional_lamp('Old Man', 'Old Man Cave')
|
||||
add_conditional_lamp('Old Man Cave Exit (East)', 'Old Man Cave', 'Entrance')
|
||||
add_conditional_lamp('Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave', 'Entrance')
|
||||
add_conditional_lamp('Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave', 'Entrance')
|
||||
add_conditional_lamp('Old Man House Front to Back', 'Old Man House', 'Entrance')
|
||||
add_conditional_lamp('Old Man House Back to Front', 'Old Man House', 'Entrance')
|
||||
add_conditional_lamp('Eastern Palace - Big Key Chest', 'Eastern Palace', 'Location')
|
||||
add_conditional_lamp('Eastern Palace - Boss', 'Eastern Palace', 'Location')
|
||||
add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location')
|
||||
add_conditional_lamp('Eastern Palace - Big Key Chest', 'Eastern Palace')
|
||||
add_conditional_lamp('Eastern Palace - Boss', 'Eastern Palace', 'Location', True)
|
||||
add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location', True)
|
||||
|
||||
if not world.sewer_light_cone[player]:
|
||||
add_lamp_requirement(world.get_location('Sewers - Dark Cross', player), player)
|
||||
add_lamp_requirement(world.get_entrance('Sewers Back Door', player), player)
|
||||
add_lamp_requirement(world.get_entrance('Throne Room', player), player)
|
||||
add_lamp_requirement(world, world.get_location('Sewers - Dark Cross', player), player)
|
||||
add_lamp_requirement(world, world.get_entrance('Sewers Back Door', player), player)
|
||||
add_lamp_requirement(world, world.get_entrance('Throne Room', player), player)
|
||||
|
||||
|
||||
def open_rules(world, player):
|
||||
|
@ -786,27 +820,19 @@ def open_rules(world, player):
|
|||
|
||||
|
||||
def swordless_rules(world, player):
|
||||
|
||||
set_rule(world.get_entrance('Agahnim 1', player), lambda state: (state.has('Hammer', player) or state.has('Fire Rod', player) or state.can_shoot_arrows(player) or state.has('Cane of Somaria', player)) and state.has_key('Small Key (Agahnims Tower)', player, 2))
|
||||
set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
|
||||
set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain
|
||||
set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) #in swordless mode bombos pads are present in the relevant parts of ice palace
|
||||
if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
|
||||
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Bow', player) and state.can_shoot_arrows(player) and state.has_triforce_pieces(world.treasure_hunt_count[player], player))
|
||||
else:
|
||||
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Bow', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player))
|
||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop
|
||||
|
||||
if world.mode[player] != 'inverted':
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
||||
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!)
|
||||
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player) and state.has_Mirror(player))
|
||||
else:
|
||||
# only need ddm access for aga tower in inverted
|
||||
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!)
|
||||
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
|
||||
|
||||
|
||||
def add_connection(parent_name, target_name, entrance_name, world, player):
|
||||
|
|
|
@ -19,12 +19,21 @@
|
|||
|
||||
description: Template Name # Used to describe your yaml. Useful if you have multiple files
|
||||
name: YourName # Your name in-game. Spaces will be replaced with underscores and there is a 16 character limit
|
||||
### Logic Section ###
|
||||
glitches_required: # Determine the logic required to complete the seed
|
||||
none: 50 # No glitches required
|
||||
minor_glitches: 0 # Puts fake flipper, waterwalk, super bunny shenanigans, and etc into logic
|
||||
overworld_glitches: 0 # Assumes the player has knowledge of both overworld major glitches (boots clips, mirror clips) and minor glitches (fake flipper, super bunny shenanigans, water walk and etc.)
|
||||
no_logic: 0 # Your own items are placed with no regard to any logic; such as your Fire Rod can be on your Trinexx.
|
||||
# Other players items are placed into your world under OWG logic
|
||||
dark_room_logic: # Logic for unlit dark rooms
|
||||
lamp: 50 # require the Lamp for these rooms to be considered accessible.
|
||||
sconces: 0 # in addition to lamp, allow the fire rod and presence of easily accessible sconces for access
|
||||
none: 0 # all dark rooms are always considered doable, meaning this may force completion of rooms in complete darkness
|
||||
restrict_dungeon_item_on_boss: # aka ambrosia boss items
|
||||
on: 0 # prevents unshuffled compasses, maps and keys to be boss drops, they can still drop keysanity and other players' items
|
||||
off: 50
|
||||
### End of Logic Section ###
|
||||
meta_ignore: # Nullify options specified in the meta.yaml file. Adding an option here guarantees it will not occur in your seed, even if the .yaml file specifies it
|
||||
mode:
|
||||
- inverted # Never play inverted seeds
|
||||
|
@ -305,8 +314,8 @@ rom:
|
|||
on: 0
|
||||
off: 50
|
||||
quickswap: # Enable switching items by pressing the L+R shoulder buttons
|
||||
on: 0
|
||||
off: 50
|
||||
on: 50
|
||||
off: 0
|
||||
menuspeed: # Controls how fast the item menu opens and closes
|
||||
normal: 50
|
||||
instant: 0
|
||||
|
|
Loading…
Reference in New Issue