Merge remote-tracking branch 'Main/main' into rework_accessibility

# Conflicts:
#	worlds/alttp/Options.py
#	worlds/alttp/Rules.py
This commit is contained in:
alwaysintreble
2024-02-20 13:24:00 -06:00
98 changed files with 2679 additions and 2491 deletions

View File

@@ -18,11 +18,14 @@ import NetUtils
import Options
import Utils
if typing.TYPE_CHECKING:
from worlds import AutoWorld
class Group(TypedDict, total=False):
name: str
game: str
world: auto_world
world: "AutoWorld.World"
players: Set[int]
item_pool: Set[str]
replacement_items: Dict[int, Optional[str]]
@@ -55,7 +58,7 @@ class MultiWorld():
plando_texts: List[Dict[str, str]]
plando_items: List[List[Dict[str, Any]]]
plando_connections: List
worlds: Dict[int, auto_world]
worlds: Dict[int, "AutoWorld.World"]
groups: Dict[int, Group]
regions: RegionManager
itempool: List[Item]
@@ -156,11 +159,11 @@ class MultiWorld():
self.fix_trock_doors = self.AttributeProxy(
lambda player: self.shuffle[player] != 'vanilla' or self.mode[player] == 'inverted')
self.fix_skullwoods_exit = self.AttributeProxy(
lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'])
lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeons_simple'])
self.fix_palaceofdarkness_exit = self.AttributeProxy(
lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'])
lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeons_simple'])
self.fix_trock_exit = self.AttributeProxy(
lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'])
lambda player: self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeons_simple'])
for player in range(1, players + 1):
def set_player_attr(attr, val):
@@ -219,6 +222,8 @@ class MultiWorld():
def add_group(self, name: str, game: str, players: Set[int] = frozenset()) -> Tuple[int, Group]:
"""Create a group with name and return the assigned player ID and group.
If a group of this name already exists, the set of players is extended instead of creating a new one."""
from worlds import AutoWorld
for group_id, group in self.groups.items():
if group["name"] == name:
group["players"] |= players
@@ -253,6 +258,8 @@ class MultiWorld():
def set_options(self, args: Namespace) -> None:
# TODO - remove this section once all worlds use options dataclasses
from worlds import AutoWorld
all_keys: Set[str] = {key for player in self.player_ids for key in
AutoWorld.AutoWorldRegister.world_types[self.game[player]].options_dataclass.type_hints}
for option_key in all_keys:
@@ -270,6 +277,8 @@ class MultiWorld():
for option_key in options_dataclass.type_hints})
def set_item_links(self):
from worlds import AutoWorld
item_links = {}
replacement_prio = [False, True, None]
for player in self.player_ids:
@@ -1328,6 +1337,8 @@ class Spoiler:
get_path(state, multiworld.get_region('Inverted Big Bomb Shop', player))
def to_file(self, filename: str) -> None:
from worlds import AutoWorld
def write_option(option_key: str, option_obj: Options.AssembleOptions) -> None:
res = getattr(self.multiworld.worlds[player].options, option_key)
display_name = getattr(option_obj, "display_name", option_key)
@@ -1451,8 +1462,3 @@ def get_seed(seed: Optional[int] = None) -> int:
random.seed(None)
return random.randint(0, pow(10, seeddigits) - 1)
return seed
from worlds import AutoWorld
auto_world = AutoWorld.World

View File

@@ -941,4 +941,5 @@ def run_as_textclient():
if __name__ == '__main__':
logging.getLogger().setLevel(logging.INFO) # force log-level to work around log level resetting to WARNING
run_as_textclient()

View File

@@ -315,20 +315,6 @@ def prefer_int(input_data: str) -> Union[str, int]:
return input_data
goals = {
'ganon': 'ganon',
'crystals': 'crystals',
'bosses': 'bosses',
'pedestal': 'pedestal',
'ganon_pedestal': 'ganonpedestal',
'triforce_hunt': 'triforcehunt',
'local_triforce_hunt': 'localtriforcehunt',
'ganon_triforce_hunt': 'ganontriforcehunt',
'local_ganon_triforce_hunt': 'localganontriforcehunt',
'ice_rod_hunt': 'icerodhunt',
}
def roll_percentage(percentage: Union[int, float]) -> bool:
"""Roll a percentage chance.
percentage is expected to be in range [0, 100]"""
@@ -357,15 +343,6 @@ def roll_meta_option(option_key, game: str, category_dict: Dict) -> Any:
if options[option_key].supports_weighting:
return get_choice(option_key, category_dict)
return category_dict[option_key]
if game == "A Link to the Past": # TODO wow i hate this
if option_key in {"glitches_required", "dark_room_logic", "entrance_shuffle", "goals", "triforce_pieces_mode",
"triforce_pieces_percentage", "triforce_pieces_available", "triforce_pieces_extra",
"triforce_pieces_required", "shop_shuffle", "mode", "item_pool", "item_functionality",
"boss_shuffle", "enemy_damage", "enemy_health", "timer", "countdown_start_time",
"red_clock_time", "blue_clock_time", "green_clock_time", "dungeon_counters", "shuffle_prizes",
"misery_mire_medallion", "turtle_rock_medallion", "sprite_pool", "sprite",
"random_sprite_on_event"}:
return get_choice(option_key, category_dict)
raise Exception(f"Error generating meta option {option_key} for {game}.")
@@ -504,101 +481,6 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
if "dungeon_items" in weights and get_choice_legacy('dungeon_items', weights, "none") != "none":
raise Exception(f"dungeon_items key in A Link to the Past was removed, but is present in these weights as {get_choice_legacy('dungeon_items', weights, False)}.")
glitches_required = get_choice_legacy('glitches_required', weights)
if glitches_required not in [None, 'none', 'no_logic', 'overworld_glitches', 'hybrid_major_glitches', 'minor_glitches']:
logging.warning("Only NMG, OWG, HMG and No Logic supported")
glitches_required = 'none'
ret.logic = {None: 'noglitches', 'none': 'noglitches', 'no_logic': 'nologic', 'overworld_glitches': 'owglitches',
'minor_glitches': 'minorglitches', 'hybrid_major_glitches': 'hybridglitches'}[
glitches_required]
ret.dark_room_logic = get_choice_legacy("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}\"")
entrance_shuffle = get_choice_legacy('entrance_shuffle', weights, 'vanilla')
if entrance_shuffle.startswith('none-'):
ret.shuffle = 'vanilla'
else:
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
goal = get_choice_legacy('goals', weights, 'ganon')
ret.goal = goals[goal]
extra_pieces = get_choice_legacy('triforce_pieces_mode', weights, 'available')
ret.triforce_pieces_required = LttPOptions.TriforcePieces.from_any(get_choice_legacy('triforce_pieces_required', weights, 20))
# sum a percentage to required
if extra_pieces == 'percentage':
percentage = max(100, float(get_choice_legacy('triforce_pieces_percentage', weights, 150))) / 100
ret.triforce_pieces_available = int(round(ret.triforce_pieces_required * percentage, 0))
# vanilla mode (specify how many pieces are)
elif extra_pieces == 'available':
ret.triforce_pieces_available = LttPOptions.TriforcePieces.from_any(
get_choice_legacy('triforce_pieces_available', weights, 30))
# required pieces + fixed extra
elif extra_pieces == 'extra':
extra_pieces = max(0, int(get_choice_legacy('triforce_pieces_extra', weights, 10)))
ret.triforce_pieces_available = ret.triforce_pieces_required + extra_pieces
# change minimum to required pieces to avoid problems
ret.triforce_pieces_available = min(max(ret.triforce_pieces_required, int(ret.triforce_pieces_available)), 90)
ret.shop_shuffle = get_choice_legacy('shop_shuffle', weights, '')
if not ret.shop_shuffle:
ret.shop_shuffle = ''
ret.mode = get_choice_legacy("mode", weights)
ret.difficulty = get_choice_legacy('item_pool', weights)
ret.item_functionality = get_choice_legacy('item_functionality', weights)
ret.enemy_damage = {None: 'default',
'default': 'default',
'shuffled': 'shuffled',
'random': 'chaos', # to be removed
'chaos': 'chaos',
}[get_choice_legacy('enemy_damage', weights)]
ret.enemy_health = get_choice_legacy('enemy_health', weights)
ret.timer = {'none': False,
None: False,
False: False,
'timed': 'timed',
'timed_ohko': 'timed-ohko',
'ohko': 'ohko',
'timed_countdown': 'timed-countdown',
'display': 'display'}[get_choice_legacy('timer', weights, False)]
ret.countdown_start_time = int(get_choice_legacy('countdown_start_time', weights, 10))
ret.red_clock_time = int(get_choice_legacy('red_clock_time', weights, -2))
ret.blue_clock_time = int(get_choice_legacy('blue_clock_time', weights, 2))
ret.green_clock_time = int(get_choice_legacy('green_clock_time', weights, 4))
ret.dungeon_counters = get_choice_legacy('dungeon_counters', weights, 'default')
ret.shuffle_prizes = get_choice_legacy('shuffle_prizes', weights, "g")
ret.required_medallions = [get_choice_legacy("misery_mire_medallion", weights, "random"),
get_choice_legacy("turtle_rock_medallion", weights, "random")]
for index, medallion in enumerate(ret.required_medallions):
ret.required_medallions[index] = {"ether": "Ether", "quake": "Quake", "bombos": "Bombos", "random": "random"} \
.get(medallion.lower(), None)
if not ret.required_medallions[index]:
raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}")
ret.plando_texts = {}
if PlandoOptions.texts in plando_options:

View File

@@ -8,7 +8,7 @@ if __name__ == "__main__":
import ModuleUpdate
ModuleUpdate.update()
from worlds.Files import AutoPatchRegister, APDeltaPatch
from worlds.Files import AutoPatchRegister, APPatch
class RomMeta(TypedDict):
@@ -20,7 +20,7 @@ class RomMeta(TypedDict):
def create_rom_file(patch_file: str) -> Tuple[RomMeta, str]:
auto_handler = AutoPatchRegister.get_handler(patch_file)
if auto_handler:
handler: APDeltaPatch = auto_handler(patch_file)
handler: APPatch = auto_handler(patch_file)
target = os.path.splitext(patch_file)[0]+handler.result_file_ending
handler.patch(target)
return {"server": handler.server,

Binary file not shown.

View File

@@ -22,6 +22,10 @@ SOFTWARE.
local SCRIPT_VERSION = 1
-- Set to log incoming requests
-- Will cause lag due to large console output
local DEBUG = false
--[[
This script expects to receive JSON and will send JSON back. A message should
be a list of 1 or more requests which will be executed in order. Each request
@@ -271,10 +275,6 @@ local base64 = require("base64")
local socket = require("socket")
local json = require("json")
-- Set to log incoming requests
-- Will cause lag due to large console output
local DEBUG = false
local SOCKET_PORT_FIRST = 43055
local SOCKET_PORT_RANGE_SIZE = 5
local SOCKET_PORT_LAST = SOCKET_PORT_FIRST + SOCKET_PORT_RANGE_SIZE
@@ -330,18 +330,28 @@ function unlock ()
client_socket:settimeout(0)
end
function process_request (req)
local res = {}
request_handlers = {
["PING"] = function (req)
local res = {}
if req["type"] == "PING" then
res["type"] = "PONG"
elseif req["type"] == "SYSTEM" then
return res
end,
["SYSTEM"] = function (req)
local res = {}
res["type"] = "SYSTEM_RESPONSE"
res["value"] = emu.getsystemid()
elseif req["type"] == "PREFERRED_CORES" then
return res
end,
["PREFERRED_CORES"] = function (req)
local res = {}
local preferred_cores = client.getconfig().PreferredCores
res["type"] = "PREFERRED_CORES_RESPONSE"
res["value"] = {}
res["value"]["NES"] = preferred_cores.NES
@@ -354,14 +364,21 @@ function process_request (req)
res["value"]["PCECD"] = preferred_cores.PCECD
res["value"]["SGX"] = preferred_cores.SGX
elseif req["type"] == "HASH" then
return res
end,
["HASH"] = function (req)
local res = {}
res["type"] = "HASH_RESPONSE"
res["value"] = rom_hash
elseif req["type"] == "GUARD" then
res["type"] = "GUARD_RESPONSE"
local expected_data = base64.decode(req["expected_data"])
return res
end,
["GUARD"] = function (req)
local res = {}
local expected_data = base64.decode(req["expected_data"])
local actual_data = memory.read_bytes_as_array(req["address"], #expected_data, req["domain"])
local data_is_validated = true
@@ -372,39 +389,83 @@ function process_request (req)
end
end
res["type"] = "GUARD_RESPONSE"
res["value"] = data_is_validated
res["address"] = req["address"]
elseif req["type"] == "LOCK" then
return res
end,
["LOCK"] = function (req)
local res = {}
res["type"] = "LOCKED"
lock()
elseif req["type"] == "UNLOCK" then
return res
end,
["UNLOCK"] = function (req)
local res = {}
res["type"] = "UNLOCKED"
unlock()
elseif req["type"] == "READ" then
return res
end,
["READ"] = function (req)
local res = {}
res["type"] = "READ_RESPONSE"
res["value"] = base64.encode(memory.read_bytes_as_array(req["address"], req["size"], req["domain"]))
elseif req["type"] == "WRITE" then
return res
end,
["WRITE"] = function (req)
local res = {}
res["type"] = "WRITE_RESPONSE"
memory.write_bytes_as_array(req["address"], base64.decode(req["value"]), req["domain"])
elseif req["type"] == "DISPLAY_MESSAGE" then
return res
end,
["DISPLAY_MESSAGE"] = function (req)
local res = {}
res["type"] = "DISPLAY_MESSAGE_RESPONSE"
message_queue:push(req["message"])
elseif req["type"] == "SET_MESSAGE_INTERVAL" then
return res
end,
["SET_MESSAGE_INTERVAL"] = function (req)
local res = {}
res["type"] = "SET_MESSAGE_INTERVAL_RESPONSE"
message_interval = req["value"]
else
return res
end,
["default"] = function (req)
local res = {}
res["type"] = "ERROR"
res["err"] = "Unknown command: "..req["type"]
end
return res
return res
end,
}
function process_request (req)
if request_handlers[req["type"]] then
return request_handlers[req["type"]](req)
else
return request_handlers["default"](req)
end
end
-- Receive data from AP client and send message back

View File

@@ -1,13 +1,13 @@
colorama>=0.4.5
colorama>=0.4.6
websockets>=12.0
PyYAML>=6.0.1
jellyfish>=1.0.3
jinja2>=3.1.2
jinja2>=3.1.3
schema>=0.7.5
kivy>=2.3.0
bsdiff4>=1.2.4
platformdirs>=4.0.0
platformdirs>=4.1.0
certifi>=2023.11.17
cython>=3.0.6
cython>=3.0.8
cymem>=2.0.8
orjson>=3.9.10
orjson>=3.9.10

View File

@@ -396,8 +396,6 @@ class BuildExeCommand(cx_Freeze.command.build_exe.BuildEXE):
folders_to_remove.append(file_name)
shutil.rmtree(world_directory)
shutil.copyfile("meta.yaml", self.buildfolder / "Players" / "Templates" / "meta.yaml")
# TODO: fix LttP options one day
shutil.copyfile("playerSettings.yaml", self.buildfolder / "Players" / "Templates" / "A Link to the Past.yaml")
try:
from maseya import z3pr
except ImportError:

View File

@@ -7,7 +7,7 @@ from argparse import Namespace
from Generate import get_seed_name
from test.general import gen_steps
from worlds import AutoWorld
from worlds.AutoWorld import call_all
from worlds.AutoWorld import World, call_all
from BaseClasses import Location, MultiWorld, CollectionState, ItemClassification, Item
from worlds.alttp.Items import ItemFactory
@@ -105,9 +105,15 @@ class TestBase(unittest.TestCase):
class WorldTestBase(unittest.TestCase):
options: typing.Dict[str, typing.Any] = {}
"""Define options that should be used when setting up this TestBase."""
multiworld: MultiWorld
"""The constructed MultiWorld instance after setup."""
world: World
"""The constructed World instance after setup."""
player: typing.ClassVar[int] = 1
game: typing.ClassVar[str] # define game name in subclass, example "Secret of Evermore"
game: typing.ClassVar[str]
"""Define game name in subclass, example "Secret of Evermore"."""
auto_construct: typing.ClassVar[bool] = True
""" automatically set up a world for each test in this class """
memory_leak_tested: typing.ClassVar[bool] = False
@@ -150,8 +156,8 @@ class WorldTestBase(unittest.TestCase):
if not hasattr(self, "game"):
raise NotImplementedError("didn't define game name")
self.multiworld = MultiWorld(1)
self.multiworld.game[1] = self.game
self.multiworld.player_name = {1: "Tester"}
self.multiworld.game[self.player] = self.game
self.multiworld.player_name = {self.player: "Tester"}
self.multiworld.set_seed(seed)
self.multiworld.state = CollectionState(self.multiworld)
random.seed(self.multiworld.seed)
@@ -159,9 +165,10 @@ class WorldTestBase(unittest.TestCase):
args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].options_dataclass.type_hints.items():
setattr(args, name, {
1: option.from_any(self.options.get(name, getattr(option, "default")))
1: option.from_any(self.options.get(name, option.default))
})
self.multiworld.set_options(args)
self.world = self.multiworld.worlds[self.player]
for step in gen_steps:
call_all(self.multiworld, step)
@@ -220,19 +227,19 @@ class WorldTestBase(unittest.TestCase):
def can_reach_location(self, location: str) -> bool:
"""Determines if the current state can reach the provided location name"""
return self.multiworld.state.can_reach(location, "Location", 1)
return self.multiworld.state.can_reach(location, "Location", self.player)
def can_reach_entrance(self, entrance: str) -> bool:
"""Determines if the current state can reach the provided entrance name"""
return self.multiworld.state.can_reach(entrance, "Entrance", 1)
return self.multiworld.state.can_reach(entrance, "Entrance", self.player)
def can_reach_region(self, region: str) -> bool:
"""Determines if the current state can reach the provided region name"""
return self.multiworld.state.can_reach(region, "Region", 1)
return self.multiworld.state.can_reach(region, "Region", self.player)
def count(self, item_name: str) -> int:
"""Returns the amount of an item currently in state"""
return self.multiworld.state.count(item_name, 1)
return self.multiworld.state.count(item_name, self.player)
def assertAccessDependency(self,
locations: typing.List[str],
@@ -246,10 +253,11 @@ class WorldTestBase(unittest.TestCase):
self.collect_all_but(all_items, state)
if only_check_listed:
for location in locations:
self.assertFalse(state.can_reach(location, "Location", 1), f"{location} is reachable without {all_items}")
self.assertFalse(state.can_reach(location, "Location", self.player),
f"{location} is reachable without {all_items}")
else:
for location in self.multiworld.get_locations():
loc_reachable = state.can_reach(location, "Location", 1)
loc_reachable = state.can_reach(location, "Location", self.player)
self.assertEqual(loc_reachable, location.name not in locations,
f"{location.name} is reachable without {all_items}" if loc_reachable
else f"{location.name} is not reachable without {all_items}")
@@ -258,7 +266,7 @@ class WorldTestBase(unittest.TestCase):
for item in items:
state.collect(item)
for location in locations:
self.assertTrue(state.can_reach(location, "Location", 1),
self.assertTrue(state.can_reach(location, "Location", self.player),
f"{location} not reachable with {item_names}")
for item in items:
state.remove(item)
@@ -285,7 +293,7 @@ class WorldTestBase(unittest.TestCase):
if not (self.run_default_tests and self.constructed):
return
with self.subTest("Game", game=self.game):
excluded = self.multiworld.worlds[1].options.exclude_locations.value
excluded = self.multiworld.worlds[self.player].options.exclude_locations.value
state = self.multiworld.get_all_state(False)
for location in self.multiworld.get_locations():
if location.name not in excluded:
@@ -302,7 +310,7 @@ class WorldTestBase(unittest.TestCase):
return
with self.subTest("Game", game=self.game):
state = CollectionState(self.multiworld)
locations = self.multiworld.get_reachable_locations(state, 1)
locations = self.multiworld.get_reachable_locations(state, self.player)
self.assertGreater(len(locations), 0,
"Need to be able to reach at least one location to get started.")
@@ -328,7 +336,7 @@ class WorldTestBase(unittest.TestCase):
for location in sphere:
if location.item:
state.collect(location.item, True, location)
return self.multiworld.has_beaten_game(state, 1)
return self.multiworld.has_beaten_game(state, self.player)
with self.subTest("Game", game=self.game, seed=self.multiworld.seed):
distribute_items_restrictive(self.multiworld)

View File

@@ -438,7 +438,7 @@ class World(metaclass=AutoWorldRegister):
def get_pre_fill_items(self) -> List["Item"]:
return []
# following methods should not need to be overridden.
# these two methods can be extended for pseudo-items on state
def collect(self, state: "CollectionState", item: "Item") -> bool:
name = self.collect_item(state, item)
if name:

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import abc
import json
import zipfile
import os
@@ -15,7 +16,7 @@ del threading
del os
class AutoPatchRegister(type):
class AutoPatchRegister(abc.ABCMeta):
patch_types: ClassVar[Dict[str, AutoPatchRegister]] = {}
file_endings: ClassVar[Dict[str, AutoPatchRegister]] = {}
@@ -112,14 +113,25 @@ class APContainer:
}
class APDeltaPatch(APContainer, metaclass=AutoPatchRegister):
"""An APContainer that additionally has delta.bsdiff4
class APPatch(APContainer, abc.ABC, metaclass=AutoPatchRegister):
"""
An abstract `APContainer` that defines the requirements for an object
to be used by the `Patch.create_rom_file` function.
"""
result_file_ending: str = ".sfc"
@abc.abstractmethod
def patch(self, target: str) -> None:
""" create the output file with the file name `target` """
class APDeltaPatch(APPatch):
"""An APPatch that additionally has delta.bsdiff4
containing a delta patch to get the desired file, often a rom."""
hash: Optional[str] # base checksum of source file
patch_file_ending: str = ""
delta: Optional[bytes] = None
result_file_ending: str = ".sfc"
source_data: bytes
def __init__(self, *args: Any, patched_path: str = "", **kwargs: Any) -> None:

View File

@@ -6,7 +6,7 @@ from typing import Optional, Union, List, Tuple, Callable, Dict, TYPE_CHECKING
from Fill import FillError
from .Options import LTTPBosses as Bosses
from .StateHelpers import can_shoot_arrows, can_extend_magic, can_get_good_bee, has_sword, has_beam_sword, \
has_melee_weapon, has_fire_source
has_melee_weapon, has_fire_source, can_use_bombs
if TYPE_CHECKING:
from . import ALTTPWorld
@@ -62,7 +62,8 @@ def MoldormDefeatRule(state, player: int) -> bool:
def HelmasaurKingDefeatRule(state, player: int) -> bool:
# TODO: technically possible with the hammer
return has_sword(state, player) or can_shoot_arrows(state, player)
return (can_use_bombs(state, player, 5) or state.has("Hammer", player)) and (has_sword(state, player)
or can_shoot_arrows(state, player))
def ArrghusDefeatRule(state, player: int) -> bool:
@@ -143,7 +144,7 @@ def GanonDefeatRule(state, player: int) -> bool:
can_hurt = has_beam_sword(state, player)
common = can_hurt and has_fire_source(state, player)
# silverless ganon may be needed in anything higher than no glitches
if state.multiworld.logic[player] != 'noglitches':
if state.multiworld.glitches_required[player] != 'no_glitches':
# 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 can_shoot_arrows(state, player)) or

View File

@@ -9,7 +9,7 @@ from Fill import fill_restrictive
from .Bosses import BossFactory, Boss
from .Items import ItemFactory
from .Regions import lookup_boss_drops, key_drop_data
from .Options import smallkey_shuffle
from .Options import small_key_shuffle
if typing.TYPE_CHECKING:
from .SubClasses import ALttPLocation, ALttPItem
@@ -66,7 +66,7 @@ def create_dungeons(world: "ALTTPWorld"):
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
dungeon = Dungeon(name, dungeon_regions, big_key,
[] if multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal else small_keys,
[] if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal else small_keys,
dungeon_items, player)
for item in dungeon.all_items:
item.dungeon = dungeon

View File

@@ -23,7 +23,7 @@ def parse_arguments(argv, no_defaults=False):
multiargs, _ = parser.parse_known_args(argv)
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'owglitches', 'hybridglitches', 'nologic'],
parser.add_argument('--logic', default=defval('no_glitches'), const='no_glitches', nargs='?', choices=['no_glitches', 'minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'],
help='''\
Select Enforcement of Item Requirements. (default: %(default)s)
No Glitches:
@@ -49,7 +49,7 @@ def parse_arguments(argv, no_defaults=False):
instead of a bunny.
''')
parser.add_argument('--goal', default=defval('ganon'), const='ganon', nargs='?',
choices=['ganon', 'pedestal', 'bosses', 'triforcehunt', 'localtriforcehunt', 'ganontriforcehunt', 'localganontriforcehunt', 'crystals', 'ganonpedestal'],
choices=['ganon', 'pedestal', 'bosses', 'triforce_hunt', 'local_triforce_hunt', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'crystals', 'ganon_pedestal'],
help='''\
Select completion goal. (default: %(default)s)
Ganon: Collect all crystals, beat Agahnim 2 then
@@ -92,7 +92,7 @@ def parse_arguments(argv, no_defaults=False):
Hard: Reduced functionality.
Expert: Greatly reduced functionality.
''')
parser.add_argument('--timer', default=defval('none'), const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'],
parser.add_argument('--timer', default=defval('none'), const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed_ohko', 'ohko', 'timed_countdown'],
help='''\
Select game timer setting. Affects available itempool. (default: %(default)s)
None: No timer.
@@ -151,7 +151,7 @@ def parse_arguments(argv, no_defaults=False):
slightly biased to placing progression items with
less restrictions.
''')
parser.add_argument('--shuffle', default=defval('vanilla'), const='vanilla', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple', 'dungeonscrossed'],
parser.add_argument('--shuffle', default=defval('vanilla'), const='vanilla', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed'],
help='''\
Select Entrance Shuffling Algorithm. (default: %(default)s)
Full: Mix cave and dungeon entrances freely while limiting
@@ -178,9 +178,9 @@ def parse_arguments(argv, no_defaults=False):
parser.add_argument('--open_pyramid', default=defval('auto'), help='''\
Pre-opens the pyramid hole, this removes the Agahnim 2 requirement for it.
Depending on goal, you might still need to beat Agahnim 2 in order to beat ganon.
fast ganon goals are crystals, ganontriforcehunt, localganontriforcehunt, pedestalganon
fast ganon goals are crystals, ganon_triforce_hunt, local_ganon_triforce_hunt, pedestalganon
auto - Only opens pyramid hole if the goal specifies a fast ganon, and entrance shuffle
is vanilla, dungeonssimple or dungeonsfull.
is vanilla, dungeons_simple or dungeons_full.
goal - Opens pyramid hole if the goal specifies a fast ganon.
yes - Always opens the pyramid hole.
no - Never opens the pyramid hole.

View File

@@ -21,17 +21,17 @@ def link_entrances(world, player):
connect_simple(world, exitname, regionname, player)
# if we do not shuffle, set default connections
if world.shuffle[player] == 'vanilla':
if world.entrance_shuffle[player] == 'vanilla':
for exitname, regionname in default_connections:
connect_simple(world, exitname, regionname, player)
for exitname, regionname in default_dungeon_connections:
connect_simple(world, exitname, regionname, player)
elif world.shuffle[player] == 'dungeonssimple':
elif world.entrance_shuffle[player] == 'dungeons_simple':
for exitname, regionname in default_connections:
connect_simple(world, exitname, regionname, player)
simple_shuffle_dungeons(world, player)
elif world.shuffle[player] == 'dungeonsfull':
elif world.entrance_shuffle[player] == 'dungeons_full':
for exitname, regionname in default_connections:
connect_simple(world, exitname, regionname, player)
@@ -63,9 +63,9 @@ def link_entrances(world, player):
connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit), player)
connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit), player)
connect_caves(world, lw_entrances, dw_entrances, dungeon_exits, player)
elif world.shuffle[player] == 'dungeonscrossed':
elif world.entrance_shuffle[player] == 'dungeons_crossed':
crossed_shuffle_dungeons(world, player)
elif world.shuffle[player] == 'simple':
elif world.entrance_shuffle[player] == 'simple':
simple_shuffle_dungeons(world, player)
old_man_entrances = list(Old_Man_Entrances)
@@ -136,7 +136,7 @@ def link_entrances(world, player):
# place remaining doors
connect_doors(world, single_doors, door_targets, player)
elif world.shuffle[player] == 'restricted':
elif world.entrance_shuffle[player] == 'restricted':
simple_shuffle_dungeons(world, player)
lw_entrances = list(LW_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances)
@@ -207,62 +207,8 @@ def link_entrances(world, player):
# place remaining doors
connect_doors(world, doors, door_targets, player)
elif world.shuffle[player] == 'restricted_legacy':
simple_shuffle_dungeons(world, player)
lw_entrances = list(LW_Entrances)
dw_entrances = list(DW_Entrances)
dw_must_exits = list(DW_Entrances_Must_Exit)
old_man_entrances = list(Old_Man_Entrances)
caves = list(Cave_Exits)
three_exit_caves = list(Cave_Three_Exits)
single_doors = list(Single_Cave_Doors)
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
door_targets = list(Single_Cave_Targets)
# only use two exit caves to do mandatory dw connections
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
# add three exit doors to pool for remainder
caves.extend(three_exit_caves)
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
lw_entrances.extend(old_man_entrances)
world.random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
# place Old Man House in Light World
connect_caves(world, lw_entrances, [], Old_Man_House, player)
# connect rest. There's 2 dw entrances remaining, so we will not run into parity issue placing caves
connect_caves(world, lw_entrances, dw_entrances, caves, player)
# scramble holes
scramble_holes(world, player)
# place blacksmith, has limited options
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
# place remaining doors
connect_doors(world, single_doors, door_targets, player)
elif world.shuffle[player] == 'full':
elif world.entrance_shuffle[player] == 'full':
skull_woods_shuffle(world, player)
lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances)
@@ -368,7 +314,7 @@ def link_entrances(world, player):
# place remaining doors
connect_doors(world, doors, door_targets, player)
elif world.shuffle[player] == 'crossed':
elif world.entrance_shuffle[player] == 'crossed':
skull_woods_shuffle(world, player)
entrances = list(LW_Entrances + LW_Dungeon_Entrances + LW_Single_Cave_Doors + Old_Man_Entrances + DW_Entrances + DW_Dungeon_Entrances + DW_Single_Cave_Doors)
@@ -445,337 +391,8 @@ def link_entrances(world, player):
# place remaining doors
connect_doors(world, entrances, door_targets, player)
elif world.shuffle[player] == 'full_legacy':
skull_woods_shuffle(world, player)
lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances)
dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances)
dw_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit)
lw_must_exits = list(LW_Dungeon_Entrances_Must_Exit)
old_man_entrances = list(Old_Man_Entrances + ['Tower of Hera'])
caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits) # don't need to consider three exit caves, have one exit caves to avoid parity issues
single_doors = list(Single_Cave_Doors)
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
door_targets = list(Single_Cave_Targets)
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
caves.append(tuple(world.random.sample(
['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
lw_entrances.append('Hyrule Castle Entrance (South)')
if not world.shuffle_ganon:
connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player)
else:
dw_entrances.append('Ganons Tower')
caves.append('Ganons Tower Exit')
# we randomize which world requirements we fulfill first so we get better dungeon distribution
if world.random.randint(0, 1) == 0:
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
else:
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
if world.mode[player] == 'standard':
# rest of hyrule castle must be in light world
connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player)
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in lw_entrances]
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
lw_entrances.remove(old_man_exit)
world.random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
# place Old Man House in Light World
connect_caves(world, lw_entrances, [], list(Old_Man_House), player) #need this to avoid badness with multiple seeds
# now scramble the rest
connect_caves(world, lw_entrances, dw_entrances, caves, player)
# scramble holes
scramble_holes(world, player)
# place blacksmith, has limited options
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place bomb shop, has limited options
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
# place remaining doors
connect_doors(world, single_doors, door_targets, player)
elif world.shuffle[player] == 'madness_legacy':
# here lie dragons, connections are no longer two way
lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances + Old_Man_Entrances)
dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances)
dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit)
lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave',
'Bat Cave Cave',
'North Fairy Cave',
'Sanctuary',
'Lost Woods Hideout Stump',
'Lumberjack Tree Cave'] + list(
Old_Man_Entrances)
dw_doors = list(
DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + [
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
world.random.shuffle(lw_doors)
world.random.shuffle(dw_doors)
dw_entrances_must_exits.append('Skull Woods Second Section Door (West)')
dw_entrances.append('Skull Woods Second Section Door (East)')
dw_entrances.append('Skull Woods First Section Door')
lw_entrances.extend(
['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump',
'Lumberjack Tree Cave'])
lw_entrances_must_exits = list(LW_Dungeon_Entrances_Must_Exit)
old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera']
mandatory_light_world = ['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)']
mandatory_dark_world = []
caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits)
# shuffle up holes
lw_hole_entrances = ['Kakariko Well Drop', 'Bat Cave Drop', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave']
dw_hole_entrances = ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
hole_targets = [('Kakariko Well Exit', 'Kakariko Well (top)'),
('Bat Cave Exit', 'Bat Cave (right)'),
('North Fairy Cave Exit', 'North Fairy Cave'),
('Lost Woods Hideout Exit', 'Lost Woods Hideout (top)'),
('Lumberjack Tree Exit', 'Lumberjack Tree (top)'),
(('Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'), 'Skull Woods Second Section (Drop)')]
if world.mode[player] == 'standard':
# cannot move uncle cave
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player)
connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player)
else:
lw_hole_entrances.append('Hyrule Castle Secret Entrance Drop')
hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance'))
lw_doors.append('Hyrule Castle Secret Entrance Stairs')
lw_entrances.append('Hyrule Castle Secret Entrance Stairs')
if not world.shuffle_ganon:
connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player)
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
else:
dw_entrances.append('Ganons Tower')
caves.append('Ganons Tower Exit')
dw_hole_entrances.append('Pyramid Hole')
hole_targets.append(('Pyramid Exit', 'Pyramid'))
dw_entrances_must_exits.append('Pyramid Entrance')
dw_doors.extend(['Ganons Tower', 'Pyramid Entrance'])
world.random.shuffle(lw_hole_entrances)
world.random.shuffle(dw_hole_entrances)
world.random.shuffle(hole_targets)
# decide if skull woods first section should be in light or dark world
sw_light = world.random.randint(0, 1) == 0
if sw_light:
sw_hole_pool = lw_hole_entrances
mandatory_light_world.append('Skull Woods First Section Exit')
else:
sw_hole_pool = dw_hole_entrances
mandatory_dark_world.append('Skull Woods First Section Exit')
for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)',
'Skull Woods First Section (Top)']:
connect_entrance(world, sw_hole_pool.pop(), target, player)
# sanctuary has to be in light world
connect_entrance(world, lw_hole_entrances.pop(), 'Sewer Drop', player)
mandatory_light_world.append('Sanctuary Exit')
# fill up remaining holes
for hole in dw_hole_entrances:
exits, target = hole_targets.pop()
mandatory_dark_world.append(exits)
connect_entrance(world, hole, target, player)
for hole in lw_hole_entrances:
exits, target = hole_targets.pop()
mandatory_light_world.append(exits)
connect_entrance(world, hole, target, player)
# hyrule castle handling
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player)
mandatory_light_world.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
else:
lw_doors.append('Hyrule Castle Entrance (South)')
lw_entrances.append('Hyrule Castle Entrance (South)')
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
# now let's deal with mandatory reachable stuff
def extract_reachable_exit(cavelist):
world.random.shuffle(cavelist)
candidate = None
for cave in cavelist:
if isinstance(cave, tuple) and len(cave) > 1:
# special handling: TRock and Spectracle Rock cave have two entries that we should consider entrance only
# ToDo this should be handled in a more sensible manner
if cave[0] in ['Turtle Rock Exit (Front)', 'Spectacle Rock Cave Exit (Peak)'] and len(cave) == 2:
continue
candidate = cave
break
if candidate is None:
raise KeyError('No suitable cave.')
cavelist.remove(candidate)
return candidate
def connect_reachable_exit(entrance, general, worldspecific, worldoors):
# select which one is the primary option
if world.random.randint(0, 1) == 0:
primary = general
secondary = worldspecific
else:
primary = worldspecific
secondary = general
try:
cave = extract_reachable_exit(primary)
except KeyError:
cave = extract_reachable_exit(secondary)
exit = cave[-1]
cave = cave[:-1]
connect_exit(world, exit, entrance, player)
connect_entrance(world, worldoors.pop(), exit, player)
# rest of cave now is forced to be in this world
worldspecific.append(cave)
# we randomize which world requirements we fulfill first so we get better dungeon distribution
if world.random.randint(0, 1) == 0:
for entrance in lw_entrances_must_exits:
connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors)
for entrance in dw_entrances_must_exits:
connect_reachable_exit(entrance, caves, mandatory_dark_world, dw_doors)
else:
for entrance in dw_entrances_must_exits:
connect_reachable_exit(entrance, caves, mandatory_dark_world, dw_doors)
for entrance in lw_entrances_must_exits:
connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors)
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in lw_entrances]
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
lw_entrances.remove(old_man_exit)
connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player)
connect_entrance(world, lw_doors.pop(), 'Old Man Cave Exit (East)', player)
mandatory_light_world.append('Old Man Cave Exit (West)')
# we connect up the mandatory associations we have found
for mandatory in mandatory_light_world:
if not isinstance(mandatory, tuple):
mandatory = (mandatory,)
for exit in mandatory:
# point out somewhere
connect_exit(world, exit, lw_entrances.pop(), player)
# point in from somewhere
connect_entrance(world, lw_doors.pop(), exit, player)
for mandatory in mandatory_dark_world:
if not isinstance(mandatory, tuple):
mandatory = (mandatory,)
for exit in mandatory:
# point out somewhere
connect_exit(world, exit, dw_entrances.pop(), player)
# point in from somewhere
connect_entrance(world, dw_doors.pop(), exit, player)
# handle remaining caves
while caves:
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits accross worlds left to fill
cave_candidate = (None, 0)
for i, cave in enumerate(caves):
if isinstance(cave, str):
cave = (cave,)
if len(cave) > cave_candidate[1]:
cave_candidate = (i, len(cave))
cave = caves.pop(cave_candidate[0])
place_lightworld = world.random.randint(0, 1) == 0
if place_lightworld:
target_doors = lw_doors
target_entrances = lw_entrances
else:
target_doors = dw_doors
target_entrances = dw_entrances
if isinstance(cave, str):
cave = (cave,)
# check if we can still fit the cave into our target group
if len(target_doors) < len(cave):
if not place_lightworld:
target_doors = lw_doors
target_entrances = lw_entrances
else:
target_doors = dw_doors
target_entrances = dw_entrances
for exit in cave:
connect_exit(world, exit, target_entrances.pop(), player)
connect_entrance(world, target_doors.pop(), exit, player)
# handle simple doors
single_doors = list(Single_Cave_Doors)
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
door_targets = list(Single_Cave_Targets)
# place blacksmith, has limited options
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
# place remaining doors
connect_doors(world, single_doors, door_targets, player)
elif world.shuffle[player] == 'insanity':
elif world.entrance_shuffle[player] == 'insanity':
# beware ye who enter here
entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave']
@@ -922,157 +539,15 @@ def link_entrances(world, player):
# place remaining doors
connect_doors(world, doors, door_targets, player)
elif world.shuffle[player] == 'insanity_legacy':
world.fix_fake_world[player] = False
# beware ye who enter here
entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave']
entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)']
doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\
DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)']
world.random.shuffle(doors)
old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera']
caves = Cave_Exits + Dungeon_Exits + Cave_Three_Exits + ['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)',
'Kakariko Well Exit', 'Bat Cave Exit', 'North Fairy Cave Exit', 'Lost Woods Hideout Exit', 'Lumberjack Tree Exit', 'Sanctuary Exit']
# shuffle up holes
hole_entrances = ['Kakariko Well Drop', 'Bat Cave Drop', 'North Fairy Cave Drop', 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave',
'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
hole_targets = ['Kakariko Well (top)', 'Bat Cave (right)', 'North Fairy Cave', 'Lost Woods Hideout (top)', 'Lumberjack Tree (top)', 'Sewer Drop', 'Skull Woods Second Section (Drop)',
'Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)']
if world.mode[player] == 'standard':
# cannot move uncle cave
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player)
connect_entrance(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player)
else:
hole_entrances.append('Hyrule Castle Secret Entrance Drop')
hole_targets.append('Hyrule Castle Secret Entrance')
doors.append('Hyrule Castle Secret Entrance Stairs')
entrances.append('Hyrule Castle Secret Entrance Stairs')
caves.append('Hyrule Castle Secret Entrance Exit')
if not world.shuffle_ganon:
connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player)
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
else:
entrances.append('Ganons Tower')
caves.extend(['Ganons Tower Exit', 'Pyramid Exit'])
hole_entrances.append('Pyramid Hole')
hole_targets.append('Pyramid')
entrances_must_exits.append('Pyramid Entrance')
doors.extend(['Ganons Tower', 'Pyramid Entrance'])
world.random.shuffle(hole_entrances)
world.random.shuffle(hole_targets)
world.random.shuffle(entrances)
# fill up holes
for hole in hole_entrances:
connect_entrance(world, hole, hole_targets.pop(), player)
# hyrule castle handling
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player)
caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
else:
doors.append('Hyrule Castle Entrance (South)')
entrances.append('Hyrule Castle Entrance (South)')
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
# now let's deal with mandatory reachable stuff
def extract_reachable_exit(cavelist):
world.random.shuffle(cavelist)
candidate = None
for cave in cavelist:
if isinstance(cave, tuple) and len(cave) > 1:
# special handling: TRock has two entries that we should consider entrance only
# ToDo this should be handled in a more sensible manner
if cave[0] in ['Turtle Rock Exit (Front)', 'Spectacle Rock Cave Exit (Peak)'] and len(cave) == 2:
continue
candidate = cave
break
if candidate is None:
raise KeyError('No suitable cave.')
cavelist.remove(candidate)
return candidate
def connect_reachable_exit(entrance, caves, doors):
cave = extract_reachable_exit(caves)
exit = cave[-1]
cave = cave[:-1]
connect_exit(world, exit, entrance, player)
connect_entrance(world, doors.pop(), exit, player)
# rest of cave now is forced to be in this world
caves.append(cave)
# connect mandatory exits
for entrance in entrances_must_exits:
connect_reachable_exit(entrance, caves, doors)
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances]
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
entrances.remove(old_man_exit)
connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player)
connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)', player)
caves.append('Old Man Cave Exit (West)')
# handle remaining caves
for cave in caves:
if isinstance(cave, str):
cave = (cave,)
for exit in cave:
connect_exit(world, exit, entrances.pop(), player)
connect_entrance(world, doors.pop(), exit, player)
# handle simple doors
single_doors = list(Single_Cave_Doors)
bomb_shop_doors = list(Bomb_Shop_Single_Cave_Doors)
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
door_targets = list(Single_Cave_Targets)
# place blacksmith, has limited options
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
# place remaining doors
connect_doors(world, single_doors, door_targets, player)
else:
raise NotImplementedError(
f'{world.shuffle[player]} Shuffling not supported yet. Player {world.get_player_name(player)}')
f'{world.entrance_shuffle[player]} Shuffling not supported yet. Player {world.get_player_name(player)}')
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
if world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']:
overworld_glitch_connections(world, player)
# mandatory hybrid major glitches connections
if world.logic[player] in ['hybridglitches', 'nologic']:
if world.glitches_required[player] in ['hybrid_major_glitches', 'no_logic']:
underworld_glitch_connections(world, player)
# check for swamp palace fix
@@ -1106,17 +581,17 @@ def link_inverted_entrances(world, player):
connect_simple(world, exitname, regionname, player)
# if we do not shuffle, set default connections
if world.shuffle[player] == 'vanilla':
if world.entrance_shuffle[player] == 'vanilla':
for exitname, regionname in inverted_default_connections:
connect_simple(world, exitname, regionname, player)
for exitname, regionname in inverted_default_dungeon_connections:
connect_simple(world, exitname, regionname, player)
elif world.shuffle[player] == 'dungeonssimple':
elif world.entrance_shuffle[player] == 'dungeons_simple':
for exitname, regionname in inverted_default_connections:
connect_simple(world, exitname, regionname, player)
simple_shuffle_dungeons(world, player)
elif world.shuffle[player] == 'dungeonsfull':
elif world.entrance_shuffle[player] == 'dungeons_full':
for exitname, regionname in inverted_default_connections:
connect_simple(world, exitname, regionname, player)
@@ -1171,9 +646,9 @@ def link_inverted_entrances(world, player):
connect_mandatory_exits(world, lw_entrances, dungeon_exits, lw_dungeon_entrances_must_exit, player)
connect_caves(world, lw_entrances, dw_entrances, dungeon_exits, player)
elif world.shuffle[player] == 'dungeonscrossed':
elif world.entrance_shuffle[player] == 'dungeons_crossed':
inverted_crossed_shuffle_dungeons(world, player)
elif world.shuffle[player] == 'simple':
elif world.entrance_shuffle[player] == 'simple':
simple_shuffle_dungeons(world, player)
old_man_entrances = list(Inverted_Old_Man_Entrances)
@@ -1270,7 +745,7 @@ def link_inverted_entrances(world, player):
# place remaining doors
connect_doors(world, single_doors, door_targets, player)
elif world.shuffle[player] == 'restricted':
elif world.entrance_shuffle[player] == 'restricted':
simple_shuffle_dungeons(world, player)
lw_entrances = list(Inverted_LW_Entrances + Inverted_LW_Single_Cave_Doors)
@@ -1355,7 +830,7 @@ def link_inverted_entrances(world, player):
doors = lw_entrances + dw_entrances
# place remaining doors
connect_doors(world, doors, door_targets, player)
elif world.shuffle[player] == 'full':
elif world.entrance_shuffle[player] == 'full':
skull_woods_shuffle(world, player)
lw_entrances = list(Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Single_Cave_Doors)
@@ -1506,7 +981,7 @@ def link_inverted_entrances(world, player):
# place remaining doors
connect_doors(world, doors, door_targets, player)
elif world.shuffle[player] == 'crossed':
elif world.entrance_shuffle[player] == 'crossed':
skull_woods_shuffle(world, player)
entrances = list(Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Single_Cave_Doors + Inverted_Old_Man_Entrances + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_DW_Single_Cave_Doors)
@@ -1617,7 +1092,7 @@ def link_inverted_entrances(world, player):
# place remaining doors
connect_doors(world, entrances, door_targets, player)
elif world.shuffle[player] == 'insanity':
elif world.entrance_shuffle[player] == 'insanity':
# beware ye who enter here
entrances = Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_Old_Man_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)']
@@ -1776,10 +1251,10 @@ def link_inverted_entrances(world, player):
else:
raise NotImplementedError('Shuffling not supported yet')
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
if world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']:
overworld_glitch_connections(world, player)
# mandatory hybrid major glitches connections
if world.logic[player] in ['hybridglitches', 'nologic']:
if world.glitches_required[player] in ['hybrid_major_glitches', 'no_logic']:
underworld_glitch_connections(world, player)
# patch swamp drain
@@ -1880,14 +1355,14 @@ def scramble_holes(world, player):
hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance'))
# do not shuffle sanctuary into pyramid hole unless shuffle is crossed
if world.shuffle[player] == 'crossed':
if world.entrance_shuffle[player] == 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
if world.shuffle_ganon:
world.random.shuffle(hole_targets)
exit, target = hole_targets.pop()
connect_two_way(world, 'Pyramid Entrance', exit, player)
connect_entrance(world, 'Pyramid Hole', target, player)
if world.shuffle[player] != 'crossed':
if world.entrance_shuffle[player] != 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
world.random.shuffle(hole_targets)
@@ -1922,14 +1397,14 @@ def scramble_inverted_holes(world, player):
hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance'))
# do not shuffle sanctuary into pyramid hole unless shuffle is crossed
if world.shuffle[player] == 'crossed':
if world.entrance_shuffle[player] == 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
if world.shuffle_ganon:
world.random.shuffle(hole_targets)
exit, target = hole_targets.pop()
connect_two_way(world, 'Inverted Pyramid Entrance', exit, player)
connect_entrance(world, 'Inverted Pyramid Hole', target, player)
if world.shuffle[player] != 'crossed':
if world.entrance_shuffle[player] != 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
world.random.shuffle(hole_targets)
@@ -1958,7 +1433,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', 'hybridglitches', 'nologic']:
if world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']:
from worlds.alttp import OverworldGlitchRules
for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'):
invalid_connections[entrance] = set()
@@ -3038,6 +2513,7 @@ mandatory_connections = [('Links House S&Q', 'Links House'),
('Sanctuary Push Door', 'Sanctuary'),
('Sewer Drop', 'Sewers'),
('Sewers Back Door', 'Sewers (Dark)'),
('Sewers Secret Room', 'Sewers Secret Room'),
('Agahnim 1', 'Agahnim 1'),
('Flute Spot 1', 'Death Mountain'),
('Death Mountain Entrance Rock', 'Death Mountain Entrance'),
@@ -3053,6 +2529,8 @@ mandatory_connections = [('Links House S&Q', 'Links House'),
('Spiral Cave Ledge Access', 'Spiral Cave Ledge'),
('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'),
('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'),
('Hookshot Cave Bomb Wall (South)', 'Hookshot Cave (Upper)'),
('Hookshot Cave Bomb Wall (North)', 'Hookshot Cave'),
('East Death Mountain (Top)', 'East Death Mountain (Top)'),
('Death Mountain (Top)', 'Death Mountain (Top)'),
('Death Mountain Drop', 'Death Mountain'),
@@ -3227,6 +2705,7 @@ inverted_mandatory_connections = [('Links House S&Q', 'Inverted Links House'),
('Sanctuary Push Door', 'Sanctuary'),
('Sewer Drop', 'Sewers'),
('Sewers Back Door', 'Sewers (Dark)'),
('Sewers Secret Room', 'Sewers Secret Room'),
('Agahnim 1', 'Agahnim 1'),
('Death Mountain Entrance Rock', 'Death Mountain Entrance'),
('Death Mountain Entrance Drop', 'Light World'),
@@ -3241,6 +2720,8 @@ inverted_mandatory_connections = [('Links House S&Q', 'Inverted Links House'),
('Spiral Cave Ledge Access', 'Spiral Cave Ledge'),
('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'),
('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'),
('Hookshot Cave Bomb Wall (South)', 'Hookshot Cave (Upper)'),
('Hookshot Cave Bomb Wall (North)', 'Hookshot Cave'),
('East Death Mountain (Top)', 'East Death Mountain (Top)'),
('Death Mountain (Top)', 'Death Mountain (Top)'),
('Death Mountain Drop', 'Death Mountain'),
@@ -3572,7 +3053,7 @@ default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'),
('Superbunny Cave Exit (Bottom)', 'Dark Death Mountain (East Bottom)'),
('Hookshot Cave Exit (South)', 'Dark Death Mountain (Top)'),
('Hookshot Cave Exit (North)', 'Death Mountain Floating Island (Dark World)'),
('Hookshot Cave Back Entrance', 'Hookshot Cave'),
('Hookshot Cave Back Entrance', 'Hookshot Cave (Upper)'),
('Mimic Cave', 'Mimic Cave'),
('Pyramid Hole', 'Pyramid'),
@@ -3703,7 +3184,7 @@ inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'
('Superbunny Cave (Bottom)', 'Superbunny Cave (Bottom)'),
('Superbunny Cave Exit (Bottom)', 'Dark Death Mountain (East Bottom)'),
('Hookshot Cave Exit (North)', 'Death Mountain Floating Island (Dark World)'),
('Hookshot Cave Back Entrance', 'Hookshot Cave'),
('Hookshot Cave Back Entrance', 'Hookshot Cave (Upper)'),
('Mimic Cave', 'Mimic Cave'),
('Inverted Pyramid Hole', 'Pyramid'),
('Inverted Links House', 'Inverted Links House'),

View File

@@ -133,7 +133,7 @@ def create_inverted_regions(world, player):
create_cave_region(world, player, 'Kakariko Gamble Game', 'a game of chance'),
create_cave_region(world, player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
create_lw_region(world, player, 'Lake Hylia Island', ['Lake Hylia Island']),
create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies'),
create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade Shop']),
create_cave_region(world, player, 'Two Brothers House', 'a connector', None,
['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
create_lw_region(world, player, 'Maze Race Ledge', ['Maze Race'],
@@ -176,8 +176,9 @@ def create_inverted_regions(world, player):
'Throne Room']),
create_dungeon_region(world, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
create_dungeon_region(world, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross', 'Sewers - Key Rat Key Drop'], ['Sewers Door']),
create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', None, ['Sanctuary Push Door', 'Sewers Back Door', 'Sewers Secret Room']),
create_dungeon_region(world, player, 'Sewers Secret Room', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
'Sewers - Secret Room - Right']),
create_dungeon_region(world, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
create_dungeon_region(world, player, 'Inverted Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze', 'Castle Tower - Dark Archer Key Drop', 'Castle Tower - Circle of Pots Key Drop'], ['Agahnim 1', 'Inverted Agahnims Tower Exit']),
create_dungeon_region(world, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
@@ -346,7 +347,9 @@ def create_inverted_regions(world, player):
create_cave_region(world, player, 'Hookshot Cave', 'a connector',
['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right',
'Hookshot Cave - Bottom Left'],
['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']),
['Hookshot Cave Exit (South)', 'Hookshot Cave Bomb Wall (South)']),
create_cave_region(world, player, 'Hookshot Cave (Upper)', 'a connector', None, ['Hookshot Cave Exit (North)',
'Hookshot Cave Bomb Wall (North)']),
create_dw_region(world, player, 'Death Mountain Floating Island (Dark World)', None,
['Floating Island Drop', 'Hookshot Cave Back Entrance']),
create_cave_region(world, player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
@@ -380,8 +383,8 @@ def create_inverted_regions(world, player):
create_dungeon_region(world, player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest', 'Skull Woods - West Lobby Pot Key'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
create_dungeon_region(world, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room', 'Skull Woods - Spike Corner Key Drop'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
create_dungeon_region(world, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop'], ['Ice Palace (Second Section)', 'Ice Palace Exit']),
create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Main)']),
create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Second Section)', 'Ice Palace Exit']),
create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Palace (Main)']),
create_dungeon_region(world, player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Freezor Chest',
'Ice Palace - Many Pots Pot Key',
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']),

View File

@@ -5,12 +5,12 @@ from BaseClasses import ItemClassification
from Fill import FillError
from .SubClasses import ALttPLocation, LTTPRegion, LTTPRegionType
from .Shops import TakeAny, total_shop_slots, set_up_shops, shuffle_shops, create_dynamic_shop_locations
from .Shops import TakeAny, total_shop_slots, set_up_shops, shop_table_by_location, ShopType
from .Bosses import place_bosses
from .Dungeons import get_dungeon_item_pool_player
from .EntranceShuffle import connect_entrance
from .Items import ItemFactory, GetBeemizerItem
from .Options import smallkey_shuffle, compass_shuffle, bigkey_shuffle, map_shuffle, LTTPBosses
from .Items import ItemFactory, GetBeemizerItem, trap_replaceable, item_name_groups
from .Options import small_key_shuffle, compass_shuffle, big_key_shuffle, map_shuffle, TriforcePiecesMode
from .StateHelpers import has_triforce_pieces, has_melee_weapon
from .Regions import key_drop_data
@@ -189,104 +189,62 @@ difficulties = {
),
}
ice_rod_hunt_difficulties = dict()
for diff in {'easy', 'normal', 'hard', 'expert'}:
ice_rod_hunt_difficulties[diff] = Difficulty(
baseitems=['Nothing'] * 41,
bottles=['Nothing'] * 4,
bottle_count=difficulties[diff].bottle_count,
same_bottle=difficulties[diff].same_bottle,
progressiveshield=['Nothing'] * 3,
basicshield=['Nothing'] * 3,
progressivearmor=['Nothing'] * 2,
basicarmor=['Nothing'] * 2,
swordless=['Nothing'] * 4,
progressivemagic=['Nothing'] * 2,
basicmagic=['Nothing'] * 2,
progressivesword=['Nothing'] * 4,
basicsword=['Nothing'] * 4,
progressivebow=['Nothing'] * 2,
basicbow=['Nothing'] * 2,
timedohko=difficulties[diff].timedohko,
timedother=difficulties[diff].timedother,
progressiveglove=['Nothing'] * 2,
basicglove=['Nothing'] * 2,
alwaysitems=['Ice Rod'] + ['Nothing'] * 19,
legacyinsanity=['Nothing'] * 2,
universal_keys=['Nothing'] * 29,
extras=[['Nothing'] * 15, ['Nothing'] * 15, ['Nothing'] * 10, ['Nothing'] * 5, ['Nothing'] * 25],
progressive_sword_limit=difficulties[diff].progressive_sword_limit,
progressive_shield_limit=difficulties[diff].progressive_shield_limit,
progressive_armor_limit=difficulties[diff].progressive_armor_limit,
progressive_bow_limit=difficulties[diff].progressive_bow_limit,
progressive_bottle_limit=difficulties[diff].progressive_bottle_limit,
boss_heart_container_limit=difficulties[diff].boss_heart_container_limit,
heart_piece_limit=difficulties[diff].heart_piece_limit,
)
items_reduction_table = (
("Piece of Heart", "Boss Heart Container", 4, 1),
# the order of the upgrades is important
("Arrow Upgrade (+5)", "Arrow Upgrade (+10)", 8, 4),
("Arrow Upgrade (+5)", "Arrow Upgrade (+10)", 7, 4),
("Arrow Upgrade (+5)", "Arrow Upgrade (+10)", 6, 3),
("Arrow Upgrade (+10)", "Arrow Upgrade (70)", 4, 1),
("Bomb Upgrade (+5)", "Bomb Upgrade (+10)", 8, 4),
("Bomb Upgrade (+5)", "Bomb Upgrade (+10)", 7, 4),
("Bomb Upgrade (+5)", "Bomb Upgrade (+10)", 6, 3),
("Bomb Upgrade (+10)", "Bomb Upgrade (50)", 5, 1),
("Bomb Upgrade (+10)", "Bomb Upgrade (50)", 4, 1),
("Progressive Sword", 4),
("Fighter Sword", 1),
("Master Sword", 1),
("Tempered Sword", 1),
("Golden Sword", 1),
("Progressive Shield", 3),
("Blue Shield", 1),
("Red Shield", 1),
("Mirror Shield", 1),
("Progressive Mail", 2),
("Blue Mail", 1),
("Red Mail", 1),
("Progressive Bow", 2),
("Bow", 1),
("Silver Bow", 1),
("Lamp", 1),
("Bottles",)
)
def generate_itempool(world):
player = world.player
multiworld = world.multiworld
if multiworld.difficulty[player] not in difficulties:
raise NotImplementedError(f"Diffulty {multiworld.difficulty[player]}")
if multiworld.goal[player] not in {'ganon', 'pedestal', 'bosses', 'triforcehunt', 'localtriforcehunt', 'icerodhunt',
'ganontriforcehunt', 'localganontriforcehunt', 'crystals', 'ganonpedestal'}:
if multiworld.item_pool[player].current_key not in difficulties:
raise NotImplementedError(f"Diffulty {multiworld.item_pool[player]}")
if multiworld.goal[player] not in ('ganon', 'pedestal', 'bosses', 'triforce_hunt', 'local_triforce_hunt',
'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'crystals',
'ganon_pedestal'):
raise NotImplementedError(f"Goal {multiworld.goal[player]} for player {player}")
if multiworld.mode[player] not in {'open', 'standard', 'inverted'}:
if multiworld.mode[player] not in ('open', 'standard', 'inverted'):
raise NotImplementedError(f"Mode {multiworld.mode[player]} for player {player}")
if multiworld.timer[player] not in {False, 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'}:
if multiworld.timer[player] not in {False, 'display', 'timed', 'timed_ohko', 'ohko', 'timed_countdown'}:
raise NotImplementedError(f"Timer {multiworld.mode[player]} for player {player}")
if multiworld.timer[player] in ['ohko', 'timed-ohko']:
if multiworld.timer[player] in ['ohko', 'timed_ohko']:
multiworld.can_take_damage[player] = False
if multiworld.goal[player] in ['pedestal', 'triforcehunt', 'localtriforcehunt', 'icerodhunt']:
if multiworld.goal[player] in ['pedestal', 'triforce_hunt', 'local_triforce_hunt']:
multiworld.push_item(multiworld.get_location('Ganon', player), ItemFactory('Nothing', player), False)
else:
multiworld.push_item(multiworld.get_location('Ganon', player), ItemFactory('Triforce', player), False)
if multiworld.goal[player] == 'icerodhunt':
multiworld.progression_balancing[player].value = 0
loc = multiworld.get_location('Turtle Rock - Boss', player)
multiworld.push_item(loc, ItemFactory('Triforce Piece', player), False)
multiworld.treasure_hunt_count[player] = 1
if multiworld.boss_shuffle[player] != 'none':
if isinstance(multiworld.boss_shuffle[player].value, str) and 'turtle rock-' not in multiworld.boss_shuffle[player].value:
multiworld.boss_shuffle[player] = LTTPBosses.from_text(f'Turtle Rock-Trinexx;{multiworld.boss_shuffle[player].current_key}')
elif isinstance(multiworld.boss_shuffle[player].value, int):
multiworld.boss_shuffle[player] = LTTPBosses.from_text(f'Turtle Rock-Trinexx;{multiworld.boss_shuffle[player].current_key}')
else:
logging.warning(f'Cannot guarantee that Trinexx is the boss of Turtle Rock for player {player}')
loc.event = True
loc.locked = True
itemdiff = difficulties[multiworld.difficulty[player]]
itempool = []
itempool.extend(itemdiff.alwaysitems)
itempool.remove('Ice Rod')
itempool.extend(['Single Arrow', 'Sanctuary Heart Container'])
itempool.extend(['Boss Heart Container'] * itemdiff.boss_heart_container_limit)
itempool.extend(['Piece of Heart'] * itemdiff.heart_piece_limit)
itempool.extend(itemdiff.bottles)
itempool.extend(itemdiff.basicbow)
itempool.extend(itemdiff.basicarmor)
if not multiworld.swordless[player]:
itempool.extend(itemdiff.basicsword)
itempool.extend(itemdiff.basicmagic)
itempool.extend(itemdiff.basicglove)
itempool.extend(itemdiff.basicshield)
itempool.extend(itemdiff.legacyinsanity)
itempool.extend(['Rupees (300)'] * 34)
itempool.extend(['Bombs (10)'] * 5)
itempool.extend(['Arrows (10)'] * 7)
if multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
itempool.extend(itemdiff.universal_keys)
for item in itempool:
multiworld.push_precollected(ItemFactory(item, player))
if multiworld.goal[player] in ['triforcehunt', 'localtriforcehunt', 'icerodhunt']:
if multiworld.goal[player] in ['triforce_hunt', 'local_triforce_hunt']:
region = multiworld.get_region('Light World', player)
loc = ALttPLocation(player, "Murahdahla", parent=region)
@@ -308,7 +266,8 @@ def generate_itempool(world):
('Missing Smith', 'Return Smith'),
('Floodgate', 'Open Floodgate'),
('Agahnim 1', 'Beat Agahnim 1'),
('Flute Activation Spot', 'Activated Flute')
('Flute Activation Spot', 'Activated Flute'),
('Capacity Upgrade Shop', 'Capacity Upgrade Shop')
]
for location_name, event_name in event_pairs:
location = multiworld.get_location(location_name, player)
@@ -340,17 +299,31 @@ def generate_itempool(world):
if not found_sword:
found_sword = True
possible_weapons.append(item)
if item in ['Progressive Bow', 'Bow'] and not found_bow:
elif item in ['Progressive Bow', 'Bow'] and not found_bow:
found_bow = True
possible_weapons.append(item)
if item in ['Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
elif item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
if item not in possible_weapons:
possible_weapons.append(item)
elif (item == 'Bombs (10)' and (not multiworld.bombless_start[player]) and item not in
possible_weapons):
possible_weapons.append(item)
elif (item in ['Bomb Upgrade (+10)', 'Bomb Upgrade (50)'] and multiworld.bombless_start[player] and item
not in possible_weapons):
possible_weapons.append(item)
starting_weapon = multiworld.random.choice(possible_weapons)
placed_items["Link's Uncle"] = starting_weapon
pool.remove(starting_weapon)
if placed_items["Link's Uncle"] in ['Bow', 'Progressive Bow', 'Bombs (10)', 'Cane of Somaria', 'Cane of Byrna'] and multiworld.enemy_health[player] not in ['default', 'easy']:
multiworld.escape_assist[player].append('bombs')
if (placed_items["Link's Uncle"] in ['Bow', 'Progressive Bow', 'Bombs (10)', 'Bomb Upgrade (+10)',
'Bomb Upgrade (50)', 'Cane of Somaria', 'Cane of Byrna'] and multiworld.enemy_health[player] not in ['default', 'easy']):
if multiworld.bombless_start[player] and "Bomb Upgrade" not in placed_items["Link's Uncle"]:
if 'Bow' in placed_items["Link's Uncle"]:
multiworld.escape_assist[player].append('arrows')
elif 'Cane' in placed_items["Link's Uncle"]:
multiworld.escape_assist[player].append('magic')
else:
multiworld.escape_assist[player].append('bombs')
for (location, item) in placed_items.items():
multiworld.get_location(location, player).place_locked_item(ItemFactory(item, player))
@@ -377,7 +350,7 @@ def generate_itempool(world):
for key_loc in key_drop_data:
key_data = key_drop_data[key_loc]
drop_item = ItemFactory(key_data[3], player)
if multiworld.goal[player] == 'icerodhunt' or not multiworld.key_drop_shuffle[player]:
if not multiworld.key_drop_shuffle[player]:
if drop_item in dungeon_items:
dungeon_items.remove(drop_item)
else:
@@ -391,88 +364,151 @@ def generate_itempool(world):
world.dungeons[dungeon].small_keys.remove(drop_item)
elif world.dungeons[dungeon].big_key is not None and world.dungeons[dungeon].big_key == drop_item:
world.dungeons[dungeon].big_key = None
if not multiworld.key_drop_shuffle[player]:
# key drop item was removed from the pool because key drop shuffle is off
# and it will now place the removed key into its original location
loc = multiworld.get_location(key_loc, player)
loc.place_locked_item(drop_item)
loc.address = None
elif multiworld.goal[player] == 'icerodhunt':
# key drop item removed because of icerodhunt
multiworld.itempool.append(ItemFactory(GetBeemizerItem(world, player, 'Nothing'), player))
multiworld.push_precollected(drop_item)
elif "Small" in key_data[3] and multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
elif "Small" in key_data[3] and multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal:
# key drop shuffle and universal keys are on. Add universal keys in place of key drop keys.
multiworld.itempool.append(ItemFactory(GetBeemizerItem(world, player, 'Small Key (Universal)'), player))
multiworld.itempool.append(ItemFactory(GetBeemizerItem(multiworld, player, 'Small Key (Universal)'), player))
dungeon_item_replacements = sum(difficulties[multiworld.difficulty[player]].extras, []) * 2
multiworld.random.shuffle(dungeon_item_replacements)
if multiworld.goal[player] == 'icerodhunt':
for item in dungeon_items:
multiworld.itempool.append(ItemFactory(GetBeemizerItem(multiworld, player, 'Nothing'), player))
for x in range(len(dungeon_items)-1, -1, -1):
item = dungeon_items[x]
if ((multiworld.small_key_shuffle[player] == small_key_shuffle.option_start_with and item.type == 'SmallKey')
or (multiworld.big_key_shuffle[player] == big_key_shuffle.option_start_with and item.type == 'BigKey')
or (multiworld.compass_shuffle[player] == compass_shuffle.option_start_with and item.type == 'Compass')
or (multiworld.map_shuffle[player] == map_shuffle.option_start_with and item.type == 'Map')):
dungeon_items.pop(x)
multiworld.push_precollected(item)
else:
for x in range(len(dungeon_items)-1, -1, -1):
item = dungeon_items[x]
if ((multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_start_with and item.type == 'SmallKey')
or (multiworld.bigkey_shuffle[player] == bigkey_shuffle.option_start_with and item.type == 'BigKey')
or (multiworld.compass_shuffle[player] == compass_shuffle.option_start_with and item.type == 'Compass')
or (multiworld.map_shuffle[player] == map_shuffle.option_start_with and item.type == 'Map')):
dungeon_items.pop(x)
multiworld.push_precollected(item)
multiworld.itempool.append(ItemFactory(dungeon_item_replacements.pop(), player))
multiworld.itempool.extend([item for item in dungeon_items])
# logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
# rather than making all hearts/heart pieces progression items (which slows down generation considerably)
# We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
if multiworld.goal[player] != 'icerodhunt' and multiworld.difficulty[player] in ['easy', 'normal', 'hard'] and not (multiworld.custom and multiworld.customitemarray[30] == 0):
next(item for item in items if item.name == 'Boss Heart Container').classification = ItemClassification.progression
elif multiworld.goal[player] != 'icerodhunt' and multiworld.difficulty[player] in ['expert'] and not (multiworld.custom and multiworld.customitemarray[29] < 4):
adv_heart_pieces = (item for item in items if item.name == 'Piece of Heart')
for i in range(4):
next(adv_heart_pieces).classification = ItemClassification.progression
multiworld.itempool.append(ItemFactory(dungeon_item_replacements.pop(), player))
multiworld.itempool.extend([item for item in dungeon_items])
progressionitems = []
nonprogressionitems = []
for item in items:
if item.advancement or item.type:
progressionitems.append(item)
else:
nonprogressionitems.append(GetBeemizerItem(multiworld, item.player, item))
multiworld.random.shuffle(nonprogressionitems)
if additional_triforce_pieces:
if additional_triforce_pieces > len(nonprogressionitems):
raise FillError(f"Not enough non-progression items to replace with Triforce pieces found for player "
f"{multiworld.get_player_name(player)}.")
progressionitems += [ItemFactory("Triforce Piece", player) for _ in range(additional_triforce_pieces)]
nonprogressionitems.sort(key=lambda item: int("Heart" in item.name)) # try to keep hearts in the pool
nonprogressionitems = nonprogressionitems[additional_triforce_pieces:]
multiworld.random.shuffle(nonprogressionitems)
# shuffle medallions
if multiworld.required_medallions[player][0] == "random":
mm_medallion = multiworld.random.choice(['Ether', 'Quake', 'Bombos'])
else:
mm_medallion = multiworld.required_medallions[player][0]
if multiworld.required_medallions[player][1] == "random":
tr_medallion = multiworld.random.choice(['Ether', 'Quake', 'Bombos'])
else:
tr_medallion = multiworld.required_medallions[player][1]
multiworld.required_medallions[player] = (mm_medallion, tr_medallion)
place_bosses(world)
set_up_shops(multiworld, player)
if multiworld.shop_shuffle[player]:
shuffle_shops(multiworld, nonprogressionitems, player)
if multiworld.retro_bow[player]:
shop_items = 0
shop_locations = [location for shop_locations in (shop.region.locations for shop in multiworld.shops if
shop.type == ShopType.Shop and shop.region.player == player) for location in shop_locations if
location.shop_slot is not None]
for location in shop_locations:
if location.shop.inventory[location.shop_slot]["item"] == "Single Arrow":
location.place_locked_item(ItemFactory("Single Arrow", player))
else:
shop_items += 1
else:
shop_items = min(multiworld.shop_item_slots[player], 30 if multiworld.include_witch_hut[player] else 27)
multiworld.itempool += progressionitems + nonprogressionitems
if multiworld.shuffle_capacity_upgrades[player]:
shop_items += 2
chance_100 = int(multiworld.retro_bow[player]) * 0.25 + int(
multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal) * 0.5
for _ in range(shop_items):
if multiworld.random.random() < chance_100:
items.append(ItemFactory(GetBeemizerItem(multiworld, player, "Rupees (100)"), player))
else:
items.append(ItemFactory(GetBeemizerItem(multiworld, player, "Rupees (50)"), player))
multiworld.random.shuffle(items)
pool_count = len(items)
new_items = ["Triforce Piece" for _ in range(additional_triforce_pieces)]
if multiworld.shuffle_capacity_upgrades[player] or multiworld.bombless_start[player]:
progressive = multiworld.progressive[player]
progressive = multiworld.random.choice([True, False]) if progressive == 'grouped_random' else progressive == 'on'
if multiworld.shuffle_capacity_upgrades[player] == "on_combined":
new_items.append("Bomb Upgrade (50)")
elif multiworld.shuffle_capacity_upgrades[player] == "on":
new_items += ["Bomb Upgrade (+5)"] * 6
new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)")
if multiworld.shuffle_capacity_upgrades[player] != "on_combined" and multiworld.bombless_start[player]:
new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)")
if multiworld.shuffle_capacity_upgrades[player] and not multiworld.retro_bow[player]:
if multiworld.shuffle_capacity_upgrades[player] == "on_combined":
new_items += ["Arrow Upgrade (70)"]
else:
new_items += ["Arrow Upgrade (+5)"] * 6
new_items.append("Arrow Upgrade (+5)" if progressive else "Arrow Upgrade (+10)")
items += [ItemFactory(item, player) for item in new_items]
removed_filler = []
multiworld.random.shuffle(items) # Decide what gets tossed randomly.
while len(items) > pool_count:
for i, item in enumerate(items):
if item.classification in (ItemClassification.filler, ItemClassification.trap):
removed_filler.append(items.pop(i))
break
else:
# no more junk to remove, condense progressive items
def condense_items(items, small_item, big_item, rem, add):
small_item = ItemFactory(small_item, player)
# while (len(items) >= pool_count + rem - 1 # minus 1 to account for the replacement item
# and items.count(small_item) >= rem):
if items.count(small_item) >= rem:
for _ in range(rem):
items.remove(small_item)
removed_filler.append(ItemFactory(small_item.name, player))
items += [ItemFactory(big_item, player) for _ in range(add)]
return True
return False
def cut_item(items, item_to_cut, minimum_items):
item_to_cut = ItemFactory(item_to_cut, player)
if items.count(item_to_cut) > minimum_items:
items.remove(item_to_cut)
removed_filler.append(ItemFactory(item_to_cut.name, player))
return True
return False
while len(items) > pool_count:
items_were_cut = False
for reduce_item in items_reduction_table:
if len(items) <= pool_count:
break
if len(reduce_item) == 2:
items_were_cut = items_were_cut or cut_item(items, *reduce_item)
elif len(reduce_item) == 4:
items_were_cut = items_were_cut or condense_items(items, *reduce_item)
elif len(reduce_item) == 1: # Bottles
bottles = [item for item in items if item.name in item_name_groups["Bottles"]]
if len(bottles) > 4:
bottle = multiworld.random.choice(bottles)
items.remove(bottle)
removed_filler.append(bottle)
items_were_cut = True
assert items_were_cut, f"Failed to limit item pool size for player {player}"
if len(items) < pool_count:
items += removed_filler[len(items) - pool_count:]
if multiworld.randomize_cost_types[player]:
# Heart and Arrow costs require all Heart Container/Pieces and Arrow Upgrades to be advancement items for logic
for item in items:
if (item.name in ("Boss Heart Container", "Sanctuary Heart Container", "Piece of Heart")
or "Arrow Upgrade" in item.name):
item.classification = ItemClassification.progression
else:
# Otherwise, logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
# rather than making all hearts/heart pieces progression items (which slows down generation considerably)
# We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
if multiworld.item_pool[player] in ['easy', 'normal', 'hard'] and not (multiworld.custom and multiworld.customitemarray[30] == 0):
next(item for item in items if item.name == 'Boss Heart Container').classification = ItemClassification.progression
elif multiworld.item_pool[player] in ['expert'] and not (multiworld.custom and multiworld.customitemarray[29] < 4):
adv_heart_pieces = (item for item in items if item.name == 'Piece of Heart')
for i in range(4):
next(adv_heart_pieces).classification = ItemClassification.progression
multiworld.required_medallions[player] = (multiworld.misery_mire_medallion[player].current_key.title(),
multiworld.turtle_rock_medallion[player].current_key.title())
place_bosses(world)
multiworld.itempool += items
if multiworld.retro_caves[player]:
set_up_take_anys(multiworld, player) # depends on world.itempool to be set
# set_up_take_anys needs to run first
create_dynamic_shop_locations(multiworld, player)
take_any_locations = {
@@ -516,9 +552,14 @@ def set_up_take_anys(world, player):
sword = world.random.choice(swords)
world.itempool.remove(sword)
world.itempool.append(ItemFactory('Rupees (20)', player))
old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True)
old_man_take_any.shop.add_inventory(0, sword.name, 0, 0)
loc_name = "Old Man Sword Cave"
location = ALttPLocation(player, loc_name, shop_table_by_location[loc_name], parent=old_man_take_any)
location.shop_slot = 0
old_man_take_any.locations.append(location)
location.place_locked_item(sword)
else:
old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=True)
old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0)
for num in range(4):
take_any = LTTPRegion("Take-Any #{}".format(num+1), LTTPRegionType.Cave, 'a cave of choice', player, world)
@@ -532,18 +573,22 @@ def set_up_take_anys(world, player):
take_any.shop = TakeAny(take_any, room_id, 0xE3, True, True, total_shop_slots + num + 1)
world.shops.append(take_any.shop)
take_any.shop.add_inventory(0, 'Blue Potion', 0, 0)
take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=True)
take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0)
location = ALttPLocation(player, take_any.name, shop_table_by_location[take_any.name], parent=take_any)
location.shop_slot = 1
take_any.locations.append(location)
location.place_locked_item(ItemFactory("Boss Heart Container", player))
def get_pool_core(world, player: int):
shuffle = world.shuffle[player]
difficulty = world.difficulty[player]
timer = world.timer[player]
goal = world.goal[player]
mode = world.mode[player]
shuffle = world.entrance_shuffle[player].current_key
difficulty = world.item_pool[player].current_key
timer = world.timer[player].current_key
goal = world.goal[player].current_key
mode = world.mode[player].current_key
swordless = world.swordless[player]
retro_bow = world.retro_bow[player]
logic = world.logic[player]
logic = world.glitches_required[player]
pool = []
placed_items = {}
@@ -552,7 +597,7 @@ def get_pool_core(world, player: int):
treasure_hunt_count = None
treasure_hunt_icon = None
diff = ice_rod_hunt_difficulties[difficulty] if goal == 'icerodhunt' else difficulties[difficulty]
diff = difficulties[difficulty]
pool.extend(diff.alwaysitems)
def place_item(loc, item):
@@ -560,7 +605,7 @@ def get_pool_core(world, player: int):
placed_items[loc] = item
# provide boots to major glitch dependent seeds
if logic in {'owglitches', 'hybridglitches', 'nologic'} and world.glitch_boots[player] and goal != 'icerodhunt':
if logic in {'overworld_glitches', 'hybrid_major_glitches', 'no_logic'} and world.glitch_boots[player]:
precollected_items.append('Pegasus Boots')
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
@@ -611,7 +656,7 @@ def get_pool_core(world, player: int):
if want_progressives(world.random):
pool.extend(diff.progressivebow)
world.worlds[player].has_progressive_bows = True
elif (swordless or logic == 'noglitches') and goal != 'icerodhunt':
elif (swordless or logic == 'no_glitches'):
swordless_bows = ['Bow', 'Silver Bow']
if difficulty == "easy":
swordless_bows *= 2
@@ -627,21 +672,32 @@ def get_pool_core(world, player: int):
extraitems = total_items_to_place - len(pool) - len(placed_items)
if timer in ['timed', 'timed-countdown']:
if timer in ['timed', 'timed_countdown']:
pool.extend(diff.timedother)
extraitems -= len(diff.timedother)
clock_mode = 'stopwatch' if timer == 'timed' else 'countdown'
elif timer == 'timed-ohko':
elif timer == 'timed_ohko':
pool.extend(diff.timedohko)
extraitems -= len(diff.timedohko)
clock_mode = 'countdown-ohko'
additional_pieces_to_place = 0
if 'triforcehunt' in goal:
pieces_in_core = min(extraitems, world.triforce_pieces_available[player])
additional_pieces_to_place = world.triforce_pieces_available[player] - pieces_in_core
if 'triforce_hunt' in goal:
if world.triforce_pieces_mode[player].value == TriforcePiecesMode.option_extra:
triforce_pieces = world.triforce_pieces_available[player].value + world.triforce_pieces_extra[player].value
elif world.triforce_pieces_mode[player].value == TriforcePiecesMode.option_percentage:
percentage = float(max(100, world.triforce_pieces_percentage[player].value)) / 100
triforce_pieces = int(round(world.triforce_pieces_required[player].value * percentage, 0))
else: # available
triforce_pieces = world.triforce_pieces_available[player].value
triforce_pieces = max(triforce_pieces, world.triforce_pieces_required[player].value)
pieces_in_core = min(extraitems, triforce_pieces)
additional_pieces_to_place = triforce_pieces - pieces_in_core
pool.extend(["Triforce Piece"] * pieces_in_core)
extraitems -= pieces_in_core
treasure_hunt_count = world.triforce_pieces_required[player]
treasure_hunt_count = world.triforce_pieces_required[player].value
treasure_hunt_icon = 'Triforce Piece'
for extra in diff.extras:
@@ -659,12 +715,12 @@ def get_pool_core(world, player: int):
pool.remove("Rupees (20)")
if retro_bow:
replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)'}
replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)', 'Arrow Upgrade (50)'}
pool = ['Rupees (5)' if item in replace else item for item in pool]
if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
if world.small_key_shuffle[player] == small_key_shuffle.option_universal:
pool.extend(diff.universal_keys)
if mode == 'standard':
if world.key_drop_shuffle[player] and world.goal[player] != 'icerodhunt':
if world.key_drop_shuffle[player]:
key_locations = ['Secret Passage', 'Hyrule Castle - Map Guard Key Drop']
key_location = world.random.choice(key_locations)
key_locations.remove(key_location)
@@ -688,8 +744,8 @@ def get_pool_core(world, player: int):
def make_custom_item_pool(world, player):
shuffle = world.shuffle[player]
difficulty = world.difficulty[player]
shuffle = world.entrance_shuffle[player]
difficulty = world.item_pool[player]
timer = world.timer[player]
goal = world.goal[player]
mode = world.mode[player]
@@ -798,9 +854,9 @@ def make_custom_item_pool(world, player):
treasure_hunt_count = world.triforce_pieces_required[player]
treasure_hunt_icon = 'Triforce Piece'
if timer in ['display', 'timed', 'timed-countdown']:
clock_mode = 'countdown' if timer == 'timed-countdown' else 'stopwatch'
elif timer == 'timed-ohko':
if timer in ['display', 'timed', 'timed_countdown']:
clock_mode = 'countdown' if timer == 'timed_countdown' else 'stopwatch'
elif timer == 'timed_ohko':
clock_mode = 'countdown-ohko'
elif timer == 'ohko':
clock_mode = 'ohko'
@@ -810,7 +866,7 @@ def make_custom_item_pool(world, player):
itemtotal = itemtotal + 1
if mode == 'standard':
if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
if world.small_key_shuffle[player] == small_key_shuffle.option_universal:
key_location = world.random.choice(
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
@@ -833,7 +889,7 @@ def make_custom_item_pool(world, player):
pool.extend(['Magic Mirror'] * customitemarray[22])
pool.extend(['Moon Pearl'] * customitemarray[28])
if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
if world.small_key_shuffle[player] == small_key_shuffle.option_universal:
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in universal Mode
if world.key_drop_shuffle[player]:
itemtotal = itemtotal - (len(key_drop_data) - 1)

View File

@@ -112,13 +112,15 @@ item_table = {'Bow': ItemData(IC.progression, None, 0x0B, 'You have\nchosen the\
'Crystal 7': ItemData(IC.progression, 'Crystal', (0x08, 0x34, 0x64, 0x40, 0x7C, 0x06), None, None, None, None, None, None, "a blue crystal"),
'Single Arrow': ItemData(IC.filler, None, 0x43, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'),
'Arrows (10)': ItemData(IC.filler, None, 0x44, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack','stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again','ten arrows'),
'Arrow Upgrade (+10)': ItemData(IC.filler, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Arrow Upgrade (+5)': ItemData(IC.filler, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Arrow Upgrade (+10)': ItemData(IC.useful, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Arrow Upgrade (+5)': ItemData(IC.useful, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Arrow Upgrade (70)': ItemData(IC.useful, None, 0x4D, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
'Single Bomb': ItemData(IC.filler, None, 0x27, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'),
'Bombs (3)': ItemData(IC.filler, None, 0x28, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'),
'Bombs (10)': ItemData(IC.filler, None, 0x31, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'),
'Bomb Upgrade (+10)': ItemData(IC.filler, None, 0x52, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Bomb Upgrade (+5)': ItemData(IC.filler, None, 0x51, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Bomb Upgrade (+10)': ItemData(IC.progression, None, 0x52, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Bomb Upgrade (+5)': ItemData(IC.progression, None, 0x51, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Bomb Upgrade (50)': ItemData(IC.progression, None, 0x4C, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),
'Blue Mail': ItemData(IC.useful, None, 0x22, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the Blue Mail'),
'Red Mail': ItemData(IC.useful, None, 0x23, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the Red Mail'),
'Progressive Mail': ItemData(IC.useful, None, 0x60, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'),
@@ -222,6 +224,7 @@ item_table = {'Bow': ItemData(IC.progression, None, 0x0B, 'You have\nchosen the\
'Return Smith': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None),
'Pick Up Purple Chest': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None),
'Open Floodgate': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None),
'Capacity Upgrade Shop': ItemData(IC.progression, 'Event', None, None, None, None, None, None, None, None),
}
item_init_table = {name: data.as_init_dict() for name, data in item_table.items()}
@@ -287,5 +290,5 @@ progression_items = {name for name in everything if
item_table[name].classification in {IC.progression, IC.progression_skip_balancing}}
item_name_groups['Progression Items'] = progression_items
item_name_groups['Non Progression Items'] = everything - progression_items
item_name_groups['Upgrades'] = {name for name in everything if 'Upgrade' in name}
trap_replaceable = item_name_groups['Rupees'] | {'Arrows (10)', 'Single Bomb', 'Bombs (3)', 'Bombs (10)', 'Nothing'}

View File

@@ -1,11 +1,18 @@
import typing
from BaseClasses import MultiWorld
from Options import Choice, DeathLink, DefaultOnToggle, ItemsAccessibility, Option, PlandoBosses, Range, \
StartInventoryPool, Toggle
from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink, StartInventoryPool, PlandoBosses,\
FreeText, ItemsAccessibility
class Logic(Choice):
class GlitchesRequired(Choice):
"""Determine the logic required to complete the seed
None: No glitches required
Minor Glitches: Puts fake flipper, waterwalk, super bunny shenanigans, and etc into logic
Overworld Glitches: Assumes the player has knowledge of both overworld major glitches (boots clips, mirror clips) and minor glitches
Hybrid Major Glitches: In addition to overworld glitches, also requires underworld clips between dungeons.
No Logic: Your own items are placed with no regard to any logic; such as your Fire Rod can be on your Trinexx."""
display_name = "Glitches Required"
option_no_glitches = 0
option_minor_glitches = 1
option_overworld_glitches = 2
@@ -13,20 +20,122 @@ class Logic(Choice):
option_no_logic = 4
alias_owg = 2
alias_hmg = 3
alias_none = 0
class Objective(Choice):
option_crystals = 0
# option_pendants = 1
option_triforce_pieces = 2
option_pedestal = 3
option_bingo = 4
class DarkRoomLogic(Choice):
"""Logic for unlit dark rooms. Lamp: require the Lamp for these rooms to be considered accessible.
Torches: in addition to lamp, allow the fire rod and presence of easily accessible torches for access.
None: all dark rooms are always considered doable, meaning this may force completion of rooms in complete darkness."""
display_name = "Dark Room Logic"
option_lamp = 0
option_torches = 1
option_none = 2
default = 0
class Goal(Choice):
option_kill_ganon = 0
option_kill_ganon_and_gt_agahnim = 1
option_hand_in = 2
"""Ganon: Climb GT, defeat Agahnim 2, and then kill Ganon
Crystals: Only killing Ganon is required. However, items may still be placed in GT
Bosses: Defeat the boss of all dungeons, including Agahnim's tower and GT (Aga 2)
Pedestal: Pull the Triforce from the Master Sword pedestal
Ganon Pedestal: Pull the Master Sword pedestal, then kill Ganon
Triforce Hunt: Collect Triforce pieces spread throughout the worlds, then turn them in to Murahadala in front of Hyrule Castle
Local Triforce Hunt: Collect Triforce pieces spread throughout your world, then turn them in to Murahadala in front of Hyrule Castle
Ganon Triforce Hunt: Collect Triforce pieces spread throughout the worlds, then kill Ganon
Local Ganon Triforce Hunt: Collect Triforce pieces spread throughout your world, then kill Ganon
Ice Rod Hunt: You start with everything except Ice Rod. Find the Ice rod, then kill Trinexx at Turtle rock."""
display_name = "Goal"
default = 0
option_ganon = 0
option_crystals = 1
option_bosses = 2
option_pedestal = 3
option_ganon_pedestal = 4
option_triforce_hunt = 5
option_local_triforce_hunt = 6
option_ganon_triforce_hunt = 7
option_local_ganon_triforce_hunt = 8
class EntranceShuffle(Choice):
"""Dungeons Simple: Shuffle just dungeons amongst each other, swapping dungeons entirely, so Hyrule Castle is always 1 dungeon.
Dungeons Full: Shuffle any dungeon entrance with any dungeon interior, so Hyrule Castle can be 4 different dungeons, but keep dungeons to a specific world.
Dungeons Crossed: like dungeons_full, but allow cross-world traversal through a dungeon. Warning: May force repeated dungeon traversal.
Simple: Entrances are grouped together before being randomized. Interiors with two entrances are grouped shuffled together with each other,
and Death Mountain entrances are shuffled only on Death Mountain. Dungeons are swapped entirely.
Restricted: Like Simple, but single entrance interiors, multi entrance interiors, and Death Mountain interior entrances are all shuffled with each other.
Full: Like Restricted, but all Dungeon entrances are shuffled with all non-Dungeon entrances.
Crossed: Like Full, but interiors with multiple entrances are no longer confined to the same world, which may allow crossing worlds.
Insanity: Like Crossed, but entrances and exits may be decoupled from each other, so that leaving through an exit may not return you to the entrance you entered from."""
display_name = "Entrance Shuffle"
default = 0
alias_none = 0
option_vanilla = 0
option_dungeons_simple = 1
option_dungeons_full = 2
option_dungeons_crossed = 3
option_simple = 4
option_restricted = 5
option_full = 6
option_crossed = 7
option_insanity = 8
alias_dungeonssimple = 1
alias_dungeonsfull = 2
alias_dungeonscrossed = 3
class EntranceShuffleSeed(FreeText):
"""You can specify a number to use as an entrance shuffle seed, or a group name. Everyone with the same group name
will get the same entrance shuffle result as long as their Entrance Shuffle, Mode, Retro Caves, and Glitches
Required options are the same."""
default = "random"
display_name = "Entrance Shuffle Seed"
class TriforcePiecesMode(Choice):
"""Determine how to calculate the extra available triforce pieces.
Extra: available = triforce_pieces_extra + triforce_pieces_required
Percentage: available = (triforce_pieces_percentage /100) * triforce_pieces_required
Available: available = triforce_pieces_available"""
display_name = "Triforce Pieces Mode"
default = 2
option_extra = 0
option_percentage = 1
option_available = 2
class TriforcePiecesPercentage(Range):
"""Set to how many triforce pieces according to a percentage of the required ones, are available to collect in the world."""
display_name = "Triforce Pieces Percentage"
range_start = 100
range_end = 1000
default = 150
class TriforcePiecesAvailable(Range):
"""Set to how many triforces pieces are available to collect in the world. Default is 30. Max is 90, Min is 1"""
display_name = "Triforce Pieces Available"
range_start = 1
range_end = 90
default = 30
class TriforcePiecesRequired(Range):
"""Set to how many out of X triforce pieces you need to win the game in a triforce hunt.
Default is 20. Max is 90, Min is 1."""
display_name = "Triforce Pieces Required"
range_start = 1
range_end = 90
default = 20
class TriforcePiecesExtra(Range):
"""Set to how many extra triforces pieces are available to collect in the world."""
display_name = "Triforce Pieces Extra"
range_start = 0
range_end = 89
default = 10
class OpenPyramid(Choice):
@@ -45,10 +154,10 @@ class OpenPyramid(Choice):
def to_bool(self, world: MultiWorld, player: int) -> bool:
if self.value == self.option_goal:
return world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'}
return world.goal[player] in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'}
elif self.value == self.option_auto:
return world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'} \
and (world.shuffle[player] in {'vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed'} or not
return world.goal[player] in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'} \
and (world.entrance_shuffle[player] in {'vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed'} or not
world.shuffle_ganon)
elif self.value == self.option_open:
return True
@@ -77,13 +186,13 @@ class DungeonItem(Choice):
return self.value in {1, 2, 3, 4}
class bigkey_shuffle(DungeonItem):
class big_key_shuffle(DungeonItem):
"""Big Key Placement"""
item_name_group = "Big Keys"
display_name = "Big Key Shuffle"
class smallkey_shuffle(DungeonItem):
class small_key_shuffle(DungeonItem):
"""Small Key Placement"""
option_universal = 5
item_name_group = "Small Keys"
@@ -108,6 +217,149 @@ class key_drop_shuffle(Toggle):
display_name = "Key Drop Shuffle"
class DungeonCounters(Choice):
"""On: Always display amount of items checked in a dungeon. Pickup: Show when compass is picked up.
Default: Show when compass is picked up if the compass itself is shuffled. Off: Never show item count in dungeons."""
display_name = "Dungeon Counters"
default = 1
option_on = 0
option_pickup = 1
option_default = 2
option_off = 4
class Mode(Choice):
"""Standard: Begin the game by rescuing Zelda from her cell and escorting her to the Sanctuary
Open: Begin the game from your choice of Link's House or the Sanctuary
Inverted: Begin in the Dark World. The Moon Pearl is required to avoid bunny-state in Light World, and the Light World game map is altered"""
option_standard = 0
option_open = 1
option_inverted = 2
default = 1
display_name = "Mode"
class ItemPool(Choice):
"""Easy: Doubled upgrades, progressives, and etc. Normal: Item availability remains unchanged from vanilla game.
Hard: Reduced upgrade availability (max: 14 hearts, blue mail, tempered sword, fire shield, no silvers unless swordless).
Expert: Minimum upgrade availability (max: 8 hearts, green mail, master sword, fighter shield, no silvers unless swordless)."""
display_name = "Item Pool"
default = 1
option_easy = 0
option_normal = 1
option_hard = 2
option_expert = 3
class ItemFunctionality(Choice):
"""Easy: Allow Hammer to damage ganon, Allow Hammer tablet collection, Allow swordless medallion use everywhere.
Normal: Vanilla item functionality
Hard: Reduced helpfulness of items (potions less effective, can't catch faeries, cape uses double magic, byrna does not grant invulnerability, boomerangs do not stun, silvers disabled outside ganon)
Expert: Vastly reduces the helpfulness of items (potions barely effective, can't catch faeries, cape uses double magic, byrna does not grant invulnerability, boomerangs and hookshot do not stun, silvers disabled outside ganon)"""
display_name = "Item Functionality"
default = 1
option_easy = 0
option_normal = 1
option_hard = 2
option_expert = 3
class EnemyHealth(Choice):
"""Default: Vanilla enemy HP. Easy: Enemies have reduced health. Hard: Enemies have increased health.
Expert: Enemies have greatly increased health."""
display_name = "Enemy Health"
default = 1
option_easy = 0
option_default = 1
option_hard = 2
option_expert = 3
class EnemyDamage(Choice):
"""Default: Vanilla enemy damage. Shuffled: 0 # Enemies deal 0 to 4 hearts and armor helps.
Chaos: Enemies deal 0 to 8 hearts and armor just reshuffles the damage."""
display_name = "Enemy Damage"
default = 0
option_default = 0
option_shuffled = 2
option_chaos = 3
class ShufflePrizes(Choice):
"""Shuffle "general" prize packs, as in enemy, tree pull, dig etc.; "bonk" prizes; or both."""
display_name = "Shuffle Prizes"
default = 1
option_off = 0
option_general = 1
option_bonk = 2
option_both = 3
class Medallion(Choice):
default = "random"
option_ether = 0
option_bombos = 1
option_quake = 2
class MiseryMireMedallion(Medallion):
"""Required medallion to open Misery Mire front entrance."""
display_name = "Misery Mire Medallion"
class TurtleRockMedallion(Medallion):
"""Required medallion to open Turtle Rock front entrance."""
display_name = "Turtle Rock Medallion"
class Timer(Choice):
"""None: No timer will be displayed. OHKO: Timer always at zero. Permanent OHKO.
Timed: Starts with clock at zero. Green clocks subtract 4 minutes (total 20). Blue clocks subtract 2 minutes (total 10). Red clocks add two minutes (total 10). Winner is the player with the lowest time at the end.
Timed OHKO: Starts the clock at ten minutes. Green clocks add five minutes (total 25). As long as the clock as at zero, Link will die in one hit.
Timed Countdown: Starts the clock with forty minutes. Same clocks as timed mode, but if the clock hits zero you lose. You can still keep playing, though.
Display: Displays a timer, but otherwise does not affect gameplay or the item pool."""
display_name = "Timer"
option_none = 0
option_timed = 1
option_timed_ohko = 2
option_ohko = 3
option_timed_countdown = 4
option_display = 5
default = 0
class CountdownStartTime(Range):
"""For Timed OHKO and Timed Countdown timer modes, the amount of time in minutes to start with."""
display_name = "Countdown Start Time"
range_start = 0
range_end = 480
default = 10
class ClockTime(Range):
range_start = -60
range_end = 60
class RedClockTime(ClockTime):
"""For all timer modes, the amount of time in minutes to gain or lose when picking up a red clock."""
display_name = "Red Clock Time"
default = -2
class BlueClockTime(ClockTime):
"""For all timer modes, the amount of time in minutes to gain or lose when picking up a blue clock."""
display_name = "Blue Clock Time"
default = 2
class GreenClockTime(ClockTime):
"""For all timer modes, the amount of time in minutes to gain or lose when picking up a green clock."""
display_name = "Green Clock Time"
default = 4
class Crystals(Range):
range_start = 0
range_end = 7
@@ -138,18 +390,52 @@ class ShopItemSlots(Range):
range_end = 30
class RandomizeShopInventories(Choice):
"""Generate new default inventories for overworld/underworld shops, and unique shops; or each shop independently"""
display_name = "Randomize Shop Inventories"
default = 0
option_default = 0
option_randomize_by_shop_type = 1
option_randomize_each = 2
class ShuffleShopInventories(Toggle):
"""Shuffle default inventories of the shops around"""
display_name = "Shuffle Shop Inventories"
class RandomizeShopPrices(Toggle):
"""Randomize the prices of the items in shop inventories"""
display_name = "Randomize Shop Prices"
class RandomizeCostTypes(Toggle):
"""Prices of the items in shop inventories may cost hearts, arrow, or bombs instead of rupees"""
display_name = "Randomize Cost Types"
class ShopPriceModifier(Range):
"""Percentage modifier for shuffled item prices in shops"""
display_name = "Shop Price Cost Percent"
display_name = "Shop Price Modifier"
range_start = 0
default = 100
range_end = 400
class WorldState(Choice):
option_standard = 1
option_open = 0
option_inverted = 2
class IncludeWitchHut(Toggle):
"""Consider witch's hut like any other shop and shuffle/randomize it too"""
display_name = "Include Witch's Hut"
class ShuffleCapacityUpgrades(Choice):
"""Shuffle capacity upgrades into the item pool (and allow them to traverse the multiworld).
On Combined will shuffle only a single bomb upgrade and arrow upgrade each which bring you to the maximum capacity."""
display_name = "Shuffle Capacity Upgrades"
option_off = 0
option_on = 1
option_on_combined = 2
alias_false = 0
alias_true = 1
class LTTPBosses(PlandoBosses):
@@ -237,6 +523,11 @@ class Swordless(Toggle):
display_name = "Swordless"
class BomblessStart(Toggle):
"""Start with a max of 0 bombs available, requiring Bomb Upgrade items in order to use bombs"""
display_name = "Bombless Start"
# Might be a decent idea to split "Bow" into its own option with choices of
# Defer to Progressive Option (default), Progressive, Non-Progressive, Bow + Silvers, Retro
class RetroBow(Toggle):
@@ -435,29 +726,66 @@ class AllowCollect(Toggle):
alttp_options: typing.Dict[str, type(Option)] = {
"accessibility": ItemsAccessibility,
"start_inventory_from_pool": StartInventoryPool,
"goal": Goal,
"mode": Mode,
"glitches_required": GlitchesRequired,
"dark_room_logic": DarkRoomLogic,
"open_pyramid": OpenPyramid,
"crystals_needed_for_gt": CrystalsTower,
"crystals_needed_for_ganon": CrystalsGanon,
"open_pyramid": OpenPyramid,
"bigkey_shuffle": bigkey_shuffle,
"smallkey_shuffle": smallkey_shuffle,
"triforce_pieces_mode": TriforcePiecesMode,
"triforce_pieces_percentage": TriforcePiecesPercentage,
"triforce_pieces_required": TriforcePiecesRequired,
"triforce_pieces_available": TriforcePiecesAvailable,
"triforce_pieces_extra": TriforcePiecesExtra,
"entrance_shuffle": EntranceShuffle,
"entrance_shuffle_seed": EntranceShuffleSeed,
"big_key_shuffle": big_key_shuffle,
"small_key_shuffle": small_key_shuffle,
"key_drop_shuffle": key_drop_shuffle,
"compass_shuffle": compass_shuffle,
"map_shuffle": map_shuffle,
"restrict_dungeon_item_on_boss": RestrictBossItem,
"item_pool": ItemPool,
"item_functionality": ItemFunctionality,
"enemy_health": EnemyHealth,
"enemy_damage": EnemyDamage,
"progressive": Progressive,
"swordless": Swordless,
"dungeon_counters": DungeonCounters,
"retro_bow": RetroBow,
"retro_caves": RetroCaves,
"hints": Hints,
"scams": Scams,
"restrict_dungeon_item_on_boss": RestrictBossItem,
"boss_shuffle": LTTPBosses,
"pot_shuffle": PotShuffle,
"enemy_shuffle": EnemyShuffle,
"killable_thieves": KillableThieves,
"bush_shuffle": BushShuffle,
"shop_item_slots": ShopItemSlots,
"randomize_shop_inventories": RandomizeShopInventories,
"shuffle_shop_inventories": ShuffleShopInventories,
"include_witch_hut": IncludeWitchHut,
"randomize_shop_prices": RandomizeShopPrices,
"randomize_cost_types": RandomizeCostTypes,
"shop_price_modifier": ShopPriceModifier,
"shuffle_capacity_upgrades": ShuffleCapacityUpgrades,
"bombless_start": BomblessStart,
"shuffle_prizes": ShufflePrizes,
"tile_shuffle": TileShuffle,
"misery_mire_medallion": MiseryMireMedallion,
"turtle_rock_medallion": TurtleRockMedallion,
"glitch_boots": GlitchBoots,
"beemizer_total_chance": BeemizerTotalChance,
"beemizer_trap_chance": BeemizerTrapChance,
"timer": Timer,
"countdown_start_time": CountdownStartTime,
"red_clock_time": RedClockTime,
"blue_clock_time": BlueClockTime,
"green_clock_time": GreenClockTime,
"death_link": DeathLink,
"allow_collect": AllowCollect,
"ow_palettes": OWPalette,
"uw_palettes": UWPalette,
"hud_palettes": HUDPalette,
@@ -471,10 +799,4 @@ alttp_options: typing.Dict[str, type(Option)] = {
"music": Music,
"reduceflashing": ReduceFlashing,
"triforcehud": TriforceHud,
"glitch_boots": GlitchBoots,
"beemizer_total_chance": BeemizerTotalChance,
"beemizer_trap_chance": BeemizerTrapChance,
"death_link": DeathLink,
"allow_collect": AllowCollect,
"start_inventory_from_pool": StartInventoryPool,
}

View File

@@ -94,7 +94,7 @@ def create_regions(world, player):
create_cave_region(world, player, 'Kakariko Gamble Game', 'a game of chance'),
create_cave_region(world, player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
create_lw_region(world, player, 'Lake Hylia Island', ['Lake Hylia Island']),
create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies'),
create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade Shop']),
create_cave_region(world, player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
create_lw_region(world, player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']),
create_cave_region(world, player, '50 Rupee Cave', 'a cave with some cash'),
@@ -121,8 +121,9 @@ def create_regions(world, player):
['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']),
create_dungeon_region(world, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
create_dungeon_region(world, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross', 'Sewers - Key Rat Key Drop'], ['Sewers Door']),
create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit', None, ['Sanctuary Push Door', 'Sewers Back Door', 'Sewers Secret Room']),
create_dungeon_region(world, player, 'Sewers Secret Room', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
'Sewers - Secret Room - Right']),
create_dungeon_region(world, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
create_dungeon_region(world, player, 'Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze', 'Castle Tower - Dark Archer Key Drop', 'Castle Tower - Circle of Pots Key Drop'], ['Agahnim 1', 'Agahnims Tower Exit']),
create_dungeon_region(world, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
@@ -275,7 +276,9 @@ def create_regions(world, player):
create_cave_region(world, player, 'Hookshot Cave', 'a connector',
['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right',
'Hookshot Cave - Bottom Left'],
['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']),
['Hookshot Cave Exit (South)', 'Hookshot Cave Bomb Wall (South)']),
create_cave_region(world, player, 'Hookshot Cave (Upper)', 'a connector', None, ['Hookshot Cave Exit (North)',
'Hookshot Cave Bomb Wall (North)']),
create_dw_region(world, player, 'Death Mountain Floating Island (Dark World)', None,
['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']),
create_lw_region(world, player, 'Death Mountain Floating Island (Light World)', ['Floating Island']),
@@ -311,8 +314,8 @@ def create_regions(world, player):
create_dungeon_region(world, player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest', 'Skull Woods - West Lobby Pot Key'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
create_dungeon_region(world, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
create_dungeon_region(world, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop', 'Skull Woods - Boss', 'Skull Woods - Prize']),
create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop'], ['Ice Palace (Second Section)', 'Ice Palace Exit']),
create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Main)']),
create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', ['Ice Palace - Jelly Key Drop', 'Ice Palace - Compass Chest'], ['Ice Palace (Second Section)', 'Ice Palace Exit']),
create_dungeon_region(world, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Palace (Main)']),
create_dungeon_region(world, player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Freezor Chest',
'Ice Palace - Many Pots Pot Key',
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
@@ -735,6 +738,7 @@ location_table: typing.Dict[str,
'Missing Smith': (None, None, False, None),
'Dark Blacksmith Ruins': (None, None, False, None),
'Flute Activation Spot': (None, None, False, None),
'Capacity Upgrade Shop': (None, None, False, None),
'Eastern Palace - Prize': ([0x1209D, 0x53EF8, 0x53EF9, 0x180052, 0x18007C, 0xC6FE], None, True, 'Eastern Palace'),
'Desert Palace - Prize': ([0x1209E, 0x53F1C, 0x53F1D, 0x180053, 0x180078, 0xC6FF], None, True, 'Desert Palace'),
'Tower of Hera - Prize': (

View File

@@ -4,7 +4,7 @@ import Utils
import worlds.Files
LTTPJPN10HASH: str = "03a63945398191337e896e5771f77173"
RANDOMIZERBASEHASH: str = "9952c2a3ec1b421e408df0d20c8f0c7f"
RANDOMIZERBASEHASH: str = "35d010bc148e0ea0ee68e81e330223f1"
ROM_PLAYER_LIMIT: int = 255
import io
@@ -36,7 +36,7 @@ from .Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmith
SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
from .Items import ItemFactory, item_table, item_name_groups, progression_items
from .EntranceShuffle import door_addresses
from .Options import smallkey_shuffle
from .Options import small_key_shuffle
try:
from maseya import z3pr
@@ -294,7 +294,7 @@ def patch_enemizer(world, rom: LocalRom, enemizercli, output_directory):
'RandomizeBushEnemyChance': multiworld.bush_shuffle[player].value,
'RandomizeEnemyHealthRange': multiworld.enemy_health[player] != 'default',
'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[
multiworld.enemy_health[player]],
multiworld.enemy_health[player].current_key],
'OHKO': False,
'RandomizeEnemyDamage': multiworld.enemy_damage[player] != 'default',
'AllowEnemyZeroDamage': True,
@@ -858,13 +858,13 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# Thanks to Zarby89 for originally finding these values
# todo fix screen scrolling
if world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness_legacy'} and \
if world.entrance_shuffle[player] != 'insanity' and \
exit.name in {'Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit',
'Skull Woods Final Section Exit', 'Ice Palace Exit', 'Misery Mire Exit',
'Palace of Darkness Exit', 'Swamp Palace Exit', 'Ganons Tower Exit',
'Desert Palace Exit (North)', 'Agahnims Tower Exit', 'Spiral Cave Exit (Top)',
'Superbunny Cave Exit (Bottom)', 'Turtle Rock Ledge Exit (East)'} and \
(world.logic[player] not in ['hybridglitches', 'nologic'] or
(world.glitches_required[player] not in ['hybrid_major_glitches', 'no_logic'] or
exit.name not in {'Palace of Darkness Exit', 'Tower of Hera Exit', 'Swamp Palace Exit'}):
# For exits that connot be reached from another, no need to apply offset fixes.
rom.write_int16(0x15DB5 + 2 * offset, link_y) # same as final else
@@ -907,7 +907,9 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
if world.retro_caves[player]: # Old man cave and Take any caves will count towards collection rate.
credits_total += 5
if world.shop_item_slots[player]: # Potion shop only counts towards collection rate if included in the shuffle.
credits_total += 30 if 'w' in world.shop_shuffle[player] else 27
credits_total += 30 if world.include_witch_hut[player] else 27
if world.shuffle_capacity_upgrades[player]:
credits_total += 2
rom.write_byte(0x187010, credits_total) # dynamic credits
@@ -1059,7 +1061,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# Set stun items
rom.write_byte(0x180180, 0x03) # All standard items
# Set overflow items for progressive equipment
if world.timer[player] in ['timed', 'timed-countdown', 'timed-ohko']:
if world.timer[player] in ['timed', 'timed_countdown', 'timed_ohko']:
overflow_replacement = GREEN_CLOCK
else:
overflow_replacement = GREEN_TWENTY_RUPEES
@@ -1079,7 +1081,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
difficulty.progressive_bow_limit, item_table[difficulty.basicbow[-1]].item_code])
if difficulty.progressive_bow_limit < 2 and (
world.swordless[player] or world.logic[player] == 'noglitches'):
world.swordless[player] or world.glitches_required[player] == 'no_glitches'):
rom.write_bytes(0x180098, [2, item_table["Silver Bow"].item_code])
rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon
rom.write_byte(0x180182, 0x00) # Don't auto equip silvers on pickup
@@ -1095,7 +1097,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
prize_replacements[0xE1] = 0xDA # 5 Arrows -> Blue Rupee
prize_replacements[0xE2] = 0xDB # 10 Arrows -> Red Rupee
if "g" in world.shuffle_prizes[player]:
if world.shuffle_prizes[player] in ("general", "both"):
# shuffle prize packs
prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA, 0xE0,
0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF,
@@ -1157,7 +1159,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
byte = int(rom.read_byte(address))
rom.write_byte(address, prize_replacements.get(byte, byte))
if "b" in world.shuffle_prizes[player]:
if world.shuffle_prizes[player] in ("bonk", "both"):
# set bonk prizes
bonk_prizes = [0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xAC, 0xE3, 0xE3, 0xDA, 0xE3, 0xDA, 0xD8, 0xAC,
0xAC, 0xE3, 0xD8, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xDC, 0xDB, 0xE3, 0xDA, 0x79, 0x79,
@@ -1274,7 +1276,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
gametype = 0x04 # item
if world.shuffle[player] != 'vanilla':
if world.entrance_shuffle[player] != 'vanilla':
gametype |= 0x02 # entrance
if enemized:
gametype |= 0x01 # enemizer
@@ -1312,7 +1314,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
equip[0x36C] = 0x18
equip[0x36D] = 0x18
equip[0x379] = 0x68
starting_max_bombs = 10
starting_max_bombs = 0 if world.bombless_start[player] else 10
starting_max_arrows = 30
startingstate = CollectionState(world)
@@ -1430,8 +1432,8 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
'Bottle (Fairy)': 6, 'Bottle (Bee)': 7, 'Bottle (Good Bee)': 8}
rupees = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)': 50, 'Rupees (100)': 100,
'Rupees (300)': 300}
bomb_caps = {'Bomb Upgrade (+5)': 5, 'Bomb Upgrade (+10)': 10}
arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10}
bomb_caps = {'Bomb Upgrade (+5)': 5, 'Bomb Upgrade (+10)': 10, 'Bomb Upgrade (50)': 50}
arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10, 'Arrow Upgrade (70)': 70}
bombs = {'Single Bomb': 1, 'Bombs (3)': 3, 'Bombs (10)': 10}
arrows = {'Single Arrow': 1, 'Arrows (10)': 10}
@@ -1498,7 +1500,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_byte(0x3A96D, 0xF0 if world.mode[
player] != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader))
if 'u' in world.shop_shuffle[player]:
if world.shuffle_capacity_upgrades[player]:
rom.write_bytes(0x180080,
[5, 10, 5, 10]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10)
else:
@@ -1509,11 +1511,11 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
(0x02 if 'bombs' in world.escape_assist[player] else 0x00) |
(0x04 if 'magic' in world.escape_assist[player] else 0x00))) # Escape assist
if world.goal[player] in ['pedestal', 'triforcehunt', 'localtriforcehunt', 'icerodhunt']:
if world.goal[player] in ['pedestal', 'triforce_hunt', 'local_triforce_hunt']:
rom.write_byte(0x18003E, 0x01) # make ganon invincible
elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
elif world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
rom.write_byte(0x18003E, 0x05) # make ganon invincible until enough triforce pieces are collected
elif world.goal[player] in ['ganonpedestal']:
elif world.goal[player] in ['ganon_pedestal']:
rom.write_byte(0x18003E, 0x06)
elif world.goal[player] in ['bosses']:
rom.write_byte(0x18003E, 0x02) # make ganon invincible until all bosses are beat
@@ -1534,12 +1536,12 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# c - enabled for inside compasses
# s - enabled for inside small keys
# block HC upstairs doors in rain state in standard mode
rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' else 0x00)
rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.entrance_shuffle[player] != 'vanilla' else 0x00)
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.smallkey_shuffle[player] else 0x00)
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.small_key_shuffle[player] else 0x00)
| (0x02 if world.compass_shuffle[player] else 0x00)
| (0x04 if world.map_shuffle[player] else 0x00)
| (0x08 if world.bigkey_shuffle[
| (0x08 if world.big_key_shuffle[
player] else 0x00))) # free roaming item text boxes
rom.write_byte(0x18003B, 0x01 if world.map_shuffle[player] else 0x00) # maps showing crystals on overworld
@@ -1561,9 +1563,9 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# b - Big Key
# a - Small Key
#
rom.write_byte(0x180045, ((0x00 if (world.smallkey_shuffle[player] == smallkey_shuffle.option_original_dungeon or
world.smallkey_shuffle[player] == smallkey_shuffle.option_universal) else 0x01)
| (0x02 if world.bigkey_shuffle[player] else 0x00)
rom.write_byte(0x180045, ((0x00 if (world.small_key_shuffle[player] == small_key_shuffle.option_original_dungeon or
world.small_key_shuffle[player] == small_key_shuffle.option_universal) else 0x01)
| (0x02 if world.big_key_shuffle[player] else 0x00)
| (0x04 if world.map_shuffle[player] else 0x00)
| (0x08 if world.compass_shuffle[player] else 0x00))) # free roaming items in menu
@@ -1595,8 +1597,8 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_int16(0x18017C, get_reveal_bytes('Crystal 5') | get_reveal_bytes('Crystal 6') if world.map_shuffle[
player] else 0x0000) # Bomb Shop Reveal
rom.write_byte(0x180172, 0x01 if world.smallkey_shuffle[
player] == smallkey_shuffle.option_universal else 0x00) # universal keys
rom.write_byte(0x180172, 0x01 if world.small_key_shuffle[
player] == small_key_shuffle.option_universal else 0x00) # universal keys
rom.write_byte(0x18637E, 0x01 if world.retro_bow[player] else 0x00) # Skip quiver in item shops once bought
rom.write_byte(0x180175, 0x01 if world.retro_bow[player] else 0x00) # rupee bow
rom.write_byte(0x180176, 0x0A if world.retro_bow[player] else 0x00) # wood arrow cost
@@ -1613,9 +1615,9 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_byte(0x180020, digging_game_rng)
rom.write_byte(0xEFD95, digging_game_rng)
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix
rom.write_byte(0x186383, 0x01 if world.glitch_triforce or world.logic[
player] == 'nologic' else 0x00) # disable glitching to Triforce from Ganons Room
rom.write_byte(0x1800A4, 0x01 if world.glitches_required[player] != 'no_logic' else 0x00) # enable POD EG fix
rom.write_byte(0x186383, 0x01 if world.glitch_triforce or world.glitches_required[
player] == 'no_logic' else 0x00) # disable glitching to Triforce from Ganons Room
rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
# remove shield from uncle
@@ -1660,8 +1662,8 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
0x4F])
# allow smith into multi-entrance caves in appropriate shuffles
if world.shuffle[player] in ['restricted', 'full', 'crossed', 'insanity', 'madness'] or (
world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'):
if world.entrance_shuffle[player] in ['restricted', 'full', 'crossed', 'insanity'] or (
world.entrance_shuffle[player] == 'simple' and world.mode[player] == 'inverted'):
rom.write_byte(0x18004C, 0x01)
# set correct flag for hera basement item
@@ -1758,8 +1760,8 @@ def write_custom_shops(rom, world, player):
if item is None:
break
if world.shop_item_slots[player] or shop.type == ShopType.TakeAny:
count_shop = (shop.region.name != 'Potion Shop' or 'w' in world.shop_shuffle[player]) and \
shop.region.name != 'Capacity Upgrade'
count_shop = (shop.region.name != 'Potion Shop' or world.include_witch_hut[player]) and \
(shop.region.name != 'Capacity Upgrade' or world.shuffle_capacity_upgrades[player])
rom.write_byte(0x186560 + shop.sram_offset + slot, 1 if count_shop else 0)
if item['item'] == 'Single Arrow' and item['player'] == 0:
arrow_mask |= 1 << index
@@ -2201,7 +2203,7 @@ def write_strings(rom, world, player):
tt.removeUnwantedText()
# Let's keep this guy's text accurate to the shuffle setting.
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed']:
tt['kakariko_flophouse_man_no_flippers'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
tt['kakariko_flophouse_man'] = 'I really hate mowing my yard.\n{PAGEBREAK}\nI should move.'
@@ -2255,7 +2257,7 @@ def write_strings(rom, world, player):
entrances_to_hint.update({'Inverted Ganons Tower': 'The sealed castle door'})
else:
entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'})
if world.shuffle[player] in ['simple', 'restricted', 'restricted_legacy']:
if world.entrance_shuffle[player] in ['simple', 'restricted']:
for entrance in all_entrances:
if entrance.name in entrances_to_hint:
this_hint = entrances_to_hint[entrance.name] + ' leads to ' + hint_text(
@@ -2265,9 +2267,9 @@ def write_strings(rom, world, player):
break
# Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones.
entrances_to_hint.update(InconvenientOtherEntrances)
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
hint_count = 0
elif world.shuffle[player] in ['simple', 'restricted', 'restricted_legacy']:
elif world.entrance_shuffle[player] in ['simple', 'restricted']:
hint_count = 2
else:
hint_count = 4
@@ -2284,14 +2286,14 @@ def write_strings(rom, world, player):
# Next we handle hints for randomly selected other entrances,
# curating the selection intelligently based on shuffle.
if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']:
if world.entrance_shuffle[player] not in ['simple', 'restricted']:
entrances_to_hint.update(ConnectorEntrances)
entrances_to_hint.update(DungeonEntrances)
if world.mode[player] == 'inverted':
entrances_to_hint.update({'Inverted Agahnims Tower': 'The dark mountain tower'})
else:
entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'})
elif world.shuffle[player] == 'restricted':
elif world.entrance_shuffle[player] == 'restricted':
entrances_to_hint.update(ConnectorEntrances)
entrances_to_hint.update(OtherEntrances)
if world.mode[player] == 'inverted':
@@ -2301,15 +2303,15 @@ def write_strings(rom, world, player):
else:
entrances_to_hint.update({'Dark Sanctuary Hint': 'The dark sanctuary cave'})
entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'})
if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']:
if world.entrance_shuffle[player] != 'insanity':
entrances_to_hint.update(InsanityEntrances)
if world.shuffle_ganon:
if world.mode[player] == 'inverted':
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
else:
entrances_to_hint.update({'Pyramid Ledge': 'The pyramid ledge'})
hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull',
'dungeonscrossed'] else 0
hint_count = 4 if world.entrance_shuffle[player] not in ['vanilla', 'dungeons_simple', 'dungeons_full',
'dungeons_crossed'] else 0
for entrance in all_entrances:
if entrance.name in entrances_to_hint:
if hint_count:
@@ -2323,11 +2325,11 @@ def write_strings(rom, world, player):
# Next we write a few hints for specific inconvenient locations. We don't make many because in entrance this is highly unpredictable.
locations_to_hint = InconvenientLocations.copy()
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
locations_to_hint.extend(InconvenientVanillaLocations)
local_random.shuffle(locations_to_hint)
hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull',
'dungeonscrossed'] else 5
hint_count = 3 if world.entrance_shuffle[player] not in ['vanilla', 'dungeons_simple', 'dungeons_full',
'dungeons_crossed'] else 5
for location in locations_to_hint[:hint_count]:
if location == 'Swamp Left':
if local_random.randint(0, 1):
@@ -2381,16 +2383,16 @@ def write_strings(rom, world, player):
# Lastly we write hints to show where certain interesting items are.
items_to_hint = RelevantItems.copy()
if world.smallkey_shuffle[player].hints_useful:
if world.small_key_shuffle[player].hints_useful:
items_to_hint |= item_name_groups["Small Keys"]
if world.bigkey_shuffle[player].hints_useful:
if world.big_key_shuffle[player].hints_useful:
items_to_hint |= item_name_groups["Big Keys"]
if world.hints[player] == "full":
hint_count = len(hint_locations) # fill all remaining hint locations with Item hints.
else:
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull',
'dungeonscrossed'] else 8
hint_count = 5 if world.entrance_shuffle[player] not in ['vanilla', 'dungeons_simple', 'dungeons_full',
'dungeons_crossed'] else 8
hint_count = min(hint_count, len(items_to_hint), len(hint_locations))
if hint_count:
locations = world.find_items_in_locations(items_to_hint, player, True)
@@ -2417,7 +2419,7 @@ def write_strings(rom, world, player):
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
if world.worlds[player].has_progressive_bows and (world.difficulty_requirements[player].progressive_bow_limit >= 2 or (
world.swordless[player] or world.logic[player] == 'noglitches')):
world.swordless[player] or world.glitches_required[player] == 'no_glitches')):
prog_bow_locs = world.find_item_locations('Progressive Bow', player, True)
world.per_slot_randoms[player].shuffle(prog_bow_locs)
found_bow = False
@@ -2448,7 +2450,7 @@ def write_strings(rom, world, player):
if world.goal[player] == 'bosses':
tt['sign_ganon'] = 'You need to kill all bosses, Ganon last.'
elif world.goal[player] == 'ganonpedestal':
elif world.goal[player] == 'ganon_pedestal':
tt['sign_ganon'] = 'You need to pull the pedestal to defeat Ganon.'
elif world.goal[player] == "ganon":
if world.crystals_needed_for_ganon[player] == 1:
@@ -2456,14 +2458,6 @@ def write_strings(rom, world, player):
else:
tt['sign_ganon'] = f'You need {world.crystals_needed_for_ganon[player]} crystals to beat Ganon and ' \
f'have beaten Agahnim atop Ganons Tower'
elif world.goal[player] == "icerodhunt":
tt['sign_ganon'] = 'Go find the Ice Rod and Kill Trinexx, then talk to Murahdahla... Ganon is invincible!'
tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Go kill Trinexx instead.'
tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.'
tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\n" \
"invisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\n" \
"hidden in a hollow tree. " \
"If you bring me the Triforce piece from Turtle Rock, I can reassemble it."
else:
if world.crystals_needed_for_ganon[player] == 1:
tt['sign_ganon'] = 'You need a crystal to beat Ganon.'
@@ -2478,10 +2472,10 @@ def write_strings(rom, world, player):
tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[local_random.randint(0, len(Sahasrahla2_texts) - 1)]
tt['blind_by_the_light'] = Blind_texts[local_random.randint(0, len(Blind_texts) - 1)]
if world.goal[player] in ['triforcehunt', 'localtriforcehunt', 'icerodhunt']:
if world.goal[player] in ['triforce_hunt', 'local_triforce_hunt']:
tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.'
tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.'
if world.goal[player] == 'triforcehunt' and world.players > 1:
if world.goal[player] == 'triforce_hunt' and world.players > 1:
tt['sign_ganon'] = 'Go find the Triforce pieces with your friends... Ganon is invincible!'
else:
tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!'
@@ -2504,17 +2498,17 @@ def write_strings(rom, world, player):
tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!'
tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!'
if world.treasure_hunt_count[player] > 1:
if world.goal[player] == 'ganontriforcehunt' and world.players > 1:
if world.goal[player] == 'ganon_triforce_hunt' and world.players > 1:
tt['sign_ganon'] = 'You need to find %d Triforce pieces out of %d with your friends to defeat Ganon.' % \
(world.treasure_hunt_count[player], world.triforce_pieces_available[player])
elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
elif world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
tt['sign_ganon'] = 'You need to find %d Triforce pieces out of %d to defeat Ganon.' % \
(world.treasure_hunt_count[player], world.triforce_pieces_available[player])
else:
if world.goal[player] == 'ganontriforcehunt' and world.players > 1:
if world.goal[player] == 'ganon_triforce_hunt' and world.players > 1:
tt['sign_ganon'] = 'You need to find %d Triforce piece out of %d with your friends to defeat Ganon.' % \
(world.treasure_hunt_count[player], world.triforce_pieces_available[player])
elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
elif world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
tt['sign_ganon'] = 'You need to find %d Triforce piece out of %d to defeat Ganon.' % \
(world.treasure_hunt_count[player], world.triforce_pieces_available[player])
@@ -2614,12 +2608,12 @@ def set_inverted_mode(world, player, rom):
rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof
# the following bytes should only be written in vanilla
# or they'll overwrite the randomizer's shuffles
if world.shuffle[player] == 'vanilla':
if world.entrance_shuffle[player] == 'vanilla':
rom.write_byte(0xDBB73 + 0x23, 0x37) # switch AT and GT
rom.write_byte(0xDBB73 + 0x36, 0x24)
rom.write_int16(0x15AEE + 2 * 0x38, 0x00E0)
rom.write_int16(0x15AEE + 2 * 0x25, 0x000C)
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
rom.write_byte(0x15B8C, 0x6C)
rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house
rom.write_byte(0xDBB73 + 0x52, 0x01)
@@ -2677,7 +2671,7 @@ def set_inverted_mode(world, player, rom):
rom.write_int16(snes_to_pc(0x02D9A6), 0x005A)
rom.write_byte(snes_to_pc(0x02D9B3), 0x12)
# keep the old man spawn point at old man house unless shuffle is vanilla
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed']:
rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01])
rom.write_int16(snes_to_pc(0x02D8DE), 0x00F1)
rom.write_bytes(snes_to_pc(0x02D910), [0x1F, 0x1E, 0x1F, 0x1F, 0x03, 0x02, 0x03, 0x03])
@@ -2740,7 +2734,7 @@ def set_inverted_mode(world, player, rom):
rom.write_int16s(snes_to_pc(0x1bb836), [0x001B, 0x001B, 0x001B])
rom.write_int16(snes_to_pc(0x308300), 0x0140) # new pyramid hole entrance
rom.write_int16(snes_to_pc(0x308320), 0x001B)
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
rom.write_byte(snes_to_pc(0x308340), 0x7B)
rom.write_int16(snes_to_pc(0x1af504), 0x148B)
rom.write_int16(snes_to_pc(0x1af50c), 0x149B)
@@ -2777,10 +2771,10 @@ def set_inverted_mode(world, player, rom):
rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82])
rom.write_int16(0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door
rom.write_int16(0xDBA71 + 2 * 0x35, 0x06A4)
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
rom.write_byte(0xDBB73 + 0x35, 0x36)
rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
rom.write_int16(0x15AEE + 2 * 0x37, 0x0010) # pyramid exit to new hc area
rom.write_byte(0x15B8C + 0x37, 0x1B)
rom.write_int16(0x15BDB + 2 * 0x37, 0x0418)

View File

@@ -10,25 +10,25 @@ from worlds.generic.Rules import (add_item_rule, add_rule, forbid_item,
from . import OverworldGlitchRules
from .Bosses import GanonDefeatRule
from .Items import ItemFactory, item_name_groups, item_table, progression_items
from .Options import smallkey_shuffle
from .Options import small_key_shuffle
from .OverworldGlitchRules import no_logic_rules, overworld_glitches_rules
from .Regions import LTTPRegionType, location_table
from .StateHelpers import (can_extend_magic, can_kill_most_things,
can_lift_heavy_rocks, can_lift_rocks,
can_melt_things, can_retrieve_tablet,
can_shoot_arrows, has_beam_sword, has_crystals,
has_fire_source, has_hearts,
has_fire_source, has_hearts, has_melee_weapon,
has_misery_mire_medallion, has_sword, has_turtle_rock_medallion,
has_triforce_pieces)
has_triforce_pieces, can_use_bombs, can_bomb_or_bonk)
from .UnderworldGlitchRules import underworld_glitches_rules
def set_rules(world):
player = world.player
world = world.multiworld
if world.logic[player] == 'nologic':
if world.glitches_required[player] == 'no_logic':
if player == next(player_id for player_id in world.get_game_players("A Link to the Past")
if world.logic[player_id] == 'nologic'): # only warn one time
if world.glitches_required[player_id] == 'no_logic'): # only warn one time
logging.info(
'WARNING! Seeds generated under this logic often require major glitches and may be impossible!')
@@ -46,8 +46,8 @@ def set_rules(world):
else:
world.completion_condition[player] = lambda state: state.has('Triforce', player)
global_rules(world, player)
dungeon_boss_rules(world, player)
global_rules(world, player)
if world.mode[player] != 'inverted':
default_rules(world, player)
@@ -62,24 +62,24 @@ def set_rules(world):
else:
raise NotImplementedError(f'World state {world.mode[player]} is not implemented yet')
if world.logic[player] == 'noglitches':
if world.glitches_required[player] == 'no_glitches':
no_glitches_rules(world, player)
elif world.logic[player] == 'owglitches':
elif world.glitches_required[player] == 'overworld_glitches':
# Initially setting no_glitches_rules to set the baseline rules for some
# entrances. The overworld_glitches_rules set is primarily additive.
no_glitches_rules(world, player)
fake_flipper_rules(world, player)
overworld_glitches_rules(world, player)
elif world.logic[player] in ['hybridglitches', 'nologic']:
elif world.glitches_required[player] in ['hybrid_major_glitches', 'no_logic']:
no_glitches_rules(world, player)
fake_flipper_rules(world, player)
overworld_glitches_rules(world, player)
underworld_glitches_rules(world, player)
elif world.logic[player] == 'minorglitches':
elif world.glitches_required[player] == 'minor_glitches':
no_glitches_rules(world, player)
fake_flipper_rules(world, player)
else:
raise NotImplementedError(f'Not implemented yet: Logic - {world.logic[player]}')
raise NotImplementedError(f'Not implemented yet: Logic - {world.glitches_required[player]}')
if world.goal[player] == 'bosses':
# require all bosses to beat ganon
@@ -90,7 +90,7 @@ def set_rules(world):
if world.mode[player] != 'inverted':
set_big_bomb_rules(world, player)
if world.logic[player] in {'owglitches', 'hybridglitches', 'nologic'} and world.shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}:
if world.glitches_required[player] in {'overworld_glitches', 'hybrid_major_glitches', 'no_logic'} and world.entrance_shuffle[player] not in {'insanity', 'insanity_legacy', 'madness'}:
path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.multiworld.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or')
else:
@@ -98,18 +98,18 @@ def set_rules(world):
# if swamp and dam have not been moved we require mirror for swamp palace
# however there is mirrorless swamp in hybrid MG, so we don't necessarily want this. HMG handles this requirement itself.
if not world.swamp_patch_required[player] and world.logic[player] not in ['hybridglitches', 'nologic']:
if not world.swamp_patch_required[player] and world.glitches_required[player] not in ['hybrid_major_glitches', 'no_logic']:
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player))
# GT Entrance may be required for Turtle Rock for OWG and < 7 required
ganons_tower = world.get_entrance('Inverted Ganons Tower' if world.mode[player] == 'inverted' else 'Ganons Tower', player)
if world.crystals_needed_for_gt[player] == 7 and not (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and world.mode[player] != 'inverted'):
if world.crystals_needed_for_gt[player] == 7 and not (world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic'] and world.mode[player] != 'inverted'):
set_rule(ganons_tower, lambda state: False)
set_trock_key_rules(world, player)
set_rule(ganons_tower, lambda state: has_crystals(state, state.multiworld.crystals_needed_for_gt[player], player))
if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']:
if world.mode[player] != 'inverted' and world.glitches_required[player] in ['overworld_glitches', 'hybrid_major_glitches', 'no_logic']:
add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.multiworld.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or')
set_bunny_rules(world, player, world.mode[player] == 'inverted')
@@ -140,6 +140,7 @@ def set_defeat_dungeon_boss_rule(location):
add_rule(location, lambda state: location.parent_region.dungeon.boss.can_defeat(state))
def set_always_allow(spot, rule):
spot.always_allow = rule
@@ -185,6 +186,7 @@ def dungeon_boss_rules(world, player):
for location in boss_locations:
set_defeat_dungeon_boss_rule(world.get_location(location, player))
def global_rules(world, player):
# ganon can only carry triforce
add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player)
@@ -214,14 +216,61 @@ def global_rules(world, player):
set_rule(world.get_location('Ether Tablet', player), lambda state: can_retrieve_tablet(state, 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
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
set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player))
set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player))
set_rule(world.get_location('Sick Kid', player), lambda state: state.has_group("Bottles", player))
set_rule(world.get_location('Library', player), lambda state: state.has('Pegasus Boots', player))
set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player))
if world.enemy_shuffle[player]:
set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player) and
can_kill_most_things(state, player, 4))
else:
set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player)
and ((state.multiworld.enemy_health[player] in ("easy", "default") and can_use_bombs(state, player, 4))
or can_shoot_arrows(state, player) or state.has("Cane of Somaria", player)
or has_beam_sword(state, player)))
set_rule(world.get_location('Sahasrahla', player), lambda state: state.has('Green Pendant', player))
set_rule(world.get_location('Aginah\'s Cave', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Blind\'s Hideout - Top', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Chicken House', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Kakariko Well - Top', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Graveyard Cave', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Sahasrahla\'s Hut - Left', player), lambda state: can_bomb_or_bonk(state, player))
set_rule(world.get_location('Sahasrahla\'s Hut - Middle', player), lambda state: can_bomb_or_bonk(state, player))
set_rule(world.get_location('Sahasrahla\'s Hut - Right', player), lambda state: can_bomb_or_bonk(state, player))
set_rule(world.get_location('Paradox Cave Lower - Left', player), lambda state: can_use_bombs(state, player)
or has_beam_sword(state, player) or can_shoot_arrows(state, player)
or state.has_any(["Fire Rod", "Cane of Somaria"], player))
set_rule(world.get_location('Paradox Cave Lower - Right', player), lambda state: can_use_bombs(state, player)
or has_beam_sword(state, player) or can_shoot_arrows(state, player)
or state.has_any(["Fire Rod", "Cane of Somaria"], player))
set_rule(world.get_location('Paradox Cave Lower - Far Right', player), lambda state: can_use_bombs(state, player)
or has_beam_sword(state, player) or can_shoot_arrows(state, player)
or state.has_any(["Fire Rod", "Cane of Somaria"], player))
set_rule(world.get_location('Paradox Cave Lower - Middle', player), lambda state: can_use_bombs(state, player)
or has_beam_sword(state, player) or can_shoot_arrows(state, player)
or state.has_any(["Fire Rod", "Cane of Somaria"], player))
set_rule(world.get_location('Paradox Cave Lower - Far Left', player), lambda state: can_use_bombs(state, player)
or has_beam_sword(state, player) or can_shoot_arrows(state, player)
or state.has_any(["Fire Rod", "Cane of Somaria"], player))
set_rule(world.get_location('Paradox Cave Upper - Left', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Paradox Cave Upper - Right', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Mini Moldorm Cave - Far Left', player), lambda state: can_kill_most_things(state, player, 4))
set_rule(world.get_location('Mini Moldorm Cave - Left', player), lambda state: can_kill_most_things(state, player, 4))
set_rule(world.get_location('Mini Moldorm Cave - Far Right', player), lambda state: can_kill_most_things(state, player, 4))
set_rule(world.get_location('Mini Moldorm Cave - Right', player), lambda state: can_kill_most_things(state, player, 4))
set_rule(world.get_location('Mini Moldorm Cave - Generous Guy', player), lambda state: can_kill_most_things(state, player, 4))
set_rule(world.get_location('Hype Cave - Bottom', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Hype Cave - Middle Left', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Hype Cave - Middle Right', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Hype Cave - Top', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Light World Death Mountain Shop', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Two Brothers House Exit (West)', player), lambda state: can_bomb_or_bonk(state, player))
set_rule(world.get_entrance('Two Brothers House Exit (East)', player), lambda state: can_bomb_or_bonk(state, player))
set_rule(world.get_location('Spike Cave', player), lambda state:
state.has('Hammer', player) and can_lift_rocks(state, player) and
@@ -239,61 +288,81 @@ def global_rules(world, player):
set_rule(world.get_entrance('Sewers Door', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4) or (
world.smallkey_shuffle[player] == smallkey_shuffle.option_universal and world.mode[
world.small_key_shuffle[player] == small_key_shuffle.option_universal and world.mode[
player] == 'standard')) # standard universal small keys cannot access the shop
set_rule(world.get_entrance('Sewers Back Door', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4))
set_rule(world.get_entrance('Sewers Secret Room', player), lambda state: can_bomb_or_bonk(state, player))
set_rule(world.get_entrance('Agahnim 1', player),
lambda state: has_sword(state, player) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 4))
set_rule(world.get_location('Castle Tower - Room 03', player), lambda state: can_kill_most_things(state, player, 8))
set_rule(world.get_location('Castle Tower - Room 03', player), lambda state: can_kill_most_things(state, player, 4))
set_rule(world.get_location('Castle Tower - Dark Maze', player),
lambda state: can_kill_most_things(state, player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)',
lambda state: can_kill_most_things(state, player, 4) and state._lttp_has_key('Small Key (Agahnims Tower)',
player))
set_rule(world.get_location('Castle Tower - Dark Archer Key Drop', player),
lambda state: can_kill_most_things(state, player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)',
lambda state: can_kill_most_things(state, player, 4) and state._lttp_has_key('Small Key (Agahnims Tower)',
player, 2))
set_rule(world.get_location('Castle Tower - Circle of Pots Key Drop', player),
lambda state: can_kill_most_things(state, player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)',
lambda state: can_kill_most_things(state, player, 4) and state._lttp_has_key('Small Key (Agahnims Tower)',
player, 3))
set_always_allow(world.get_location('Eastern Palace - Big Key Chest', player),
lambda state, item: item.name == 'Big Key (Eastern Palace)' and item.player == player)
set_rule(world.get_location('Eastern Palace - Big Key Chest', player),
lambda state: state._lttp_has_key('Small Key (Eastern Palace)', player, 2) or
((location_item_name(state, 'Eastern Palace - Big Key Chest', player) == ('Big Key (Eastern Palace)', player)
and state.has('Small Key (Eastern Palace)', player))))
lambda state: can_kill_most_things(state, player, 5) and (state._lttp_has_key('Small Key (Eastern Palace)',
player, 2) or ((location_item_name(state, 'Eastern Palace - Big Key Chest', player)
== ('Big Key (Eastern Palace)', player) and state.has('Small Key (Eastern Palace)',
player)))))
set_rule(world.get_location('Eastern Palace - Dark Eyegore Key Drop', player),
lambda state: state.has('Big Key (Eastern Palace)', player))
lambda state: state.has('Big Key (Eastern Palace)', player) and can_kill_most_things(state, player, 1))
set_rule(world.get_location('Eastern Palace - Big Chest', player),
lambda state: state.has('Big Key (Eastern Palace)', player))
# not bothering to check for can_kill_most_things in the rooms leading to boss, as if you can kill a boss you should
# be able to get through these rooms
ep_boss = world.get_location('Eastern Palace - Boss', player)
set_rule(ep_boss, lambda state: state.has('Big Key (Eastern Palace)', player) and
add_rule(ep_boss, lambda state: state.has('Big Key (Eastern Palace)', player) and
state._lttp_has_key('Small Key (Eastern Palace)', player, 2) and
ep_boss.parent_region.dungeon.boss.can_defeat(state))
ep_prize = world.get_location('Eastern Palace - Prize', player)
set_rule(ep_prize, lambda state: state.has('Big Key (Eastern Palace)', player) and
add_rule(ep_prize, lambda state: state.has('Big Key (Eastern Palace)', player) and
state._lttp_has_key('Small Key (Eastern Palace)', player, 2) and
ep_prize.parent_region.dungeon.boss.can_defeat(state))
if not world.enemy_shuffle[player]:
add_rule(ep_boss, lambda state: can_shoot_arrows(state, player))
add_rule(ep_prize, lambda state: can_shoot_arrows(state, player))
# You can always kill the Stalfos' with the pots on easy/normal
if world.enemy_health[player] in ("hard", "expert") or world.enemy_shuffle[player]:
stalfos_rule = lambda state: can_kill_most_things(state, player, 4)
for location in ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest',
'Eastern Palace - Dark Square Pot Key', 'Eastern Palace - Dark Eyegore Key Drop',
'Eastern Palace - Big Key Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize']:
add_rule(world.get_location(location, player), stalfos_rule)
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('Pegasus Boots', player))
set_rule(world.get_entrance('Desert Palace East Wing', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4))
set_rule(world.get_location('Desert Palace - Big Key Chest', player), lambda state: can_kill_most_things(state, player))
set_rule(world.get_location('Desert Palace - Beamos Hall Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 2) and can_kill_most_things(state, player))
set_rule(world.get_location('Desert Palace - Desert Tiles 2 Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 3) and can_kill_most_things(state, player))
set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state))
set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
set_rule(world.get_location('Desert Palace - Big Key Chest', player), lambda state: can_kill_most_things(state, player, 3))
set_rule(world.get_location('Desert Palace - Beamos Hall Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 2) and can_kill_most_things(state, player, 4))
set_rule(world.get_location('Desert Palace - Desert Tiles 2 Pot Key', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 3) and can_kill_most_things(state, player, 4))
add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state))
add_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player, 4) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
# logic patch to prevent placing a crystal in Desert that's required to reach the required keys
if not (world.smallkey_shuffle[player] and world.bigkey_shuffle[player]):
if not (world.small_key_shuffle[player] and world.big_key_shuffle[player]):
add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.multiworld.get_region('Desert Palace Main (Outer)', player).can_reach(state))
set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Tower of Hera)', player) or location_item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player))
set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player))
if world.enemy_shuffle[player]:
add_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: can_kill_most_things(state, player, 3))
else:
add_rule(world.get_entrance('Tower of Hera Big Key Door', player),
lambda state: (has_melee_weapon(state, player) or (state.has('Silver Bow', player)
and can_shoot_arrows(state, player)) or state.has("Cane of Byrna", player)
or state.has("Cane of Somaria", player)))
set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player))
set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: has_fire_source(state, player))
if world.accessibility[player] != 'full':
@@ -301,9 +370,13 @@ def global_rules(world, player):
set_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player))
set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player))
set_rule(world.get_location('Swamp Palace - Map Chest', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_location('Swamp Palace - Trench 1 Pot Key', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 2))
set_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: state.has('Hammer', player) and state._lttp_has_key('Small Key (Swamp Palace)', player, 3))
set_rule(world.get_location('Swamp Palace - Hookshot Pot Key', player), lambda state: state.has('Hookshot', player))
if world.pot_shuffle[player]:
# it could move the key to the top right platform which can only be reached with bombs
add_rule(world.get_location('Swamp Palace - Hookshot Pot Key', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Swamp Palace (West)', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 6)
if state.has('Hookshot', player)
else state._lttp_has_key('Small Key (Swamp Palace)', player, 4))
@@ -311,15 +384,18 @@ def global_rules(world, player):
if world.accessibility[player] != 'full':
allow_self_locking_items(world.get_location('Swamp Palace - Big Chest', player), 'Big Key (Swamp Palace)')
set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player) and state._lttp_has_key('Small Key (Swamp Palace)', player, 5))
if not world.smallkey_shuffle[player] and world.logic[player] not in ['hybridglitches', 'nologic']:
if not world.small_key_shuffle[player] and world.glitches_required[player] not in ['hybrid_major_glitches', 'no_logic']:
forbid_item(world.get_location('Swamp Palace - Entrance', player), 'Big Key (Swamp Palace)', player)
set_rule(world.get_location('Swamp Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 6))
set_rule(world.get_location('Swamp Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Swamp Palace)', player, 6))
if world.pot_shuffle[player]:
# key can (and probably will) be moved behind bombable wall
set_rule(world.get_location('Swamp Palace - Waterway Pot Key', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player))
if world.worlds[player].dungeons["Thieves Town"].boss.enemizer_name == "Blind":
set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3))
set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3) and can_use_bombs(state, player))
set_rule(world.get_location('Thieves\' Town - Big Chest', player),
lambda state: (state._lttp_has_key('Small Key (Thieves Town)', player, 3)) and state.has('Hammer', player))
@@ -335,7 +411,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('Skull Woods First Section (Right) North Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5))
set_rule(world.get_entrance('Skull Woods First Section West Door', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5))
set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5))
set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player))
set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) and can_use_bombs(state, player))
if world.accessibility[player] != 'full':
allow_self_locking_items(world.get_location('Skull Woods - Big Chest', player), 'Big Key (Skull Woods)')
set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 4) and state.has('Fire Rod', player) and has_sword(state, player)) # sword required for curtain
@@ -343,7 +419,13 @@ def global_rules(world, player):
add_rule(world.get_location('Skull Woods - Boss', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 5))
set_rule(world.get_location('Ice Palace - Jelly Key Drop', player), lambda state: can_melt_things(state, player))
set_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player))
set_rule(world.get_location('Ice Palace - Compass Chest', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player))
set_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player) and can_use_bombs(state, player))
if not world.enemy_shuffle[player]:
# Stalfos Knights can be killed by damaging them repeatedly with boomerang, swords, etc. if bombs are
# unavailable. If bombs are available, the pots can be thrown at them, so no other weapons are needed
add_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: (can_use_bombs(state, player)
or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or has_sword(state, player) or state.has("Hammer", player)))
set_rule(world.get_entrance('Ice Palace (Main)', player), lambda state: state._lttp_has_key('Small Key (Ice Palace)', player, 2))
set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player))
set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: can_lift_rocks(state, player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state._lttp_has_key('Small Key (Ice Palace)', player, 6) or (state.has('Cane of Somaria', player) and state._lttp_has_key('Small Key (Ice Palace)', player, 5))))
@@ -388,16 +470,21 @@ def global_rules(world, player):
else state._lttp_has_key('Small Key (Misery Mire)', player, 6))
set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: has_fire_source(state, player))
set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: has_fire_source(state, player))
set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('Turtle Rock Entrance Gap', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('Turtle Rock Entrance Gap Reverse', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_location('Turtle Rock - Pokey 1 Key Drop', player), lambda state: can_kill_most_things(state, player, 5))
set_rule(world.get_location('Turtle Rock - Pokey 2 Key Drop', player), lambda state: can_kill_most_things(state, player, 5))
set_rule(world.get_location('Turtle Rock - Compass Chest', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_location('Turtle Rock - Roller Room - Left', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
set_rule(world.get_location('Turtle Rock - Roller Room - Right', player), lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
set_rule(world.get_location('Turtle Rock - Big Chest', player), lambda state: state.has('Big Key (Turtle Rock)', player) and (state.has('Cane of Somaria', player) or state.has('Hookshot', player)))
set_rule(world.get_entrance('Turtle Rock (Big Chest) (North)', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
set_rule(world.get_entrance('Turtle Rock Big Key Door', player), lambda state: state.has('Big Key (Turtle Rock)', player))
set_rule(world.get_entrance('Turtle Rock Big Key Door', player), lambda state: state.has('Big Key (Turtle Rock)', player) and can_kill_most_things(state, player, 10))
set_rule(world.get_entrance('Turtle Rock Ledge Exit (West)', player), lambda state: can_use_bombs(state, player) and can_kill_most_things(state, player, 10))
set_rule(world.get_location('Turtle Rock - Chain Chomps', player), lambda state: can_use_bombs(state, player) or can_shoot_arrows(state, player)
or has_beam_sword(state, player) or state.has_any(["Blue Boomerang", "Red Boomerang", "Hookshot", "Cane of Somaria", "Fire Rod", "Ice Rod"], player))
set_rule(world.get_entrance('Turtle Rock (Dark Room) (North)', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('Turtle Rock (Dark Room) (South)', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
@@ -406,16 +493,22 @@ def global_rules(world, player):
set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player))
set_rule(world.get_entrance('Turtle Rock (Trinexx)', player), lambda state: state._lttp_has_key('Small Key (Turtle Rock)', player, 6) and state.has('Big Key (Turtle Rock)', player) and state.has('Cane of Somaria', player))
if not world.enemy_shuffle[player]:
set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: can_shoot_arrows(state, player))
if world.enemy_shuffle[player]:
set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: can_bomb_or_bonk(state, player) and can_kill_most_things(state, player, 3))
else:
set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: can_bomb_or_bonk(state, player) and can_shoot_arrows(state, 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._lttp_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._lttp_has_key('Small Key (Palace of Darkness)', player, 6) and state.has('Big Key (Palace of Darkness)', player) and can_shoot_arrows(state, player) and state.has('Hammer', player))
set_rule(world.get_entrance('Palace of Darkness (North)', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 4))
set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player))
set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: can_use_bombs(state, player) and state.has('Big Key (Palace of Darkness)', player))
set_rule(world.get_location('Palace of Darkness - The Arena - Ledge', player), lambda state: can_use_bombs(state, player))
if world.pot_shuffle[player]:
# chest switch may be up on ledge where bombs are required
set_rule(world.get_location('Palace of Darkness - Stalfos Basement', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or (
location_item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 3)))
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase', player), lambda state: can_use_bombs(state, player) and (state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) or (
location_item_name(state, 'Palace of Darkness - Big Key Chest', player) in [('Small Key (Palace of Darkness)', player)] and state._lttp_has_key('Small Key (Palace of Darkness)', player, 3))))
if world.accessibility[player] != 'full':
set_always_allow(world.get_location('Palace of Darkness - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state._lttp_has_key('Small Key (Palace of Darkness)', player, 5))
@@ -431,13 +524,9 @@ def global_rules(world, player):
compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right', 'Ganons Tower - Conveyor Star Pits Pot Key']
back_chests = ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left', 'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']
set_rule(world.get_location('Ganons Tower - Bob\'s Torch', player), lambda state: state.has('Pegasus Boots', player))
set_rule(world.get_entrance('Ganons Tower (Tile Room)', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hammer', player) and (state.has('Hookshot', player) or state.has('Pegasus Boots', player)))
if world.pot_shuffle[player]:
# Pot Shuffle can move this check into the hookshot room
set_rule(world.get_location('Ganons Tower - Conveyor Cross Pot Key', player), lambda state: state.has('Hammer', player) and (state.has('Hookshot', player) or state.has('Pegasus Boots', player)))
set_rule(world.get_entrance('Ganons Tower (Map Room)', player), lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 8) or (
location_item_name(state, 'Ganons Tower - Map Chest', player) in [('Big Key (Ganons Tower)', player)] and state._lttp_has_key('Small Key (Ganons Tower)', player, 6)))
@@ -466,17 +555,17 @@ def global_rules(world, player):
item_name_in_location_names(state, 'Big Key (Ganons Tower)', player, zip(back_chests, [player] * len(back_chests))) and state._lttp_has_key('Small Key (Ganons Tower)', player, 5)))
# Actual requirements
for location in compass_room_chests:
set_rule(world.get_location(location, player), lambda state: state.has('Fire Rod', player) and (state._lttp_has_key('Small Key (Ganons Tower)', player, 7) or (
set_rule(world.get_location(location, player), lambda state: (can_use_bombs(state, player) or state.has("Cane of Somaria", player)) and state.has('Fire Rod', player) and (state._lttp_has_key('Small Key (Ganons Tower)', player, 7) or (
item_name_in_location_names(state, 'Big Key (Ganons Tower)', player, zip(compass_room_chests, [player] * len(compass_room_chests))) and state._lttp_has_key('Small Key (Ganons Tower)', player, 5))))
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.multiworld.get_location('Ganons Tower - Big Key Room - Left', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
lambda state: can_use_bombs(state, player) and state.multiworld.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.multiworld.get_location('Ganons Tower - Big Key Chest', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
lambda state: can_use_bombs(state, player) and state.multiworld.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.multiworld.get_location('Ganons Tower - Big Key Room - Right', player).parent_region.dungeon.bosses['bottom'].can_defeat(state))
lambda state: can_use_bombs(state, player) and state.multiworld.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))
@@ -484,7 +573,8 @@ def global_rules(world, player):
set_rule(world.get_entrance('Ganons Tower Big Key Door', player),
lambda state: state.has('Big Key (Ganons Tower)', player) and can_shoot_arrows(state, player))
set_rule(world.get_entrance('Ganons Tower Torch Rooms', player),
lambda state: has_fire_source(state, player) and state.multiworld.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state))
lambda state: can_kill_most_things(state, player, 8) and has_fire_source(state, player) and state.multiworld.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state))
set_rule(world.get_location('Ganons Tower - Mini Helmasaur Key Drop', player), lambda state: can_kill_most_things(state, player, 1))
set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player),
lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 7))
set_rule(world.get_entrance('Ganons Tower Moldorm Door', player),
@@ -494,9 +584,9 @@ def global_rules(world, player):
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']:
if world.goal[player] in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
add_rule(ganon, lambda state: has_triforce_pieces(state, player))
elif world.goal[player] == 'ganonpedestal':
elif world.goal[player] == 'ganon_pedestal':
add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player))
else:
add_rule(ganon, lambda state: has_crystals(state, state.multiworld.crystals_needed_for_ganon[player], player))
@@ -508,6 +598,12 @@ def global_rules(world, player):
def default_rules(world, player):
"""Default world rules when world state is not inverted."""
# overworld requirements
set_rule(world.get_entrance('Light World Bomb Hut', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Ice Rod Cave', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has('Pegasus Boots', player))
set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: can_lift_heavy_rocks(state, player))
set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: can_lift_heavy_rocks(state, player))
@@ -563,12 +659,12 @@ def default_rules(world, player):
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: (state.has('Moon Pearl', player) and state.has('Flippers', player) or state.has('Magic Mirror', player))) # Overworld Bunny Revival
set_rule(world.get_location('Bombos Tablet', player), lambda state: can_retrieve_tablet(state, player))
set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has('Moon 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('Moon Pearl', player)) # bomb required
set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player))
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.has('Moon Pearl', player) and can_lift_heavy_rocks(state, player))
set_rule(world.get_entrance('Hype Cave', player), lambda state: state.has('Moon Pearl', player)) # bomb required
set_rule(world.get_entrance('Brewery', player), lambda state: state.has('Moon Pearl', player)) # bomb required
set_rule(world.get_entrance('Hype Cave', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('Brewery', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('Thieves Town', player), lambda state: state.has('Moon Pearl', player)) # bunny cannot pull
set_rule(world.get_entrance('Skull Woods First Section Hole (North)', player), lambda state: state.has('Moon Pearl', player)) # bunny cannot lift bush
set_rule(world.get_entrance('Skull Woods Second Section Hole', player), lambda state: state.has('Moon Pearl', player)) # bunny cannot lift bush
@@ -622,9 +718,9 @@ def inverted_rules(world, player):
# overworld requirements
set_rule(world.get_location('Maze Race', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Ice Rod Cave', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('Ice Rod Cave', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('Light Hype Fairy', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('Potion Shop Pier', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player))
set_rule(world.get_entrance('Light World Pier', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player))
set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has('Pegasus Boots', player) and state.has('Moon Pearl', player))
@@ -670,7 +766,7 @@ def inverted_rules(world, player):
set_rule(world.get_entrance('Bush Covered Lawn Outer Bushes', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Bomb Hut Inner Bushes', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Bomb Hut Outer Bushes', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Light World Bomb Hut', player), lambda state: state.has('Moon Pearl', player)) # need bomb
set_rule(world.get_entrance('Light World Bomb Hut', player), lambda state: state.has('Moon Pearl', player) and can_use_bombs(state, player))
set_rule(world.get_entrance('North Fairy Cave Drop', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_entrance('Lost Woods Hideout Drop', player), lambda state: state.has('Moon Pearl', player))
set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and (state.can_reach('Potion Shop Area', 'Region', player))) # new inverted region, need pearl for bushes or access to potion shop door/waterfall fairy
@@ -716,6 +812,11 @@ def inverted_rules(world, player):
set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player))
set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player))
set_rule(world.get_entrance('Hype Cave', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Brewery', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: can_use_bombs(state, player))
set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player))
set_rule(world.get_entrance('Misery Mire', player), lambda state: has_sword(state, player) and has_misery_mire_medallion(state, player)) # sword required to cast magic (!)
@@ -901,20 +1002,25 @@ def add_conditional_lamps(world, player):
def open_rules(world, player):
set_rule(world.get_location('Hyrule Castle - Map Guard Key Drop', player),
lambda state: can_kill_most_things(state, player, 1))
def basement_key_rule(state):
if location_item_name(state, 'Sewers - Key Rat Key Drop', player) == ("Small Key (Hyrule Castle)", player):
return state._lttp_has_key("Small Key (Hyrule Castle)", player, 2)
else:
return state._lttp_has_key("Small Key (Hyrule Castle)", player, 3)
set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player), basement_key_rule)
set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player),
lambda state: basement_key_rule(state) and can_kill_most_things(state, player, 2))
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), basement_key_rule)
set_rule(world.get_location('Sewers - Key Rat Key Drop', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 3))
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 3) and can_kill_most_things(state, player, 1))
set_rule(world.get_location('Hyrule Castle - Big Key Drop', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4))
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4) and can_kill_most_things(state, player, 1))
set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 4) and
state.has('Big Key (Hyrule Castle)', player))
@@ -925,6 +1031,7 @@ def swordless_rules(world, player):
set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain
set_rule(world.get_location('Ice Palace - Jelly Key Drop', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player))
set_rule(world.get_location('Ice Palace - Compass Chest', player), lambda state: (state.has('Fire Rod', player) or state.has('Bombos', player)) and state._lttp_has_key('Small Key (Ice Palace)', player))
set_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: (state.has('Fire Rod', player) or state.has('Bombos', player)) and state._lttp_has_key('Small Key (Ice Palace)', player))
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop
@@ -955,7 +1062,7 @@ def standard_rules(world, player):
set_rule(world.get_entrance('Links House S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
set_rule(world.get_entrance('Sanctuary S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
if world.smallkey_shuffle[player] != smallkey_shuffle.option_universal:
if world.small_key_shuffle[player] != small_key_shuffle.option_universal:
set_rule(world.get_location('Hyrule Castle - Boomerang Guard Key Drop', player),
lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player, 1))
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player),
@@ -1063,15 +1170,15 @@ def set_trock_key_rules(world, player):
return 6
# If TR is only accessible from the middle, the big key must be further restricted to prevent softlock potential
if not can_reach_front and not world.smallkey_shuffle[player]:
if not can_reach_front and not world.small_key_shuffle[player]:
# Must not go in the Big Key Chest - only 1 other chest available and 2+ keys required for all other chests
forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Big Key (Turtle Rock)', player)
if not can_reach_big_chest:
# Must not go in the Chain Chomps chest - only 2 other chests available and 3+ keys required for all other chests
forbid_item(world.get_location('Turtle Rock - Chain Chomps', player), 'Big Key (Turtle Rock)', player)
forbid_item(world.get_location('Turtle Rock - Pokey 2 Key Drop', player), 'Big Key (Turtle Rock)', player)
if world.accessibility[player] == 'full' and world.goal[player] != 'icerodhunt':
if world.bigkey_shuffle[player] and can_reach_big_chest:
if world.accessibility[player] == 'full':
if world.big_key_shuffle[player] and can_reach_big_chest:
# Must not go in the dungeon - all 3 available chests (Chomps, Big Chest, Crystaroller) must be keys to access laser bridge, and the big key is required first
for location in ['Turtle Rock - Chain Chomps', 'Turtle Rock - Compass Chest',
'Turtle Rock - Pokey 1 Key Drop', 'Turtle Rock - Pokey 2 Key Drop',
@@ -1516,8 +1623,10 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
# regions for the exits of multi-entrance caves/drops that bunny cannot pass
# Note spiral cave and two brothers house are passable in superbunny state for glitch logic with extra requirements.
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)',
'Turtle Rock (Eye Bridge)', 'Sewers', 'Pyramid', 'Spiral Cave (Top)', 'Desert Palace Main (Inner)', 'Fairy Ascension Cave (Drop)']
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave', 'Skull Woods First Section (Right)',
'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)', 'Turtle Rock (Entrance)', 'Turtle Rock (Second Section)',
'Turtle Rock (Big Chest)', 'Skull Woods Second Section (Drop)', 'Turtle Rock (Eye Bridge)', 'Sewers', 'Pyramid',
'Spiral Cave (Top)', 'Desert Palace Main (Inner)', 'Fairy Ascension Cave (Drop)']
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid',
@@ -1550,7 +1659,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
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] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic']:
if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic']:
if region.name == 'Swamp Palace (Entrance)': # Need to 0hp revive - not in logic
return lambda state: state.has('Moon Pearl', player)
if region.name == 'Tower of Hera (Bottom)': # Need to hit the crystal switch
@@ -1590,7 +1699,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
seen.add(new_region)
if not is_link(new_region):
# For glitch rulesets, establish superbunny and revival rules.
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions():
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has('Magic Mirror', player) and has_sword(state, player))
elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions()
@@ -1627,7 +1736,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
# Add requirements for all locations that are actually in the dark world, except those available to the bunny, including dungeon revival
for entrance in world.get_entrances(player):
if is_bunny(entrance.connected_region):
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] :
if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'] :
if entrance.connected_region.type == LTTPRegionType.Dungeon:
if entrance.parent_region.type != LTTPRegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
add_rule(entrance, get_rule_to_add(entrance.connected_region, None, entrance))
@@ -1635,7 +1744,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
if entrance.connected_region.name == 'Turtle Rock (Entrance)':
add_rule(world.get_entrance('Turtle Rock Entrance Gap', player), get_rule_to_add(entrance.connected_region, None, entrance))
for location in entrance.connected_region.locations:
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
if world.glitches_required[player] in ['minor_glitches', 'overworld_glitches', 'hybrid_major_glitches', 'no_logic'] and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances():
continue
if location.name in bunny_accessible_locations:
continue

View File

@@ -5,11 +5,14 @@ import logging
from Utils import int16_as_bytes
from worlds.generic.Rules import add_rule
from BaseClasses import CollectionState
from .SubClasses import ALttPLocation
from .EntranceShuffle import door_addresses
from .Items import item_name_groups, item_table, ItemFactory, trap_replaceable, GetBeemizerItem
from .Options import smallkey_shuffle
from .Items import item_name_groups
from .Options import small_key_shuffle, RandomizeShopInventories
from .StateHelpers import has_hearts, can_use_bombs, can_hold_arrows
logger = logging.getLogger("Shops")
@@ -36,9 +39,9 @@ class ShopPriceType(IntEnum):
Item = 10
class Shop():
class Shop:
slots: int = 3 # slot count is not dynamic in asm, however inventory can have None as empty slots
blacklist: Set[str] = set() # items that don't work, todo: actually check against this
blacklist: Set[str] = set() # items that don't work
type = ShopType.Shop
slot_names: Dict[int, str] = {
0: " Left",
@@ -103,7 +106,7 @@ class Shop():
self.inventory = [None] * self.slots
def add_inventory(self, slot: int, item: str, price: int, max: int = 0,
replacement: Optional[str] = None, replacement_price: int = 0, create_location: bool = False,
replacement: Optional[str] = None, replacement_price: int = 0,
player: int = 0, price_type: int = ShopPriceType.Rupees,
replacement_price_type: int = ShopPriceType.Rupees):
self.inventory[slot] = {
@@ -114,33 +117,23 @@ class Shop():
'replacement': replacement,
'replacement_price': replacement_price,
'replacement_price_type': replacement_price_type,
'create_location': create_location,
'player': player
}
def push_inventory(self, slot: int, item: str, price: int, max: int = 1, player: int = 0,
price_type: int = ShopPriceType.Rupees):
if not self.inventory[slot]:
raise ValueError("Inventory can't be pushed back if it doesn't exist")
if not self.can_push_inventory(slot):
logging.warning(f'Warning, there is already an item pushed into this slot.')
self.inventory[slot] = {
'item': item,
'price': price,
'price_type': price_type,
'max': max,
'replacement': self.inventory[slot]["item"],
'replacement_price': self.inventory[slot]["price"],
'replacement_price_type': self.inventory[slot]["price_type"],
'create_location': self.inventory[slot]["create_location"],
'replacement': self.inventory[slot]["item"] if self.inventory[slot] else None,
'replacement_price': self.inventory[slot]["price"] if self.inventory[slot] else 0,
'replacement_price_type': self.inventory[slot]["price_type"] if self.inventory[slot] else ShopPriceType.Rupees,
'player': player
}
def can_push_inventory(self, slot: int):
return self.inventory[slot] and not self.inventory[slot]["replacement"]
class TakeAny(Shop):
type = ShopType.TakeAny
@@ -156,6 +149,10 @@ class UpgradeShop(Shop):
# Potions break due to VRAM flags set in UpgradeShop.
# Didn't check for more things breaking as not much else can be shuffled here currently
blacklist = item_name_groups["Potions"]
slot_names: Dict[int, str] = {
0: " Left",
1: " Right"
}
shop_class_mapping = {ShopType.UpgradeShop: UpgradeShop,
@@ -163,191 +160,84 @@ shop_class_mapping = {ShopType.UpgradeShop: UpgradeShop,
ShopType.TakeAny: TakeAny}
def FillDisabledShopSlots(world):
shop_slots: Set[ALttPLocation] = {location for shop_locations in (shop.region.locations for shop in world.shops)
for location in shop_locations
if location.shop_slot is not None and location.shop_slot_disabled}
def push_shop_inventories(multiworld):
shop_slots = [location for shop_locations in (shop.region.locations for shop in multiworld.shops if shop.type
!= ShopType.TakeAny) for location in shop_locations if location.shop_slot is not None]
for location in shop_slots:
location.shop_slot_disabled = True
shop: Shop = location.parent_region.shop
location.item = ItemFactory(shop.inventory[location.shop_slot]['item'], location.player)
location.item_rule = lambda item: item.name == location.item.name and item.player == location.player
location.locked = True
item_name = location.item.name
# Retro Bow arrows will already have been pushed
if (not multiworld.retro_bow[location.player]) or ((item_name, location.item.player)
!= ("Single Arrow", location.player)):
location.shop.push_inventory(location.shop_slot, item_name, location.shop_price,
1, location.item.player if location.item.player != location.player else 0,
location.shop_price_type)
location.shop_price = location.shop.inventory[location.shop_slot]["price"] = min(location.shop_price,
get_price(multiworld, location.shop.inventory[location.shop_slot], location.player,
location.shop_price_type)[1])
def ShopSlotFill(multiworld):
shop_slots: Set[ALttPLocation] = {location for shop_locations in
(shop.region.locations for shop in multiworld.shops if shop.type != ShopType.TakeAny)
for location in shop_locations if location.shop_slot is not None}
removed = set()
for location in shop_slots:
shop: Shop = location.parent_region.shop
if not shop.can_push_inventory(location.shop_slot) or location.shop_slot_disabled:
location.shop_slot_disabled = True
removed.add(location)
if removed:
shop_slots -= removed
if shop_slots:
logger.info("Filling LttP Shop Slots")
del shop_slots
from Fill import swap_location_item
# TODO: allow each game to register a blacklist to be used here?
blacklist_words = {"Rupee"}
blacklist_words = {item_name for item_name in item_table if any(
blacklist_word in item_name for blacklist_word in blacklist_words)}
blacklist_words.add("Bee")
locations_per_sphere = [sorted(sphere, key=lambda location: (location.name, location.player))
for sphere in multiworld.get_spheres()]
# currently special care needs to be taken so that Shop.region.locations.item is identical to Shop.inventory
# Potentially create Locations as needed and make inventory the only source, to prevent divergence
cumu_weights = []
shops_per_sphere = []
candidates_per_sphere = []
# sort spheres into piles of valid candidates and shops
for sphere in locations_per_sphere:
current_shops_slots = []
current_candidates = []
shops_per_sphere.append(current_shops_slots)
candidates_per_sphere.append(current_candidates)
for location in sphere:
if isinstance(location, ALttPLocation) and location.shop_slot is not None:
if not location.shop_slot_disabled:
current_shops_slots.append(location)
elif not location.locked and location.item.name not in blacklist_words:
current_candidates.append(location)
if cumu_weights:
x = cumu_weights[-1]
else:
x = 0
cumu_weights.append(len(current_candidates) + x)
multiworld.random.shuffle(current_candidates)
del locations_per_sphere
for i, current_shop_slots in enumerate(shops_per_sphere):
if current_shop_slots:
# getting all candidates and shuffling them feels cpu expensive, there may be a better method
candidates = [(location, i) for i, candidates in enumerate(candidates_per_sphere[i:], start=i)
for location in candidates]
multiworld.random.shuffle(candidates)
for location in current_shop_slots:
shop: Shop = location.parent_region.shop
for index, (c, swapping_sphere_id) in enumerate(candidates): # chosen item locations
if c.item_rule(location.item) and location.item_rule(c.item):
swap_location_item(c, location, check_locked=False)
logger.debug(f"Swapping {c} into {location}:: {location.item}")
# remove candidate
candidates_per_sphere[swapping_sphere_id].remove(c)
candidates.pop(index)
break
else:
# This *should* never happen. But let's fail safely just in case.
logger.warning("Ran out of ShopShuffle Item candidate locations.")
location.shop_slot_disabled = True
continue
item_name = location.item.name
if location.item.game != "A Link to the Past":
if location.item.advancement:
price = multiworld.random.randrange(8, 56)
elif location.item.useful:
price = multiworld.random.randrange(4, 28)
else:
price = multiworld.random.randrange(2, 14)
elif any(x in item_name for x in
['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']):
price = multiworld.random.randrange(1, 7)
elif any(x in item_name for x in ['Arrow', 'Bomb', 'Clock']):
price = multiworld.random.randrange(2, 14)
elif any(x in item_name for x in ['Small Key', 'Heart']):
price = multiworld.random.randrange(4, 28)
else:
price = multiworld.random.randrange(8, 56)
shop.push_inventory(location.shop_slot, item_name,
min(int(price * multiworld.shop_price_modifier[location.player] / 100) * 5, 9999), 1,
location.item.player if location.item.player != location.player else 0)
if 'P' in multiworld.shop_shuffle[location.player]:
price_to_funny_price(multiworld, shop.inventory[location.shop_slot], location.player)
FillDisabledShopSlots(multiworld)
def create_shops(world, player: int):
option = world.shop_shuffle[player]
def create_shops(multiworld, player: int):
player_shop_table = shop_table.copy()
if "w" in option:
if multiworld.include_witch_hut[player]:
player_shop_table["Potion Shop"] = player_shop_table["Potion Shop"]._replace(locked=False)
dynamic_shop_slots = total_dynamic_shop_slots + 3
else:
dynamic_shop_slots = total_dynamic_shop_slots
if multiworld.shuffle_capacity_upgrades[player]:
player_shop_table["Capacity Upgrade"] = player_shop_table["Capacity Upgrade"]._replace(locked=False)
num_slots = min(dynamic_shop_slots, world.shop_item_slots[player])
num_slots = min(dynamic_shop_slots, multiworld.shop_item_slots[player])
single_purchase_slots: List[bool] = [True] * num_slots + [False] * (dynamic_shop_slots - num_slots)
world.random.shuffle(single_purchase_slots)
multiworld.random.shuffle(single_purchase_slots)
if 'g' in option or 'f' in option:
if multiworld.randomize_shop_inventories[player]:
default_shop_table = [i for l in
[shop_generation_types[x] for x in ['arrows', 'bombs', 'potions', 'shields', 'bottle'] if
not world.retro_bow[player] or x != 'arrows'] for i in l]
new_basic_shop = world.random.sample(default_shop_table, k=3)
new_dark_shop = world.random.sample(default_shop_table, k=3)
not multiworld.retro_bow[player] or x != 'arrows'] for i in l]
new_basic_shop = multiworld.random.sample(default_shop_table, k=3)
new_dark_shop = multiworld.random.sample(default_shop_table, k=3)
for name, shop in player_shop_table.items():
typ, shop_id, keeper, custom, locked, items, sram_offset = shop
if not locked:
new_items = world.random.sample(default_shop_table, k=3)
if 'f' not in option:
new_items = multiworld.random.sample(default_shop_table, k=len(items))
if multiworld.randomize_shop_inventories[player] == RandomizeShopInventories.option_randomize_by_shop_type:
if items == _basic_shop_defaults:
new_items = new_basic_shop
elif items == _dark_world_shop_defaults:
new_items = new_dark_shop
keeper = world.random.choice([0xA0, 0xC1, 0xFF])
keeper = multiworld.random.choice([0xA0, 0xC1, 0xFF])
player_shop_table[name] = ShopData(typ, shop_id, keeper, custom, locked, new_items, sram_offset)
if world.mode[player] == "inverted":
if multiworld.mode[player] == "inverted":
# make sure that blue potion is available in inverted, special case locked = None; lock when done.
player_shop_table["Dark Lake Hylia Shop"] = \
player_shop_table["Dark Lake Hylia Shop"]._replace(items=_inverted_hylia_shop_defaults, locked=None)
chance_100 = int(world.retro_bow[player]) * 0.25 + int(
world.smallkey_shuffle[player] == smallkey_shuffle.option_universal) * 0.5
for region_name, (room_id, type, shopkeeper, custom, locked, inventory, sram_offset) in player_shop_table.items():
region = world.get_region(region_name, player)
region = multiworld.get_region(region_name, player)
shop: Shop = shop_class_mapping[type](region, room_id, shopkeeper, custom, locked, sram_offset)
# special case: allow shop slots, but do not allow overwriting of base inventory behind them
if locked is None:
shop.locked = True
region.shop = shop
world.shops.append(shop)
multiworld.shops.append(shop)
for index, item in enumerate(inventory):
shop.add_inventory(index, *item)
if not locked and num_slots:
if not locked and (num_slots or type == ShopType.UpgradeShop):
slot_name = f"{region.name}{shop.slot_names[index]}"
loc = ALttPLocation(player, slot_name, address=shop_table_by_location[slot_name],
parent=region, hint_text="for sale")
loc.shop_price_type, loc.shop_price = get_price(multiworld, None, player)
loc.item_rule = lambda item, spot=loc: not any(i for i in price_blacklist[spot.shop_price_type] if i in item.name)
add_rule(loc, lambda state, spot=loc: shop_price_rules(state, player, spot))
loc.shop = shop
loc.shop_slot = index
loc.locked = True
if single_purchase_slots.pop():
if world.goal[player] != 'icerodhunt':
if world.random.random() < chance_100:
additional_item = 'Rupees (100)'
else:
additional_item = 'Rupees (50)'
else:
additional_item = GetBeemizerItem(world, player, 'Nothing')
loc.item = ItemFactory(additional_item, player)
else:
loc.item = ItemFactory(GetBeemizerItem(world, player, 'Nothing'), player)
if ((not (multiworld.shuffle_capacity_upgrades[player] and type == ShopType.UpgradeShop))
and not single_purchase_slots.pop()):
loc.shop_slot_disabled = True
shop.region.locations.append(loc)
loc.locked = True
else:
shop.region.locations.append(loc)
class ShopData(NamedTuple):
@@ -387,9 +277,10 @@ total_dynamic_shop_slots = sum(3 for shopname, data in shop_table.items() if not
SHOP_ID_START = 0x400000
shop_table_by_location_id = dict(enumerate(
(f"{name}{Shop.slot_names[num]}" for name, shop_data in
sorted(shop_table.items(), key=lambda item: item[1].sram_offset)
for num in range(3)), start=SHOP_ID_START))
(f"{name}{UpgradeShop.slot_names[num]}" if shop_data.type == ShopType.UpgradeShop else
f"{name}{Shop.slot_names[num]}" for name, shop_data in sorted(shop_table.items(),
key=lambda item: item[1].sram_offset)
for num in range(2 if shop_data.type == ShopType.UpgradeShop else 3)), start=SHOP_ID_START))
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots)] = "Old Man Sword Cave"
shop_table_by_location_id[(SHOP_ID_START + total_shop_slots + 1)] = "Take-Any #1"
@@ -409,114 +300,54 @@ shop_generation_types = {
}
def set_up_shops(world, player: int):
def set_up_shops(multiworld, player: int):
# TODO: move hard+ mode changes for shields here, utilizing the new shops
if world.retro_bow[player]:
rss = world.get_region('Red Shield Shop', player).shop
if multiworld.retro_bow[player]:
rss = multiworld.get_region('Red Shield Shop', player).shop
replacement_items = [['Red Potion', 150], ['Green Potion', 75], ['Blue Potion', 200], ['Bombs (10)', 50],
['Blue Shield', 50], ['Small Heart',
10]] # Can't just replace the single arrow with 10 arrows as retro doesn't need them.
if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal:
replacement_items.append(['Small Key (Universal)', 100])
replacement_item = world.random.choice(replacement_items)
replacement_item = multiworld.random.choice(replacement_items)
rss.add_inventory(2, 'Single Arrow', 80, 1, replacement_item[0], replacement_item[1])
rss.locked = True
if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal or world.retro_bow[player]:
for shop in world.random.sample([s for s in world.shops if
s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player],
5):
if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal or multiworld.retro_bow[player]:
for shop in multiworld.random.sample([s for s in multiworld.shops if
s.custom and not s.locked and s.type == ShopType.Shop
and s.region.player == player], 5):
shop.locked = True
slots = [0, 1, 2]
world.random.shuffle(slots)
multiworld.random.shuffle(slots)
slots = iter(slots)
if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal:
shop.add_inventory(next(slots), 'Small Key (Universal)', 100)
if world.retro_bow[player]:
if multiworld.retro_bow[player]:
shop.push_inventory(next(slots), 'Single Arrow', 80)
def shuffle_shops(world, items, player: int):
option = world.shop_shuffle[player]
if 'u' in option:
progressive = world.progressive[player]
progressive = world.random.choice([True, False]) if progressive == 'grouped_random' else progressive == 'on'
progressive &= world.goal == 'icerodhunt'
new_items = ["Bomb Upgrade (+5)"] * 6
new_items.append("Bomb Upgrade (+5)" if progressive else "Bomb Upgrade (+10)")
if not world.retro_bow[player]:
new_items += ["Arrow Upgrade (+5)"] * 6
new_items.append("Arrow Upgrade (+5)" if progressive else "Arrow Upgrade (+10)")
world.random.shuffle(new_items) # Decide what gets tossed randomly if it can't insert everything.
capacityshop: Optional[Shop] = None
for shop in world.shops:
if multiworld.shuffle_capacity_upgrades[player]:
for shop in multiworld.shops:
if shop.type == ShopType.UpgradeShop and shop.region.player == player and \
shop.region.name == "Capacity Upgrade":
shop.clear_inventory()
capacityshop = shop
if world.goal[player] != 'icerodhunt':
for i, item in enumerate(items):
if item.name in trap_replaceable:
items[i] = ItemFactory(new_items.pop(), player)
if not new_items:
break
else:
logging.warning(
f"Not all upgrades put into Player{player}' item pool. Putting remaining items in Capacity Upgrade shop instead.")
bombupgrades = sum(1 for item in new_items if 'Bomb Upgrade' in item)
arrowupgrades = sum(1 for item in new_items if 'Arrow Upgrade' in item)
slots = iter(range(2))
if bombupgrades:
capacityshop.add_inventory(next(slots), 'Bomb Upgrade (+5)', 100, bombupgrades)
if arrowupgrades:
capacityshop.add_inventory(next(slots), 'Arrow Upgrade (+5)', 100, arrowupgrades)
else:
for item in new_items:
world.push_precollected(ItemFactory(item, player))
if any(setting in option for setting in 'ipP'):
if (multiworld.shuffle_shop_inventories[player] or multiworld.randomize_shop_prices[player]
or multiworld.randomize_cost_types[player]):
shops = []
upgrade_shops = []
total_inventory = []
for shop in world.shops:
for shop in multiworld.shops:
if shop.region.player == player:
if shop.type == ShopType.UpgradeShop:
upgrade_shops.append(shop)
elif shop.type == ShopType.Shop and not shop.locked:
if shop.type == ShopType.Shop and not shop.locked:
shops.append(shop)
total_inventory.extend(shop.inventory)
if 'p' in option:
def price_adjust(price: int) -> int:
# it is important that a base price of 0 always returns 0 as new price!
adjust = 2 if price < 100 else 5
return int((price / adjust) * (0.5 + world.random.random() * 1.5)) * adjust
for item in total_inventory:
item["price_type"], item["price"] = get_price(multiworld, item, player)
def adjust_item(item):
if item:
item["price"] = price_adjust(item["price"])
item['replacement_price'] = price_adjust(item["price"])
for item in total_inventory:
adjust_item(item)
for shop in upgrade_shops:
for item in shop.inventory:
adjust_item(item)
if 'P' in option:
for item in total_inventory:
price_to_funny_price(world, item, player)
# Don't apply to upgrade shops
# Upgrade shop is only one place, and will generally be too easy to
# replenish hearts and bombs
if 'i' in option:
world.random.shuffle(total_inventory)
if multiworld.shuffle_shop_inventories[player]:
multiworld.random.shuffle(total_inventory)
i = 0
for shop in shops:
@@ -539,16 +370,18 @@ price_blacklist = {
}
price_chart = {
ShopPriceType.Rupees: lambda p: p,
ShopPriceType.Hearts: lambda p: min(5, p // 5) * 8, # Each heart is 0x8 in memory, Max of 5 hearts (20 total??)
ShopPriceType.Magic: lambda p: min(15, p // 5) * 8, # Each pip is 0x8 in memory, Max of 15 pips (16 total...)
ShopPriceType.Bombs: lambda p: max(1, min(10, p // 5)), # 10 Bombs max
ShopPriceType.Arrows: lambda p: max(1, min(30, p // 5)), # 30 Arrows Max
ShopPriceType.HeartContainer: lambda p: 0x8,
ShopPriceType.BombUpgrade: lambda p: 0x1,
ShopPriceType.ArrowUpgrade: lambda p: 0x1,
ShopPriceType.Keys: lambda p: min(3, (p // 100) + 1), # Max of 3 keys for a price
ShopPriceType.Potion: lambda p: (p // 5) % 5,
ShopPriceType.Rupees: lambda p, d: p,
# Each heart is 0x8 in memory, Max of 19 hearts on easy/normal, 9 on hard, 7 on expert
ShopPriceType.Hearts: lambda p, d: max(8, min([19, 19, 9, 7][d], p // 14) * 8),
# Each pip is 0x8 in memory, Max of 15 pips (16 total)
ShopPriceType.Magic: lambda p, d: max(8, min(15, p // 18) * 8),
ShopPriceType.Bombs: lambda p, d: max(1, min(50, p // 5)), # 50 Bombs max
ShopPriceType.Arrows: lambda p, d: max(1, min(70, p // 4)), # 70 Arrows Max
ShopPriceType.HeartContainer: lambda p, d: 0x8,
ShopPriceType.BombUpgrade: lambda p, d: 0x1,
ShopPriceType.ArrowUpgrade: lambda p, d: 0x1,
ShopPriceType.Keys: lambda p, d: max(1, min(3, (p // 90) + 1)), # Max of 3 keys for a price
ShopPriceType.Potion: lambda p, d: (p // 5) % 5,
}
price_type_display_name = {
@@ -557,6 +390,8 @@ price_type_display_name = {
ShopPriceType.Bombs: "Bombs",
ShopPriceType.Arrows: "Arrows",
ShopPriceType.Keys: "Keys",
ShopPriceType.Item: "Item",
ShopPriceType.Magic: "Magic"
}
# price division
@@ -565,57 +400,74 @@ price_rate_display = {
ShopPriceType.Magic: 8,
}
# prices with no? logic requirements
simple_price_types = [
ShopPriceType.Rupees,
ShopPriceType.Hearts,
ShopPriceType.Bombs,
ShopPriceType.Arrows,
ShopPriceType.Keys
]
def get_price_modifier(item):
if item.game == "A Link to the Past":
if any(x in item.name for x in
['Compass', 'Map', 'Single Bomb', 'Single Arrow', 'Piece of Heart']):
return 0.125
elif any(x in item.name for x in
['Arrow', 'Bomb', 'Clock']) and item.name != "Bombos" and "(50)" not in item.name:
return 0.25
elif any(x in item.name for x in ['Small Key', 'Heart']):
return 0.5
else:
return 1
if item.advancement:
return 1
elif item.useful:
return 0.5
else:
return 0.25
def price_to_funny_price(world, item: dict, player: int):
"""
Converts a raw Rupee price into a special price type
"""
def get_price(multiworld, item, player: int, price_type=None):
"""Converts a raw Rupee price into a special price type"""
if price_type:
price_types = [price_type]
else:
price_types = [ShopPriceType.Rupees] # included as a chance to not change price
if multiworld.randomize_cost_types[player]:
price_types += [
ShopPriceType.Hearts,
ShopPriceType.Bombs,
ShopPriceType.Magic,
]
if multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal:
if item and item["item"] == "Small Key (Universal)":
price_types = [ShopPriceType.Rupees, ShopPriceType.Magic] # no logical requirements for repeatable keys
else:
price_types.append(ShopPriceType.Keys)
if multiworld.retro_bow[player]:
if item and item["item"] == "Single Arrow":
price_types = [ShopPriceType.Rupees, ShopPriceType.Magic] # no logical requirements for arrows
else:
price_types.append(ShopPriceType.Arrows)
diff = multiworld.item_pool[player].value
if item:
price_types = [
ShopPriceType.Rupees, # included as a chance to not change price type
ShopPriceType.Hearts,
ShopPriceType.Bombs,
]
# don't pay in universal keys to get access to universal keys
if world.smallkey_shuffle[player] == smallkey_shuffle.option_universal \
and not "Small Key (Universal)" == item['replacement']:
price_types.append(ShopPriceType.Keys)
if not world.retro_bow[player]:
price_types.append(ShopPriceType.Arrows)
world.random.shuffle(price_types)
# This is for a shop's regular inventory, the item is already determined, and we will decide the price here
price = item["price"]
if multiworld.randomize_shop_prices[player]:
adjust = 2 if price < 100 else 5
price = int((price / adjust) * (0.5 + multiworld.random.random() * 1.5)) * adjust
multiworld.random.shuffle(price_types)
for p_type in price_types:
# Ignore rupee prices
if p_type == ShopPriceType.Rupees:
return
if any(x in item['item'] for x in price_blacklist[p_type]):
continue
else:
item['price'] = min(price_chart[p_type](item['price']), 255)
item['price_type'] = p_type
break
return p_type, price_chart[p_type](price, diff)
else:
# This is an AP location and the price will be adjusted after an item is shuffled into it
p_type = multiworld.random.choice(price_types)
return p_type, price_chart[p_type](min(int(multiworld.random.randint(8, 56)
* multiworld.shop_price_modifier[player] / 100) * 5, 9999), diff)
def create_dynamic_shop_locations(world, player):
for shop in world.shops:
if shop.region.player == player:
for i, item in enumerate(shop.inventory):
if item is None:
continue
if item['create_location']:
slot_name = f"{shop.region.name}{shop.slot_names[i]}"
loc = ALttPLocation(player, slot_name,
address=shop_table_by_location[slot_name], parent=shop.region)
loc.place_locked_item(ItemFactory(item['item'], player))
if shop.type == ShopType.TakeAny:
loc.shop_slot_disabled = True
shop.region.locations.append(loc)
loc.shop_slot = i
def shop_price_rules(state: CollectionState, player: int, location: ALttPLocation):
if location.shop_price_type == ShopPriceType.Hearts:
return has_hearts(state, player, (location.shop_price / 8) + 1)
elif location.shop_price_type == ShopPriceType.Bombs:
return can_use_bombs(state, player, location.shop_price)
elif location.shop_price_type == ShopPriceType.Arrows:
return can_hold_arrows(state, player, location.shop_price)
return True

View File

@@ -10,7 +10,7 @@ def is_not_bunny(state: CollectionState, region: LTTPRegion, player: int) -> boo
def can_bomb_clip(state: CollectionState, region: LTTPRegion, player: int) -> bool:
return is_not_bunny(state, region, player) and state.has('Pegasus Boots', player)
return can_use_bombs(state, player) and is_not_bunny(state, region, player) and state.has('Pegasus Boots', player)
def can_buy_unlimited(state: CollectionState, item: str, player: int) -> bool:
@@ -83,13 +83,47 @@ def can_extend_magic(state: CollectionState, player: int, smallmagic: int = 16,
return basemagic >= smallmagic
def can_hold_arrows(state: CollectionState, player: int, quantity: int):
arrows = 30 + ((state.count("Arrow Upgrade (+5)", player) * 5) + (state.count("Arrow Upgrade (+10)", player) * 10)
+ (state.count("Bomb Upgrade (50)", player) * 50))
# Arrow Upgrade (+5) beyond the 6th gives +10
arrows += max(0, ((state.count("Arrow Upgrade (+5)", player) - 6) * 10))
return min(70, arrows) >= quantity
def can_use_bombs(state: CollectionState, player: int, quantity: int = 1) -> bool:
bombs = 0 if state.multiworld.bombless_start[player] else 10
bombs += ((state.count("Bomb Upgrade (+5)", player) * 5) + (state.count("Bomb Upgrade (+10)", player) * 10)
+ (state.count("Bomb Upgrade (50)", player) * 50))
# Bomb Upgrade (+5) beyond the 6th gives +10
bombs += max(0, ((state.count("Bomb Upgrade (+5)", player) - 6) * 10))
if (not state.multiworld.shuffle_capacity_upgrades[player]) and state.has("Capacity Upgrade Shop", player):
bombs += 40
return bombs >= min(quantity, 50)
def can_bomb_or_bonk(state: CollectionState, player: int) -> bool:
return state.has("Pegasus Boots", player) or can_use_bombs(state, player)
def can_kill_most_things(state: CollectionState, player: int, enemies: int = 5) -> bool:
return (has_melee_weapon(state, player)
or state.has('Cane of Somaria', player)
or (state.has('Cane of Byrna', player) and (enemies < 6 or can_extend_magic(state, player)))
or can_shoot_arrows(state, player)
or state.has('Fire Rod', player)
or (state.has('Bombs (10)', player) and enemies < 6))
if state.multiworld.enemy_shuffle[player]:
# I don't fully understand Enemizer's logic for placing enemies in spots where they need to be killable, if any.
# Just go with maximal requirements for now.
return (has_melee_weapon(state, player)
and state.has('Cane of Somaria', player)
and state.has('Cane of Byrna', player) and can_extend_magic(state, player)
and can_shoot_arrows(state, player)
and state.has('Fire Rod', player)
and can_use_bombs(state, player, enemies * 4))
else:
return (has_melee_weapon(state, player)
or state.has('Cane of Somaria', player)
or (state.has('Cane of Byrna', player) and (enemies < 6 or can_extend_magic(state, player)))
or can_shoot_arrows(state, player)
or state.has('Fire Rod', player)
or (state.multiworld.enemy_health[player] in ("easy", "default")
and can_use_bombs(state, player, enemies * 4)))
def can_get_good_bee(state: CollectionState, player: int) -> bool:
@@ -159,4 +193,4 @@ def can_get_glitched_speed_dw(state: CollectionState, player: int) -> bool:
rules = [state.has('Pegasus Boots', player), any([state.has('Hookshot', player), has_sword(state, player)])]
if state.multiworld.mode[player] != 'inverted':
rules.append(state.has('Moon Pearl', player))
return all(rules)
return all(rules)

View File

@@ -14,9 +14,12 @@ class ALttPLocation(Location):
crystal: bool
player_address: Optional[int]
_hint_text: Optional[str]
shop: None
shop_slot: Optional[int] = None
"""If given as integer, shop_slot is the shop's inventory index."""
shop_slot_disabled: bool = False
shop_price = 0
shop_price_type = None
parent_region: "LTTPRegion"
def __init__(self, player: int, name: str, address: Optional[int] = None, crystal: bool = False,

View File

@@ -42,7 +42,7 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du
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
if not fix_dungeon_exits: # vanilla, simple, restricted, dungeons_simple; 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.
if dungeon_entrance.name == 'Skull Woods Final Section':
set_rule(clip, lambda state: False) # entrance doesn't exist until you fire rod it from the other side
@@ -52,12 +52,12 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du
add_rule(clip, lambda state: state.has('Cape', player) or has_beam_sword(state, player) or state.has('Beat Agahnim 1', player)) # kill/bypass barrier
# Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally.
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
elif not fix_fake_worlds: # full, dungeons_full; 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.
add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player)))
# exiting restriction
add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
# Otherwise, the shuffle type is crossed, dungeonscrossed, or insanity; all of these do not need additional rules on where we can go,
# Otherwise, the shuffle type is crossed, dungeons_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.
@@ -93,7 +93,7 @@ def underworld_glitches_rules(world, player):
# 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']:
if world.entrance_shuffle[player] in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
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))

View File

@@ -13,14 +13,14 @@ from .EntranceShuffle import link_entrances, link_inverted_entrances, plando_con
from .InvertedRegions import create_inverted_regions, mark_dark_world_regions
from .ItemPool import generate_itempool, difficulties
from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem
from .Options import alttp_options, smallkey_shuffle
from .Options import alttp_options, small_key_shuffle
from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions, lookup_vanilla_location_to_entrance, \
is_main_entrance, key_drop_data
from .Client import ALTTPSNIClient
from .Rom import LocalRom, patch_rom, patch_race_rom, check_enemizer, patch_enemizer, apply_rom_settings, \
get_hash_string, get_base_rom_path, LttPDeltaPatch
from .Rules import set_rules
from .Shops import create_shops, Shop, ShopSlotFill, ShopType, price_rate_display, price_type_display_name
from .Shops import create_shops, Shop, push_shop_inventories, ShopType, price_rate_display, price_type_display_name
from .SubClasses import ALttPItem, LTTPRegionType
from worlds.AutoWorld import World, WebWorld, LogicMixin
from .StateHelpers import can_buy_unlimited
@@ -42,7 +42,7 @@ class ALTTPSettings(settings.Group):
class ALTTPWeb(WebWorld):
setup_en = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago ALttP Software on your computer. This guide covers single-player, multiworld, and related software.",
"English",
"multiworld_en.md",
@@ -78,7 +78,7 @@ class ALTTPWeb(WebWorld):
)
msu = Tutorial(
"MSU-1 Setup Tutorial",
"MSU-1 Setup Guide",
"A guide to setting up MSU-1, which allows for custom in-game music.",
"English",
"msu1_en.md",
@@ -105,7 +105,7 @@ class ALTTPWeb(WebWorld):
)
plando = Tutorial(
"Plando Tutorial",
"Plando Guide",
"A guide to creating Multiworld Plandos with LTTP",
"English",
"plando_en.md",
@@ -213,7 +213,7 @@ class ALTTPWorld(World):
item_name_to_id = {name: data.item_code for name, data in item_table.items() if type(data.item_code) == int}
location_name_to_id = lookup_name_to_id
data_version = 8
data_version = 9
required_client_version = (0, 4, 1)
web = ALTTPWeb()
@@ -290,33 +290,34 @@ class ALTTPWorld(World):
self.pyramid_fairy_bottle_fill = self.random.choice(bottle_options)
if multiworld.mode[player] == 'standard':
if multiworld.smallkey_shuffle[player]:
if (multiworld.smallkey_shuffle[player] not in
(smallkey_shuffle.option_universal, smallkey_shuffle.option_own_dungeons,
smallkey_shuffle.option_start_with)):
if multiworld.small_key_shuffle[player]:
if (multiworld.small_key_shuffle[player] not in
(small_key_shuffle.option_universal, small_key_shuffle.option_own_dungeons,
small_key_shuffle.option_start_with)):
self.multiworld.local_early_items[self.player]["Small Key (Hyrule Castle)"] = 1
self.multiworld.local_items[self.player].value.add("Small Key (Hyrule Castle)")
self.multiworld.non_local_items[self.player].value.discard("Small Key (Hyrule Castle)")
if multiworld.bigkey_shuffle[player]:
if multiworld.big_key_shuffle[player]:
self.multiworld.local_items[self.player].value.add("Big Key (Hyrule Castle)")
self.multiworld.non_local_items[self.player].value.discard("Big Key (Hyrule Castle)")
# system for sharing ER layouts
self.er_seed = str(multiworld.random.randint(0, 2 ** 64))
if "-" in multiworld.shuffle[player]:
shuffle, seed = multiworld.shuffle[player].split("-", 1)
multiworld.shuffle[player] = shuffle
if multiworld.entrance_shuffle[player] != "vanilla" and multiworld.entrance_shuffle_seed[player] != "random":
shuffle = multiworld.entrance_shuffle[player].current_key
if shuffle == "vanilla":
self.er_seed = "vanilla"
elif seed.startswith("group-") or multiworld.is_race:
elif (not multiworld.entrance_shuffle_seed[player].value.isdigit()) or multiworld.is_race:
self.er_seed = get_same_seed(multiworld, (
shuffle, seed, multiworld.retro_caves[player], multiworld.mode[player], multiworld.logic[player]))
shuffle, multiworld.entrance_shuffle_seed[player].value, multiworld.retro_caves[player], multiworld.mode[player],
multiworld.glitches_required[player]))
else: # not a race or group seed, use set seed as is.
self.er_seed = seed
elif multiworld.shuffle[player] == "vanilla":
self.er_seed = int(multiworld.entrance_shuffle_seed[player].value)
elif multiworld.entrance_shuffle[player] == "vanilla":
self.er_seed = "vanilla"
for dungeon_item in ["smallkey_shuffle", "bigkey_shuffle", "compass_shuffle", "map_shuffle"]:
for dungeon_item in ["small_key_shuffle", "big_key_shuffle", "compass_shuffle", "map_shuffle"]:
option = getattr(multiworld, dungeon_item)[player]
if option == "own_world":
multiworld.local_items[player].value |= self.item_name_groups[option.item_name_group]
@@ -329,10 +330,10 @@ class ALTTPWorld(World):
if option == "original_dungeon":
self.dungeon_specific_item_names |= self.item_name_groups[option.item_name_group]
multiworld.difficulty_requirements[player] = difficulties[multiworld.difficulty[player]]
multiworld.difficulty_requirements[player] = difficulties[multiworld.item_pool[player].current_key]
# enforce pre-defined local items.
if multiworld.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
if multiworld.goal[player] in ["local_triforce_hunt", "local_ganon_triforce_hunt"]:
multiworld.local_items[player].value.add('Triforce Piece')
# Not possible to place crystals outside boss prizes yet (might as well make it consistent with pendants too).
@@ -345,9 +346,6 @@ class ALTTPWorld(World):
player = self.player
world = self.multiworld
world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player],
world.triforce_pieces_required[player])
if world.mode[player] != 'inverted':
create_regions(world, player)
else:
@@ -355,8 +353,8 @@ class ALTTPWorld(World):
create_shops(world, player)
self.create_dungeons()
if world.logic[player] not in ["noglitches", "minorglitches"] and world.shuffle[player] in \
{"vanilla", "dungeonssimple", "dungeonsfull", "simple", "restricted", "full"}:
if world.glitches_required[player] not in ["no_glitches", "minor_glitches"] and world.entrance_shuffle[player] in \
{"vanilla", "dungeons_simple", "dungeons_full", "simple", "restricted", "full"}:
world.fix_fake_world[player] = False
# seeded entrance shuffle
@@ -455,7 +453,7 @@ class ALTTPWorld(World):
if state.has('Silver Bow', item.player):
return
elif state.has('Bow', item.player) and (self.multiworld.difficulty_requirements[item.player].progressive_bow_limit >= 2
or self.multiworld.logic[item.player] == 'noglitches'
or self.multiworld.glitches_required[item.player] == 'no_glitches'
or self.multiworld.swordless[item.player]): # modes where silver bow is always required for ganon
return 'Silver Bow'
elif self.multiworld.difficulty_requirements[item.player].progressive_bow_limit >= 1:
@@ -499,9 +497,9 @@ class ALTTPWorld(World):
break
else:
raise FillError('Unable to place dungeon prizes')
if world.mode[player] == 'standard' and world.smallkey_shuffle[player] \
and world.smallkey_shuffle[player] != smallkey_shuffle.option_universal and \
world.smallkey_shuffle[player] != smallkey_shuffle.option_own_dungeons:
if world.mode[player] == 'standard' and world.small_key_shuffle[player] \
and world.small_key_shuffle[player] != small_key_shuffle.option_universal and \
world.small_key_shuffle[player] != small_key_shuffle.option_own_dungeons:
world.local_early_items[player]["Small Key (Hyrule Castle)"] = 1
@classmethod
@@ -509,10 +507,9 @@ class ALTTPWorld(World):
from .Dungeons import fill_dungeons_restrictive
fill_dungeons_restrictive(world)
@classmethod
def stage_post_fill(cls, world):
ShopSlotFill(world)
push_shop_inventories(world)
@property
def use_enemizer(self) -> bool:
@@ -579,7 +576,7 @@ class ALTTPWorld(World):
@classmethod
def stage_extend_hint_information(cls, world, hint_data: typing.Dict[int, typing.Dict[int, str]]):
er_hint_data = {player: {} for player in world.get_game_players("A Link to the Past") if
world.shuffle[player] != "vanilla" or world.retro_caves[player]}
world.entrance_shuffle[player] != "vanilla" or world.retro_caves[player]}
for region in world.regions:
if region.player in er_hint_data and region.locations:
@@ -645,9 +642,9 @@ class ALTTPWorld(World):
trash_counts = {}
for player in world.get_game_players("A Link to the Past"):
if not world.ganonstower_vanilla[player] or \
world.logic[player] in {'owglitches', 'hybridglitches', "nologic"}:
world.glitches_required[player] in {'overworld_glitches', 'hybrid_major_glitches', "no_logic"}:
pass
elif 'triforcehunt' in world.goal[player] and ('local' in world.goal[player] or world.players == 1):
elif 'triforce_hunt' in world.goal[player].current_key and ('local' in world.goal[player].current_key or world.players == 1):
trash_counts[player] = world.random.randint(world.crystals_needed_for_gt[player] * 2,
world.crystals_needed_for_gt[player] * 4)
else:
@@ -681,35 +678,6 @@ class ALTTPWorld(World):
return variable
return "Yes" if variable else "No"
spoiler_handle.write('Logic: %s\n' % self.multiworld.logic[self.player])
spoiler_handle.write('Dark Room Logic: %s\n' % self.multiworld.dark_room_logic[self.player])
spoiler_handle.write('Mode: %s\n' % self.multiworld.mode[self.player])
spoiler_handle.write('Goal: %s\n' % self.multiworld.goal[self.player])
if "triforce" in self.multiworld.goal[self.player]: # triforce hunt
spoiler_handle.write("Pieces available for Triforce: %s\n" %
self.multiworld.triforce_pieces_available[self.player])
spoiler_handle.write("Pieces required for Triforce: %s\n" %
self.multiworld.triforce_pieces_required[self.player])
spoiler_handle.write('Difficulty: %s\n' % self.multiworld.difficulty[self.player])
spoiler_handle.write('Item Functionality: %s\n' % self.multiworld.item_functionality[self.player])
spoiler_handle.write('Entrance Shuffle: %s\n' % self.multiworld.shuffle[self.player])
if self.multiworld.shuffle[self.player] != "vanilla":
spoiler_handle.write('Entrance Shuffle Seed %s\n' % self.er_seed)
spoiler_handle.write('Shop inventory shuffle: %s\n' %
bool_to_text("i" in self.multiworld.shop_shuffle[self.player]))
spoiler_handle.write('Shop price shuffle: %s\n' %
bool_to_text("p" in self.multiworld.shop_shuffle[self.player]))
spoiler_handle.write('Shop upgrade shuffle: %s\n' %
bool_to_text("u" in self.multiworld.shop_shuffle[self.player]))
spoiler_handle.write('New Shop inventory: %s\n' %
bool_to_text("g" in self.multiworld.shop_shuffle[self.player] or
"f" in self.multiworld.shop_shuffle[self.player]))
spoiler_handle.write('Custom Potion Shop: %s\n' %
bool_to_text("w" in self.multiworld.shop_shuffle[self.player]))
spoiler_handle.write('Enemy health: %s\n' % self.multiworld.enemy_health[self.player])
spoiler_handle.write('Enemy damage: %s\n' % self.multiworld.enemy_damage[self.player])
spoiler_handle.write('Prize shuffle %s\n' % self.multiworld.shuffle_prizes[self.player])
def write_spoiler(self, spoiler_handle: typing.TextIO) -> None:
player_name = self.multiworld.get_player_name(self.player)
spoiler_handle.write("\n\nMedallions:\n")
@@ -783,7 +751,7 @@ class ALTTPWorld(World):
if item["replacement"] is None:
continue
shop_data["item_{}".format(index)] +=\
f", {item['replacement']} - {item['replacement_price']}" \
f", {item['replacement']} - {item['replacement_price'] // price_rate_display.get(item['replacement_price_type'], 1)}" \
f" {price_type_display_name[item['replacement_price_type']]}"
return shop_data
@@ -796,10 +764,7 @@ class ALTTPWorld(World):
item)))
def get_filler_item_name(self) -> str:
if self.multiworld.goal[self.player] == "icerodhunt":
item = "Nothing"
else:
item = self.multiworld.random.choice(extras_list)
item = self.multiworld.random.choice(extras_list)
return GetBeemizerItem(self.multiworld, self.player, item)
def get_pre_fill_items(self):
@@ -819,20 +784,20 @@ class ALTTPWorld(World):
# for convenient auto-tracking of the generated settings and adjusting the tracker accordingly
slot_options = ["crystals_needed_for_gt", "crystals_needed_for_ganon", "open_pyramid",
"bigkey_shuffle", "smallkey_shuffle", "compass_shuffle", "map_shuffle",
"big_key_shuffle", "small_key_shuffle", "compass_shuffle", "map_shuffle",
"progressive", "swordless", "retro_bow", "retro_caves", "shop_item_slots",
"boss_shuffle", "pot_shuffle", "enemy_shuffle", "key_drop_shuffle"]
"boss_shuffle", "pot_shuffle", "enemy_shuffle", "key_drop_shuffle", "bombless_start",
"randomize_shop_inventories", "shuffle_shop_inventories", "shuffle_capacity_upgrades",
"entrance_shuffle", "dark_room_logic", "goal", "mode",
"triforce_pieces_mode", "triforce_pieces_percentage", "triforce_pieces_required",
"triforce_pieces_available", "triforce_pieces_extra",
]
slot_data = {option_name: getattr(self.multiworld, option_name)[self.player].value for option_name in slot_options}
slot_data.update({
'mode': self.multiworld.mode[self.player],
'goal': self.multiworld.goal[self.player],
'dark_room_logic': self.multiworld.dark_room_logic[self.player],
'mm_medalion': self.multiworld.required_medallions[self.player][0],
'tr_medalion': self.multiworld.required_medallions[self.player][1],
'shop_shuffle': self.multiworld.shop_shuffle[self.player],
'entrance_shuffle': self.multiworld.shuffle[self.player],
}
)
return slot_data
@@ -849,8 +814,8 @@ def get_same_seed(world, seed_def: tuple) -> str:
class ALttPLogic(LogicMixin):
def _lttp_has_key(self, item, player, count: int = 1):
if self.multiworld.logic[player] == 'nologic':
if self.multiworld.glitches_required[player] == 'no_logic':
return True
if self.multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal:
if self.multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal:
return can_buy_unlimited(self, 'Small Key (Universal)', player)
return self.prog_items[player][item] >= count

View File

@@ -7,25 +7,25 @@ class TestAgahnimsTower(TestDungeon):
self.starting_regions = ['Agahnims Tower']
self.run_tests([
["Castle Tower - Room 03", False, []],
["Castle Tower - Room 03", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Room 03", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Room 03", True, ['Progressive Sword']],
["Castle Tower - Dark Maze", False, []],
["Castle Tower - Dark Maze", False, [], ['Small Key (Agahnims Tower)']],
["Castle Tower - Dark Maze", False, [], ['Lamp']],
["Castle Tower - Dark Maze", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Dark Maze", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Dark Maze", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Lamp']],
["Castle Tower - Dark Archer Key Drop", False, []],
["Castle Tower - Dark Archer Key Drop", False, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)']],
["Castle Tower - Dark Archer Key Drop", False, [], ['Lamp']],
["Castle Tower - Dark Archer Key Drop", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Dark Archer Key Drop", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Dark Archer Key Drop", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp']],
["Castle Tower - Circle of Pots Key Drop", False, []],
["Castle Tower - Circle of Pots Key Drop", False, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)']],
["Castle Tower - Circle of Pots Key Drop", False, [], ['Lamp']],
["Castle Tower - Circle of Pots Key Drop", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Circle of Pots Key Drop", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Circle of Pots Key Drop", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp']],
["Agahnim 1", False, []],

View File

@@ -11,29 +11,37 @@ class TestDarkPalace(TestDungeon):
["Palace of Darkness - The Arena - Ledge", False, []],
["Palace of Darkness - The Arena - Ledge", False, [], ['Progressive Bow']],
["Palace of Darkness - The Arena - Ledge", True, ['Progressive Bow']],
["Palace of Darkness - The Arena - Ledge", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Palace of Darkness - The Arena - Ledge", True, ['Progressive Bow', 'Bomb Upgrade (+5)']],
["Palace of Darkness - Map Chest", False, []],
["Palace of Darkness - Map Chest", False, [], ['Progressive Bow']],
["Palace of Darkness - Map Chest", True, ['Progressive Bow']],
["Palace of Darkness - Map Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Palace of Darkness - Map Chest", True, ['Progressive Bow', 'Bomb Upgrade (+5)']],
["Palace of Darkness - Map Chest", True, ['Progressive Bow', 'Pegasus Boots']],
#Lower requirement for self-locking key
#No lower requirement when bow/hammer is out of logic
["Palace of Darkness - Big Key Chest", False, []],
["Palace of Darkness - Big Key Chest", False, [key]*5, [key]],
["Palace of Darkness - Big Key Chest", True, [key]*6],
["Palace of Darkness - Big Key Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Palace of Darkness - Big Key Chest", True, [key]*6 + ['Bomb Upgrade (+5)']],
["Palace of Darkness - The Arena - Bridge", False, []],
["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Progressive Bow']],
["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Hammer']],
["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Palace of Darkness - The Arena - Bridge", True, [key]],
["Palace of Darkness - The Arena - Bridge", True, ['Progressive Bow', 'Hammer']],
["Palace of Darkness - The Arena - Bridge", True, ['Progressive Bow', 'Hammer', 'Bomb Upgrade (+5)']],
["Palace of Darkness - The Arena - Bridge", True, ['Progressive Bow', 'Hammer', 'Pegasus Boots']],
["Palace of Darkness - Stalfos Basement", False, []],
["Palace of Darkness - Stalfos Basement", False, [], [key, 'Progressive Bow']],
["Palace of Darkness - Stalfos Basement", False, [], [key, 'Hammer']],
["Palace of Darkness - Stalfos Basement", False, [], [key, 'Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Palace of Darkness - Stalfos Basement", True, [key]],
["Palace of Darkness - Stalfos Basement", True, ['Progressive Bow', 'Hammer']],
["Palace of Darkness - Stalfos Basement", True, ['Progressive Bow', 'Hammer', 'Bomb Upgrade (+5)']],
["Palace of Darkness - Stalfos Basement", True, ['Progressive Bow', 'Hammer', 'Pegasus Boots']],
["Palace of Darkness - Compass Chest", False, []],
["Palace of Darkness - Compass Chest", False, [key]*3, [key]],
@@ -67,8 +75,9 @@ class TestDarkPalace(TestDungeon):
["Palace of Darkness - Big Chest", False, []],
["Palace of Darkness - Big Chest", False, [], ['Lamp']],
["Palace of Darkness - Big Chest", False, [], ['Big Key (Palace of Darkness)']],
["Palace of Darkness - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Palace of Darkness - Big Chest", False, [key]*5, [key]],
["Palace of Darkness - Big Chest", True, ['Lamp', 'Big Key (Palace of Darkness)'] + [key]*6],
["Palace of Darkness - Big Chest", True, ['Bomb Upgrade (+5)', 'Lamp', 'Big Key (Palace of Darkness)'] + [key]*6],
["Palace of Darkness - Boss", False, []],
["Palace of Darkness - Boss", False, [], ['Lamp']],

View File

@@ -14,6 +14,8 @@ class TestDungeon(LTTPTestBase):
self.starting_regions = [] # Where to start exploring
self.remove_exits = [] # Block dungeon exits
self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.multiworld.bombless_start[1].value = True
self.multiworld.shuffle_capacity_upgrades[1].value = True
create_regions(self.multiworld, 1)
self.multiworld.worlds[1].create_dungeons()
create_shops(self.multiworld, 1)

View File

@@ -18,8 +18,8 @@ class TestEasternPalace(TestDungeon):
["Eastern Palace - Big Key Chest", False, []],
["Eastern Palace - Big Key Chest", False, [], ['Lamp']],
["Eastern Palace - Big Key Chest", True, ['Lamp', 'Small Key (Eastern Palace)', 'Small Key (Eastern Palace)']],
["Eastern Palace - Big Key Chest", True, ['Lamp', 'Big Key (Eastern Palace)']],
["Eastern Palace - Big Key Chest", True, ['Lamp', 'Small Key (Eastern Palace)', 'Small Key (Eastern Palace)', 'Progressive Sword']],
["Eastern Palace - Big Key Chest", True, ['Lamp', 'Big Key (Eastern Palace)', 'Progressive Sword']],
#@todo: Advanced?
["Eastern Palace - Boss", False, []],

View File

@@ -103,16 +103,16 @@ class TestGanonsTower(TestDungeon):
["Ganons Tower - Compass Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Big Key Chest", False, []],
["Ganons Tower - Big Key Chest", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Chest", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Left", False, []],
["Ganons Tower - Big Key Room - Left", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Left", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Right", False, []],
["Ganons Tower - Big Key Room - Right", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Right", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Mini Helmasaur Room - Left", False, []],
["Ganons Tower - Mini Helmasaur Room - Left", False, [], ['Progressive Bow']],

View File

@@ -11,8 +11,9 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Big Key Chest", False, [], ['Progressive Glove']],
["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Big Key Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
#@todo: Change from item randomizer - Right side key door is only in logic if big key is in there
#["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
@@ -30,8 +31,9 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Map Chest", False, [], ['Progressive Glove']],
["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Map Chest", True, ['Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Map Chest", True, ['Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Map Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
#["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cape', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
@@ -40,8 +42,9 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Spike Room", False, []],
["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Spike Room", True, ['Fire Rod', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Spike Room", True, ['Bombos', 'Progressive Sword', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Spike Room", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Fire Rod', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword', 'Hookshot', 'Small Key (Ice Palace)']],
#["Ice Palace - Spike Room", True, ['Cape', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Spike Room", True, ['Cape', 'Bombos', 'Progressive Sword', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Spike Room", True, ['Cane of Byrna', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
@@ -50,21 +53,24 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Freezor Chest", False, []],
["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Freezor Chest", True, ['Fire Rod']],
["Ice Palace - Freezor Chest", True, ['Bombos', 'Progressive Sword']],
["Ice Palace - Freezor Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Fire Rod']],
["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Iced T Room", False, []],
["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Iced T Room", True, ['Fire Rod']],
["Ice Palace - Iced T Room", True, ['Bombos', 'Progressive Sword']],
["Ice Palace - Iced T Room", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Fire Rod']],
["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Big Chest", False, []],
["Ice Palace - Big Chest", False, [], ['Big Key (Ice Palace)']],
["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Big Chest", True, ['Big Key (Ice Palace)', 'Fire Rod']],
["Ice Palace - Big Chest", True, ['Big Key (Ice Palace)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Fire Rod']],
["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Boss", False, []],
["Ice Palace - Boss", False, [], ['Hammer']],
@@ -72,9 +78,10 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Boss", False, [], ['Big Key (Ice Palace)']],
["Ice Palace - Boss", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Boss", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
# need hookshot now to reach the right side for the 6th key
["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']],
])

View File

@@ -78,7 +78,8 @@ class TestMiseryMire(TestDungeon):
["Misery Mire - Boss", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow']],
["Misery Mire - Boss", False, [], ['Big Key (Misery Mire)']],
["Misery Mire - Boss", False, [], ['Pegasus Boots', 'Hookshot']],
["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Hammer', 'Pegasus Boots']],
["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Pegasus Boots']],
["Misery Mire - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Hammer', 'Pegasus Boots']],
["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Pegasus Boots']],
])

View File

@@ -8,7 +8,8 @@ class TestSkullWoods(TestDungeon):
self.run_tests([
["Skull Woods - Big Chest", False, []],
["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']],
["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']],
["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Skull Woods)']],
["Skull Woods - Compass Chest", True, []],
@@ -64,7 +65,8 @@ class TestSkullWoods(TestDungeon):
self.run_tests([
["Skull Woods - Big Chest", False, []],
["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']],
["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']],
["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Skull Woods)']],
["Skull Woods - Compass Chest", False, []],
["Skull Woods - Compass Chest", False, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)'], ['Small Key (Skull Woods)']],

View File

@@ -30,7 +30,8 @@ class TestSwampPalace(TestDungeon):
["Swamp Palace - Map Chest", False, [], ['Flippers']],
["Swamp Palace - Map Chest", False, [], ['Open Floodgate']],
["Swamp Palace - Map Chest", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Map Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers']],
["Swamp Palace - Map Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Swamp Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers']],
["Swamp Palace - West Chest", False, []],
["Swamp Palace - West Chest", False, [], ['Flippers']],

View File

@@ -41,8 +41,9 @@ class TestThievesTown(TestDungeon):
["Thieves' Town - Boss", False, []],
["Thieves' Town - Boss", False, [], ['Big Key (Thieves Town)']],
["Thieves' Town - Boss", False, [], ['Hammer', 'Progressive Sword', 'Cane of Somaria', 'Cane of Byrna']],
["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Hammer']],
["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Progressive Sword']],
["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Somaria']],
["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Byrna']],
["Thieves' Town - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Hammer']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Progressive Sword']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Somaria']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Byrna']],
])

View File

@@ -18,11 +18,11 @@ class TestTowerOfHera(TestDungeon):
["Tower of Hera - Compass Chest", False, []],
["Tower of Hera - Compass Chest", False, [], ['Big Key (Tower of Hera)']],
["Tower of Hera - Compass Chest", True, ['Big Key (Tower of Hera)']],
["Tower of Hera - Compass Chest", True, ['Big Key (Tower of Hera)', 'Progressive Sword']],
["Tower of Hera - Big Chest", False, []],
["Tower of Hera - Big Chest", False, [], ['Big Key (Tower of Hera)']],
["Tower of Hera - Big Chest", True, ['Big Key (Tower of Hera)']],
["Tower of Hera - Big Chest", True, ['Big Key (Tower of Hera)', 'Progressive Sword']],
["Tower of Hera - Boss", False, []],
["Tower of Hera - Boss", False, [], ['Big Key (Tower of Hera)']],

View File

@@ -13,7 +13,9 @@ class TestInverted(TestBase, LTTPTestBase):
def setUp(self):
self.world_setup()
self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.multiworld.mode[1] = "inverted"
self.multiworld.mode[1].value = 2
self.multiworld.bombless_start[1].value = True
self.multiworld.shuffle_capacity_upgrades[1].value = True
create_inverted_regions(self.multiworld, 1)
self.multiworld.worlds[1].create_dungeons()
create_shops(self.multiworld, 1)

View File

@@ -11,8 +11,8 @@ class TestInvertedBombRules(LTTPTestBase):
def setUp(self):
self.world_setup()
self.multiworld.mode[1] = "inverted"
self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.multiworld.mode[1].value = 2
create_inverted_regions(self.multiworld, 1)
self.multiworld.worlds[1].create_dungeons()

View File

@@ -5,7 +5,8 @@ class TestInvertedDarkWorld(TestInverted):
def testNorthWest(self):
self.run_location_tests([
["Brewery", True, []],
["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Brewery", True, ['Bomb Upgrade (+5)']],
["C-Shaped House", True, []],
@@ -77,15 +78,16 @@ class TestInvertedDarkWorld(TestInverted):
def testSouth(self):
self.run_location_tests([
["Hype Cave - Top", True, []],
["Hype Cave - Middle Right", True, []],
["Hype Cave - Middle Left", True, []],
["Hype Cave - Bottom", True, []],
["Hype Cave - Generous Guy", True, []],
["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)']],
["Stumpy", True, []],

View File

@@ -40,10 +40,12 @@ class TestInvertedDeathMountain(TestInverted):
["Paradox Cave Lower - Far Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']],
["Paradox Cave Lower - Left", False, []],
["Paradox Cave Lower - Left", False, [], ['Moon Pearl']],
@@ -52,10 +54,12 @@ class TestInvertedDeathMountain(TestInverted):
["Paradox Cave Lower - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']],
["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']],
["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']],
["Paradox Cave Lower - Middle", False, []],
["Paradox Cave Lower - Middle", False, [], ['Moon Pearl']],
@@ -64,10 +68,12 @@ class TestInvertedDeathMountain(TestInverted):
["Paradox Cave Lower - Middle", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']],
["Paradox Cave Lower - Right", False, []],
["Paradox Cave Lower - Right", False, [], ['Moon Pearl']],
@@ -76,10 +82,12 @@ class TestInvertedDeathMountain(TestInverted):
["Paradox Cave Lower - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']],
["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']],
["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']],
["Paradox Cave Lower - Far Right", False, []],
["Paradox Cave Lower - Far Right", False, [], ['Moon Pearl']],
@@ -88,10 +96,12 @@ class TestInvertedDeathMountain(TestInverted):
["Paradox Cave Lower - Far Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Cane of Somaria', 'Fire Rod']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Moon Pearl', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl', 'Cane of Somaria']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Fire Rod']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Progressive Bow']],
["Paradox Cave Upper - Left", False, []],
["Paradox Cave Upper - Left", False, [], ['Moon Pearl']],
@@ -100,10 +110,11 @@ class TestInvertedDeathMountain(TestInverted):
["Paradox Cave Upper - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Upper - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Upper - Right", False, []],
["Paradox Cave Upper - Right", False, [], ['Moon Pearl']],
@@ -112,20 +123,22 @@ class TestInvertedDeathMountain(TestInverted):
["Paradox Cave Upper - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Upper - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Mimic Cave", False, []],
["Mimic Cave", False, [], ['Moon Pearl']],
["Mimic Cave", False, [], ['Hammer']],
["Mimic Cave", False, [], ['Progressive Glove', 'Flute']],
["Mimic Cave", False, [], ['Lamp', 'Flute']],
["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']],
["Mimic Cave", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']],
["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Bow', 'Cane of Somaria', 'Progressive Sword']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Flute', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Progressive Bow', 'Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']],
["Mimic Cave", True, ['Cane of Somaria', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']],
["Ether Tablet", False, []],
["Ether Tablet", False, [], ['Moon Pearl']],

View File

@@ -44,15 +44,17 @@ class TestInvertedLightWorld(TestInverted):
["Chicken House", False, []],
["Chicken House", False, [], ['Moon Pearl']],
["Chicken House", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Kakariko Well - Top", False, []],
["Kakariko Well - Top", False, [], ['Moon Pearl']],
["Kakariko Well - Top", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Kakariko Well - Left", False, []],
["Kakariko Well - Left", False, [], ['Moon Pearl']],
@@ -80,9 +82,10 @@ class TestInvertedLightWorld(TestInverted):
["Blind's Hideout - Top", False, []],
["Blind's Hideout - Top", False, [], ['Moon Pearl']],
["Blind's Hideout - Top", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Blind's Hideout - Left", False, []],
["Blind's Hideout - Left", False, [], ['Moon Pearl']],
@@ -161,9 +164,10 @@ class TestInvertedLightWorld(TestInverted):
["Maze Race", False, []],
["Maze Race", False, [], ['Moon Pearl']],
["Maze Race", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Maze Race", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']],
["Maze Race", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Maze Race", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
])
def testSouthLightWorld(self):
@@ -184,9 +188,10 @@ class TestInvertedLightWorld(TestInverted):
["Aginah's Cave", False, []],
["Aginah's Cave", False, [], ['Moon Pearl']],
["Aginah's Cave", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Bombos Tablet", False, []],
["Bombos Tablet", False, ['Progressive Sword'], ['Progressive Sword']],
@@ -212,39 +217,45 @@ class TestInvertedLightWorld(TestInverted):
["Mini Moldorm Cave - Far Left", False, []],
["Mini Moldorm Cave - Far Left", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']],
["Mini Moldorm Cave - Left", False, []],
["Mini Moldorm Cave - Left", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']],
["Mini Moldorm Cave - Generous Guy", False, []],
["Mini Moldorm Cave - Generous Guy", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']],
["Mini Moldorm Cave - Right", False, []],
["Mini Moldorm Cave - Right", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']],
["Mini Moldorm Cave - Far Right", False, []],
["Mini Moldorm Cave - Far Right", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Sword']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword']],
["Ice Rod Cave", False, []],
["Ice Rod Cave", False, [], ['Moon Pearl']],
["Ice Rod Cave", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
])
def testZoraArea(self):
@@ -302,21 +313,24 @@ class TestInvertedLightWorld(TestInverted):
["Sahasrahla's Hut - Left", False, []],
["Sahasrahla's Hut - Left", False, [], ['Moon Pearl']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Middle", False, []],
["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Right", False, []],
["Sahasrahla's Hut - Right", False, [], ['Moon Pearl']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla", False, []],
["Sahasrahla", False, [], ['Green Pendant']],
@@ -346,9 +360,10 @@ class TestInvertedLightWorld(TestInverted):
["Graveyard Cave", False, []],
["Graveyard Cave", False, [], ['Moon Pearl']],
["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Potion Shop", False, []],
["Potion Shop", False, [], ['Mushroom']],

View File

@@ -21,10 +21,10 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Chain Chomps", False, ['Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']],
["Turtle Rock - Chain Chomps", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']],
["Turtle Rock - Chain Chomps", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Fire Rod']],
["Turtle Rock - Roller Room - Left", False, []],
["Turtle Rock - Roller Room - Left", False, [], ['Cane of Somaria']],
@@ -54,8 +54,8 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Big Chest", False, [], ['Big Key (Turtle Rock)']],
["Turtle Rock - Big Chest", False, [], ['Magic Mirror', 'Cane of Somaria']],
["Turtle Rock - Big Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hookshot']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
@@ -68,10 +68,10 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Big Key Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
# Mirror in from ledge, use left side entrance, have enough keys to get to the chest
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", False, []],
["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']],
@@ -102,8 +102,11 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Flute', 'Magic Mirror', 'Moon Pearl', 'Hookshot', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']]
])
def testEyeBridge(self):
for location in ["Turtle Rock - Eye Bridge - Top Right", "Turtle Rock - Eye Bridge - Top Left",
"Turtle Rock - Eye Bridge - Bottom Right", "Turtle Rock - Eye Bridge - Bottom Left"]:

View File

@@ -5,7 +5,8 @@ class TestInvertedDarkWorld(TestInvertedMinor):
def testNorthWest(self):
self.run_location_tests([
["Brewery", True, []],
["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Brewery", True, ['Bomb Upgrade (+5)']],
["C-Shaped House", True, []],
@@ -67,15 +68,16 @@ class TestInvertedDarkWorld(TestInvertedMinor):
def testSouth(self):
self.run_location_tests([
["Hype Cave - Top", True, []],
["Hype Cave - Middle Right", True, []],
["Hype Cave - Middle Left", True, []],
["Hype Cave - Bottom", True, []],
["Hype Cave - Generous Guy", True, []],
["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)']],
["Stumpy", True, []],

View File

@@ -40,10 +40,11 @@ class TestInvertedDeathMountain(TestInvertedMinor):
["Paradox Cave Lower - Far Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']],
["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Left", False, []],
["Paradox Cave Lower - Left", False, [], ['Moon Pearl']],
@@ -52,10 +53,11 @@ class TestInvertedDeathMountain(TestInvertedMinor):
["Paradox Cave Lower - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']],
["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Middle", False, []],
["Paradox Cave Lower - Middle", False, [], ['Moon Pearl']],
@@ -64,10 +66,11 @@ class TestInvertedDeathMountain(TestInvertedMinor):
["Paradox Cave Lower - Middle", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']],
["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Right", False, []],
["Paradox Cave Lower - Right", False, [], ['Moon Pearl']],
@@ -76,10 +79,11 @@ class TestInvertedDeathMountain(TestInvertedMinor):
["Paradox Cave Lower - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']],
["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", False, []],
["Paradox Cave Lower - Far Right", False, [], ['Moon Pearl']],
@@ -88,10 +92,11 @@ class TestInvertedDeathMountain(TestInvertedMinor):
["Paradox Cave Lower - Far Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod']],
["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Progressive Bow', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Cane of Somaria', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Upper - Left", False, []],
["Paradox Cave Upper - Left", False, [], ['Moon Pearl']],
@@ -100,10 +105,11 @@ class TestInvertedDeathMountain(TestInvertedMinor):
["Paradox Cave Upper - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Upper - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Upper - Right", False, []],
["Paradox Cave Upper - Right", False, [], ['Moon Pearl']],
@@ -112,20 +118,21 @@ class TestInvertedDeathMountain(TestInvertedMinor):
["Paradox Cave Upper - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", False, ['Flute', 'Progressive Glove', 'Hammer', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Paradox Cave Upper - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Mimic Cave", False, []],
["Mimic Cave", False, [], ['Moon Pearl']],
["Mimic Cave", False, [], ['Hammer']],
["Mimic Cave", False, [], ['Progressive Glove', 'Flute']],
["Mimic Cave", False, [], ['Lamp', 'Flute']],
["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']],
["Mimic Cave", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Flute', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Progressive Sword', 'Progressive Sword', 'Flute', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']],
["Mimic Cave", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']],
["Mimic Cave", True, ['Cane of Somaria', 'Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']],
["Ether Tablet", False, []],
["Ether Tablet", False, [], ['Moon Pearl']],

View File

@@ -43,16 +43,18 @@ class TestInvertedLightWorld(TestInvertedMinor):
["Chicken House", False, []],
["Chicken House", False, [], ['Moon Pearl']],
["Chicken House", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Chicken House", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']],
["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
# top can't be bombed as super bunny and needs Moon Pearl
["Kakariko Well - Top", False, []],
["Kakariko Well - Top", False, [], ['Moon Pearl']],
["Kakariko Well - Top", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Kakariko Well - Top", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Kakariko Well - Left", False, []],
["Kakariko Well - Left", True, ['Beat Agahnim 1']],
@@ -76,9 +78,10 @@ class TestInvertedLightWorld(TestInvertedMinor):
["Blind's Hideout - Top", False, []],
["Blind's Hideout - Top", False, [], ['Moon Pearl', 'Magic Mirror']],
["Blind's Hideout - Top", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Blind's Hideout - Top", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Blind's Hideout - Left", False, []],
["Blind's Hideout - Left", False, [], ['Moon Pearl', 'Magic Mirror']],
@@ -157,9 +160,10 @@ class TestInvertedLightWorld(TestInvertedMinor):
["Maze Race", False, []],
["Maze Race", False, [], ['Moon Pearl']],
["Maze Race", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Maze Race", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)','Bomb Upgrade (50)', 'Pegasus Boots']],
["Maze Race", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Maze Race", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Maze Race", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
])
def testSouthLightWorld(self):
@@ -179,9 +183,10 @@ class TestInvertedLightWorld(TestInvertedMinor):
["Aginah's Cave", False, []],
["Aginah's Cave", False, [], ['Moon Pearl']],
["Aginah's Cave", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Bombos Tablet", False, []],
["Bombos Tablet", False, ['Progressive Sword'], ['Progressive Sword']],
@@ -209,39 +214,45 @@ class TestInvertedLightWorld(TestInvertedMinor):
["Mini Moldorm Cave - Far Left", False, []],
["Mini Moldorm Cave - Far Left", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Left", False, []],
["Mini Moldorm Cave - Left", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Generous Guy", False, []],
["Mini Moldorm Cave - Generous Guy", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Right", False, []],
["Mini Moldorm Cave - Right", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Far Right", False, []],
["Mini Moldorm Cave - Far Right", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Beat Agahnim 1']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Ice Rod Cave", False, []],
["Ice Rod Cave", False, [], ['Moon Pearl']],
["Ice Rod Cave", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
])
def testZoraArea(self):
@@ -297,25 +308,28 @@ class TestInvertedLightWorld(TestInvertedMinor):
["Sahasrahla's Hut - Left", False, []],
["Sahasrahla's Hut - Left", False, [], ['Moon Pearl', 'Magic Mirror']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Left", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Left", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
# super bunny bonk
["Sahasrahla's Hut - Left", True, ['Magic Mirror', 'Beat Agahnim 1', 'Pegasus Boots']],
["Sahasrahla's Hut - Middle", False, []],
["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl', 'Magic Mirror']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Middle", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Middle", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
# super bunny bonk
["Sahasrahla's Hut - Middle", True, ['Magic Mirror', 'Beat Agahnim 1', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", False, []],
["Sahasrahla's Hut - Right", False, [], ['Moon Pearl', 'Magic Mirror']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Sahasrahla's Hut - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Right", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Sahasrahla's Hut - Right", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
# super bunny bonk
["Sahasrahla's Hut - Right", True, ['Magic Mirror', 'Beat Agahnim 1', 'Pegasus Boots']],
@@ -347,9 +361,10 @@ class TestInvertedLightWorld(TestInvertedMinor):
["Graveyard Cave", False, []],
["Graveyard Cave", False, [], ['Moon Pearl']],
["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Potion Shop", False, []],
["Potion Shop", False, [], ['Mushroom']],

View File

@@ -12,8 +12,10 @@ from worlds.alttp.test import LTTPTestBase
class TestInvertedMinor(TestBase, LTTPTestBase):
def setUp(self):
self.world_setup()
self.multiworld.mode[1] = "inverted"
self.multiworld.logic[1] = "minorglitches"
self.multiworld.mode[1].value = 2
self.multiworld.glitches_required[1] = "minor_glitches"
self.multiworld.bombless_start[1].value = True
self.multiworld.shuffle_capacity_upgrades[1].value = True
self.multiworld.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.multiworld, 1)
self.multiworld.worlds[1].create_dungeons()

View File

@@ -22,10 +22,10 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Chain Chomps", False, ['Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']],
["Turtle Rock - Chain Chomps", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']],
["Turtle Rock - Chain Chomps", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']],
["Turtle Rock - Roller Room - Left", False, []],
["Turtle Rock - Roller Room - Left", False, [], ['Cane of Somaria']],
@@ -55,8 +55,8 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Big Chest", False, [], ['Big Key (Turtle Rock)']],
["Turtle Rock - Big Chest", False, [], ['Magic Mirror', 'Cane of Somaria']],
["Turtle Rock - Big Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hookshot']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
@@ -69,10 +69,10 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Big Key Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
# Mirror in from ledge, use left side entrance, have enough keys to get to the chest
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", False, []],
["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']],
@@ -98,7 +98,7 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Boss", False, [], ['Big Key (Turtle Rock)']],
["Turtle Rock - Boss", False, [], ['Magic Mirror', 'Lamp']],
["Turtle Rock - Boss", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Flute', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Flute', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],

View File

@@ -5,15 +5,16 @@ class TestDarkWorld(TestInvertedOWG):
def testSouthDarkWorld(self):
self.run_location_tests([
["Hype Cave - Top", True, []],
["Hype Cave - Middle Right", True, []],
["Hype Cave - Middle Left", True, []],
["Hype Cave - Bottom", True, []],
["Hype Cave - Generous Guy", True, []],
["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)']],
["Stumpy", True, []],
@@ -22,7 +23,8 @@ class TestDarkWorld(TestInvertedOWG):
def testWestDarkWorld(self):
self.run_location_tests([
["Brewery", True, []],
["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Brewery", True, ['Bomb Upgrade (+5)']],
["C-Shaped House", True, []],

View File

@@ -24,36 +24,38 @@ class TestDeathMountain(TestInvertedOWG):
["Paradox Cave Lower - Far Left", False, []],
["Paradox Cave Lower - Far Left", False, [], ['Moon Pearl']],
["Paradox Cave Lower - Far Left", True, ['Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Left", False, []],
["Paradox Cave Lower - Left", False, [], ['Moon Pearl']],
["Paradox Cave Lower - Left", True, ['Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Middle", False, []],
["Paradox Cave Lower - Middle", False, [], ['Moon Pearl']],
["Paradox Cave Lower - Middle", True, ['Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Right", False, []],
["Paradox Cave Lower - Right", False, [], ['Moon Pearl']],
["Paradox Cave Lower - Right", True, ['Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Far Right", False, []],
["Paradox Cave Lower - Far Right", False, [], ['Moon Pearl']],
["Paradox Cave Lower - Far Right", True, ['Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Upper - Left", False, []],
["Paradox Cave Upper - Left", False, [], ['Moon Pearl']],
["Paradox Cave Upper - Left", True, ['Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Upper - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Upper - Right", False, []],
["Paradox Cave Upper - Right", False, [], ['Moon Pearl']],
["Paradox Cave Upper - Right", True, ['Moon Pearl', 'Pegasus Boots']],
["Paradox Cave Upper - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Mimic Cave", False, []],
["Mimic Cave", False, [], ['Moon Pearl']],
["Mimic Cave", False, [], ['Hammer']],
["Mimic Cave", True, ['Moon Pearl', 'Hammer', 'Pegasus Boots']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Hammer', 'Pegasus Boots']],
["Ether Tablet", False, []],
["Ether Tablet", False, ['Progressive Sword'], ['Progressive Sword']],

View File

@@ -13,16 +13,16 @@ class TestDungeons(TestInvertedOWG):
["Sanctuary", False, []],
["Sanctuary", False, ['Beat Agahnim 1']],
["Sanctuary", True, ['Magic Mirror', 'Beat Agahnim 1']],
["Sanctuary", True, ['Lamp', 'Beat Agahnim 1', 'Small Key (Hyrule Castle)']],
["Sanctuary", True, ['Progressive Sword', 'Lamp', 'Beat Agahnim 1', 'Small Key (Hyrule Castle)']],
["Sanctuary", True, ['Moon Pearl', 'Pegasus Boots']],
["Sanctuary", True, ['Magic Mirror', 'Pegasus Boots']],
["Sewers - Secret Room - Left", False, []],
["Sewers - Secret Room - Left", True, ['Moon Pearl', 'Progressive Glove', 'Pegasus Boots']],
["Sewers - Secret Room - Left", True, ['Moon Pearl', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True,
['Magic Mirror', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Moon Pearl', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Magic Mirror', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+10)', 'Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']],
["Eastern Palace - Compass Chest", False, []],
["Eastern Palace - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots']],
@@ -45,7 +45,7 @@ class TestDungeons(TestInvertedOWG):
["Tower of Hera - Basement Cage", True, ['Pegasus Boots', 'Moon Pearl']],
["Castle Tower - Room 03", False, []],
["Castle Tower - Room 03", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Room 03", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Room 03", True, ['Pegasus Boots', 'Progressive Sword']],
["Castle Tower - Room 03", True, ['Pegasus Boots', 'Progressive Bow']],
@@ -62,7 +62,8 @@ class TestDungeons(TestInvertedOWG):
["Skull Woods - Big Chest", False, []],
["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']],
["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']],
["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Skull Woods)']],
["Skull Woods - Big Key Chest", True, []],
@@ -89,7 +90,16 @@ class TestDungeons(TestInvertedOWG):
["Turtle Rock - Compass Chest", True, ['Pegasus Boots', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Chain Chomps", False, []],
["Turtle Rock - Chain Chomps", True, ['Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Hookshot', 'Progressive Sword', 'Progressive Bow', 'Blue Boomerang', 'Red Boomerang', 'Cane of Somaria', 'Fire Rod', 'Ice Rod']],
["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", True, ['Hookshot', 'Pegasus Boots']],
["Turtle Rock - Chain Chomps", True, ['Progressive Bow', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", True, ['Blue Boomerang', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", True, ['Red Boomerang', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", True, ['Cane of Somaria', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", True, ['Fire Rod', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", True, ['Ice Rod', 'Pegasus Boots', 'Magic Mirror', 'Moon Pearl']],
["Turtle Rock - Chain Chomps", True, ['Progressive Sword', 'Progressive Sword', 'Pegasus Boots']],
["Turtle Rock - Crystaroller Room", False, []],
["Turtle Rock - Crystaroller Room", True, ['Pegasus Boots', 'Magic Mirror', 'Moon Pearl', 'Big Key (Turtle Rock)']],

View File

@@ -12,8 +12,10 @@ from worlds.alttp.test import LTTPTestBase
class TestInvertedOWG(TestBase, LTTPTestBase):
def setUp(self):
self.world_setup()
self.multiworld.logic[1] = "owglitches"
self.multiworld.mode[1] = "inverted"
self.multiworld.glitches_required[1] = "overworld_glitches"
self.multiworld.mode[1].value = 2
self.multiworld.bombless_start[1].value = True
self.multiworld.shuffle_capacity_upgrades[1].value = True
self.multiworld.difficulty_requirements[1] = difficulties['normal']
create_inverted_regions(self.multiworld, 1)
self.multiworld.worlds[1].create_dungeons()

View File

@@ -40,40 +40,46 @@ class TestLightWorld(TestInvertedOWG):
["Chicken House", False, []],
["Chicken House", False, [], ['Moon Pearl']],
["Chicken House", True, ['Moon Pearl', 'Pegasus Boots']],
["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Chicken House", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Aginah's Cave", False, []],
["Aginah's Cave", False, [], ['Moon Pearl']],
["Aginah's Cave", True, ['Moon Pearl', 'Pegasus Boots']],
["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", False, []],
["Sahasrahla's Hut - Left", False, [], ['Moon Pearl', 'Magic Mirror']],
["Sahasrahla's Hut - Left", False, [], ['Moon Pearl', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Magic Mirror', 'Pegasus Boots']],
##todo: Damage boost superbunny not in logic
#["Sahasrahla's Hut - Left", True, ['Beat Agahnim 1', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Middle", False, []],
["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl', 'Magic Mirror']],
["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl', 'Pegasus Boots']],
["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, ['Magic Mirror', 'Pegasus Boots']],
#["Sahasrahla's Hut - Middle", True, ['Beat Agahnim 1', 'Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Middle", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Right", False, []],
["Sahasrahla's Hut - Right", False, [], ['Moon Pearl', 'Magic Mirror']],
["Sahasrahla's Hut - Right", False, [], ['Moon Pearl', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Magic Mirror', 'Pegasus Boots']],
#["Sahasrahla's Hut - Right", True, ['Beat Agahnim 1', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Beat Agahnim 1']],
["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1']],
["Kakariko Well - Top", False, []],
["Kakariko Well - Top", False, [], ['Moon Pearl']],
["Kakariko Well - Top", True, ['Moon Pearl', 'Pegasus Boots']],
["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Kakariko Well - Left", False, []],
["Kakariko Well - Left", True, ['Moon Pearl', 'Pegasus Boots']],
@@ -101,7 +107,8 @@ class TestLightWorld(TestInvertedOWG):
["Blind's Hideout - Top", False, []],
["Blind's Hideout - Top", False, [], ['Moon Pearl']],
["Blind's Hideout - Top", True, ['Moon Pearl', 'Pegasus Boots']],
["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Blind's Hideout - Left", False, []],
["Blind's Hideout - Left", False, [], ['Moon Pearl', 'Magic Mirror']],
@@ -134,27 +141,33 @@ class TestLightWorld(TestInvertedOWG):
["Mini Moldorm Cave - Far Left", False, []],
["Mini Moldorm Cave - Far Left", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Left", False, []],
["Mini Moldorm Cave - Left", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Right", False, []],
["Mini Moldorm Cave - Right", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Far Right", False, []],
["Mini Moldorm Cave - Far Right", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Generous Guy", False, []],
["Mini Moldorm Cave - Generous Guy", False, [], ['Moon Pearl']],
["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Pegasus Boots']],
["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Moon Pearl', 'Pegasus Boots']],
["Ice Rod Cave", False, []],
["Ice Rod Cave", False, [], ['Moon Pearl']],
["Ice Rod Cave", True, ['Moon Pearl', 'Pegasus Boots']],
["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
#I don't think so
#["Ice Rod Cave", True, ['Magic Mirror', 'Pegasus Boots', 'BigRedBomb']],
#["Ice Rod Cave", True, ['Magic Mirror', 'Beat Agahnim 1', 'BigRedBomb']],
@@ -236,7 +249,8 @@ class TestLightWorld(TestInvertedOWG):
["Graveyard Cave", False, []],
["Graveyard Cave", False, [], ['Moon Pearl']],
["Graveyard Cave", True, ['Moon Pearl', 'Pegasus Boots']],
["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Checkerboard Cave", False, []],
["Checkerboard Cave", False, [], ['Progressive Glove']],

View File

@@ -7,43 +7,48 @@ class TestDarkWorld(TestMinor):
self.run_location_tests([
["Hype Cave - Top", False, []],
["Hype Cave - Top", False, [], ['Moon Pearl']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Right", False, []],
["Hype Cave - Middle Right", False, [], ['Moon Pearl']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Left", False, []],
["Hype Cave - Middle Left", False, [], ['Moon Pearl']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Bottom", False, []],
["Hype Cave - Bottom", False, [], ['Moon Pearl']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Generous Guy", False, []],
["Hype Cave - Generous Guy", False, [], ['Moon Pearl']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Stumpy", False, []],
["Stumpy", False, [], ['Moon Pearl']],
@@ -66,10 +71,11 @@ class TestDarkWorld(TestMinor):
self.run_location_tests([
["Brewery", False, []],
["Brewery", False, [], ['Moon Pearl']],
["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["C-Shaped House", False, []],
["C-Shaped House", False, [], ['Moon Pearl']],

View File

@@ -48,7 +48,8 @@ class TestDeathMountain(TestMinor):
["Mimic Cave", False, [], ['Moon Pearl']],
["Mimic Cave", False, [], ['Cane of Somaria']],
["Mimic Cave", False, ['Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Mimic Cave", True, ['Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Spiral Cave", False, []],
["Spiral Cave", False, [], ['Progressive Glove', 'Flute']],
@@ -73,10 +74,11 @@ class TestDeathMountain(TestMinor):
["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Left", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Far Left", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Far Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Left", False, []],
["Paradox Cave Lower - Left", False, [], ['Progressive Glove', 'Flute']],
@@ -87,10 +89,11 @@ class TestDeathMountain(TestMinor):
["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Left", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Left", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Middle", False, []],
["Paradox Cave Lower - Middle", False, [], ['Progressive Glove', 'Flute']],
@@ -101,10 +104,11 @@ class TestDeathMountain(TestMinor):
["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Middle", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Middle", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Middle", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Right", False, []],
["Paradox Cave Lower - Right", False, [], ['Progressive Glove', 'Flute']],
@@ -115,10 +119,11 @@ class TestDeathMountain(TestMinor):
["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Right", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Right", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Right", False, []],
["Paradox Cave Lower - Far Right", False, [], ['Progressive Glove', 'Flute']],
@@ -129,10 +134,11 @@ class TestDeathMountain(TestMinor):
["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Right", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Far Right", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Far Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Left", False, []],
["Paradox Cave Upper - Left", False, [], ['Progressive Glove', 'Flute']],
@@ -143,10 +149,11 @@ class TestDeathMountain(TestMinor):
["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Left", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Upper - Left", False, ['Flute', 'Hammer']],
["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Left", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", False, []],
["Paradox Cave Upper - Right", False, [], ['Progressive Glove', 'Flute']],
@@ -157,10 +164,11 @@ class TestDeathMountain(TestMinor):
["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Right", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Upper - Right", False, ['Flute', 'Hammer']],
["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
])
def testWestDarkWorldDeathMountain(self):

View File

@@ -29,17 +29,21 @@ class TestLightWorld(TestMinor):
["Kakariko Tavern", True, []],
["Chicken House", True, []],
["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Chicken House", True, ['Bomb Upgrade (+5)']],
["Aginah's Cave", True, []],
["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Left", True, []],
["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Middle", True, ['Pegasus Boots']],
["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Right", True, ['Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, []],
["Sahasrahla's Hut - Right", True, []],
["Kakariko Well - Top", True, []],
["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)']],
["Kakariko Well - Left", True, []],
@@ -49,7 +53,8 @@ class TestLightWorld(TestMinor):
["Kakariko Well - Bottom", True, []],
["Blind's Hideout - Top", True, []],
["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)']],
["Blind's Hideout - Left", True, []],
@@ -63,15 +68,19 @@ class TestLightWorld(TestMinor):
["Bonk Rock Cave", False, [], ['Pegasus Boots']],
["Bonk Rock Cave", True, ['Pegasus Boots']],
["Mini Moldorm Cave - Far Left", True, []],
["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Left", True, []],
["Mini Moldorm Cave - Right", True, []],
["Mini Moldorm Cave - Far Right", True, []],
["Ice Rod Cave", True, []],
["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)']],
["Bottle Merchant", True, []],
@@ -131,11 +140,12 @@ class TestLightWorld(TestMinor):
["Graveyard Cave", False, []],
["Graveyard Cave", False, [], ['Magic Mirror']],
["Graveyard Cave", False, [], ['Moon Pearl']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Checkerboard Cave", False, []],
["Checkerboard Cave", False, [], ['Progressive Glove']],
@@ -143,8 +153,6 @@ class TestLightWorld(TestMinor):
["Checkerboard Cave", False, [], ['Magic Mirror']],
["Checkerboard Cave", True, ['Flute', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Generous Guy", True, []],
["Library", False, []],
["Library", False, [], ['Pegasus Boots']],
["Library", True, ['Pegasus Boots']],
@@ -155,7 +163,10 @@ class TestLightWorld(TestMinor):
["Potion Shop", False, [], ['Mushroom']],
["Potion Shop", True, ['Mushroom']],
["Maze Race", True, []],
["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Magic Mirror', 'Pegasus Boots']],
["Maze Race", True, ['Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Maze Race", True, ['Bomb Upgrade (+5)']],
["Maze Race", True, ['Pegasus Boots']],
["Desert Ledge", False, []],
["Desert Ledge", False, [], ['Book of Mudora', 'Flute']],

View File

@@ -10,7 +10,9 @@ from worlds.alttp.test import LTTPTestBase
class TestMinor(TestBase, LTTPTestBase):
def setUp(self):
self.world_setup()
self.multiworld.logic[1] = "minorglitches"
self.multiworld.glitches_required[1] = "minor_glitches"
self.multiworld.bombless_start[1].value = True
self.multiworld.shuffle_capacity_upgrades[1].value = True
self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.multiworld.worlds[1].er_seed = 0
self.multiworld.worlds[1].create_regions()

View File

@@ -7,48 +7,53 @@ class TestDarkWorld(TestVanillaOWG):
self.run_location_tests([
["Hype Cave - Top", False, []],
["Hype Cave - Top", False, [], ['Moon Pearl']],
["Hype Cave - Top", True, ['Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Right", False, []],
["Hype Cave - Middle Right", False, [], ['Moon Pearl']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Left", False, []],
["Hype Cave - Middle Left", False, [], ['Moon Pearl']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Bottom", False, []],
["Hype Cave - Bottom", False, [], ['Moon Pearl']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Generous Guy", False, []],
["Hype Cave - Generous Guy", False, [], ['Moon Pearl']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Stumpy", False, []],
["Stumpy", False, [], ['Moon Pearl']],
@@ -129,13 +134,14 @@ class TestDarkWorld(TestVanillaOWG):
self.run_location_tests([
["Brewery", False, []],
["Brewery", False, [], ['Moon Pearl']],
["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Brewery", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot', 'Progressive Glove']],
["Brewery", True, ['Moon Pearl', 'Pegasus Boots']],
["Brewery", True, ['Moon Pearl', 'Flute', 'Magic Mirror']],
["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Flute', 'Magic Mirror']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["C-Shaped House", False, []],
["C-Shaped House", False, [], ['Moon Pearl', 'Magic Mirror']],

View File

@@ -48,9 +48,10 @@ class TestDeathMountain(TestVanillaOWG):
["Mimic Cave", False, [], ['Hammer']],
["Mimic Cave", False, [], ['Pegasus Boots', 'Flute', 'Lamp']],
["Mimic Cave", False, [], ['Pegasus Boots', 'Flute', 'Progressive Glove']],
["Mimic Cave", True, ['Magic Mirror', 'Hammer', 'Pegasus Boots']],
["Mimic Cave", True, ['Magic Mirror', 'Hammer', 'Progressive Glove', 'Lamp']],
["Mimic Cave", True, ['Magic Mirror', 'Hammer', 'Flute']],
["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Hookshot', 'Hammer']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Magic Mirror', 'Hammer', 'Pegasus Boots']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Magic Mirror', 'Hammer', 'Progressive Glove', 'Lamp']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Magic Mirror', 'Hammer', 'Flute']],
["Spiral Cave", False, []],
["Spiral Cave", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
@@ -64,65 +65,72 @@ class TestDeathMountain(TestVanillaOWG):
["Paradox Cave Lower - Far Left", False, []],
["Paradox Cave Lower - Far Left", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
["Paradox Cave Lower - Far Left", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Pegasus Boots']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Far Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Far Left", True, ['Fire Rod', 'Pegasus Boots']],
["Paradox Cave Lower - Far Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Far Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']],
["Paradox Cave Lower - Left", False, []],
["Paradox Cave Lower - Left", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
["Paradox Cave Lower - Left", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Pegasus Boots']],
["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Left", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Left", True, ['Fire Rod', 'Pegasus Boots']],
["Paradox Cave Lower - Left", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']],
["Paradox Cave Lower - Middle", False, []],
["Paradox Cave Lower - Middle", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
["Paradox Cave Lower - Middle", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Pegasus Boots']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Middle", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Middle", True, ['Fire Rod', 'Pegasus Boots']],
["Paradox Cave Lower - Middle", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Middle", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']],
["Paradox Cave Lower - Right", False, []],
["Paradox Cave Lower - Right", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
["Paradox Cave Lower - Right", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Pegasus Boots']],
["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Right", True, ['Fire Rod', 'Pegasus Boots']],
["Paradox Cave Lower - Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']],
["Paradox Cave Lower - Far Right", False, []],
["Paradox Cave Lower - Far Right", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
["Paradox Cave Lower - Far Right", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Pegasus Boots']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Far Right", False, ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Progressive Bow', 'Fire Rod', 'Cane of Somaria']],
["Paradox Cave Lower - Far Right", True, ['Fire Rod', 'Pegasus Boots']],
["Paradox Cave Lower - Far Right", True, ['Cane of Somaria', 'Flute', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Sword', 'Progressive Sword', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Bow', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Lower - Far Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']],
["Paradox Cave Upper - Left", False, []],
["Paradox Cave Upper - Left", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
["Paradox Cave Upper - Left", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Pegasus Boots']],
["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Upper - Left", True, ['Flute', 'Magic Mirror']],
["Paradox Cave Upper - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Pegasus Boots']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']],
["Paradox Cave Upper - Right", False, []],
["Paradox Cave Upper - Right", False, [], ['Pegasus Boots', 'Progressive Glove', 'Flute']],
["Paradox Cave Upper - Right", False, [], ['Pegasus Boots', 'Magic Mirror', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Pegasus Boots']],
["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Upper - Right", True, ['Flute', 'Magic Mirror']],
["Paradox Cave Upper - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Pegasus Boots']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror']],
])
def testWestDarkWorldDeathMountain(self):

View File

@@ -6,13 +6,14 @@ class TestDungeons(TestVanillaOWG):
def testFirstDungeonChests(self):
self.run_location_tests([
["Hyrule Castle - Map Chest", True, []],
["Hyrule Castle - Map Guard Key Drop", True, []],
["Hyrule Castle - Map Guard Key Drop", False, []],
["Hyrule Castle - Map Guard Key Drop", True, ['Progressive Sword']],
["Sanctuary", True, []],
["Sewers - Secret Room - Left", False, []],
["Sewers - Secret Room - Left", True, ['Progressive Glove']],
["Sewers - Secret Room - Left", True, ['Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Pegasus Boots', 'Progressive Glove']],
["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Bomb Upgrade (+5)', 'Lamp', 'Small Key (Hyrule Castle)']],
["Eastern Palace - Compass Chest", True, []],
@@ -41,10 +42,9 @@ class TestDungeons(TestVanillaOWG):
["Castle Tower - Room 03", False, []],
["Castle Tower - Room 03", False, ['Progressive Sword'], ['Progressive Sword', 'Cape', 'Beat Agahnim 1']],
["Castle Tower - Room 03", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Room 03", True, ['Progressive Sword', 'Progressive Sword']],
["Castle Tower - Room 03", True, ['Cape', 'Progressive Bow']],
["Castle Tower - Room 03", True, ['Beat Agahnim 1', 'Fire Rod']],
["Castle Tower - Room 03", True, ['Progressive Sword', 'Cape']],
["Castle Tower - Room 03", True, ['Progressive Sword', 'Beat Agahnim 1']],
["Palace of Darkness - Shooter Room", False, []],
["Palace of Darkness - Shooter Room", False, [], ['Moon Pearl']],
@@ -69,9 +69,10 @@ class TestDungeons(TestVanillaOWG):
["Skull Woods - Big Chest", False, []],
["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']],
["Skull Woods - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
#todo: Bomb Jump in logic?
#["Skull Woods - Big Chest", True, ['Magic Mirror', 'Pegasus Boots', 'Big Key (Skull Woods)']],
["Skull Woods - Big Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Big Key (Skull Woods)']],
["Skull Woods - Big Chest", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Pegasus Boots', 'Big Key (Skull Woods)']],
["Skull Woods - Big Key Chest", False, []],
["Skull Woods - Big Key Chest", True, ['Magic Mirror', 'Pegasus Boots']],
@@ -111,8 +112,8 @@ class TestDungeons(TestVanillaOWG):
["Turtle Rock - Chain Chomps", False, []],
#todo: does clip require sword?
#["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Pegasus Boots']],
["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Pegasus Boots', 'Progressive Sword']],
["Turtle Rock - Chain Chomps", True, ['Pegasus Boots', 'Magic Mirror']],
["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Pegasus Boots', 'Progressive Sword', 'Progressive Sword']],
["Turtle Rock - Chain Chomps", True, ['Pegasus Boots', 'Magic Mirror', 'Progressive Bow']],
["Turtle Rock - Crystaroller Room", False, []],
["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)']],

View File

@@ -25,17 +25,21 @@ class TestLightWorld(TestVanillaOWG):
["Kakariko Tavern", True, []],
["Chicken House", True, []],
["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Chicken House", True, ['Bomb Upgrade (+5)']],
["Aginah's Cave", True, []],
["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Left", True, []],
["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Middle", True, ['Pegasus Boots']],
["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Right", True, ['Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, []],
["Sahasrahla's Hut - Right", True, []],
["Kakariko Well - Top", True, []],
["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)']],
["Kakariko Well - Left", True, []],
@@ -45,7 +49,8 @@ class TestLightWorld(TestVanillaOWG):
["Kakariko Well - Bottom", True, []],
["Blind's Hideout - Top", True, []],
["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)']],
["Blind's Hideout - Left", True, []],
@@ -59,15 +64,19 @@ class TestLightWorld(TestVanillaOWG):
["Bonk Rock Cave", False, [], ['Pegasus Boots']],
["Bonk Rock Cave", True, ['Pegasus Boots']],
["Mini Moldorm Cave - Far Left", True, []],
["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Left", True, []],
["Mini Moldorm Cave - Right", True, []],
["Mini Moldorm Cave - Far Right", True, []],
["Ice Rod Cave", True, []],
["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)']],
["Bottle Merchant", True, []],
@@ -126,12 +135,13 @@ class TestLightWorld(TestVanillaOWG):
["Graveyard Cave", False, []],
["Graveyard Cave", False, [], ['Pegasus Boots', 'Magic Mirror']],
["Graveyard Cave", False, [], ['Pegasus Boots', 'Moon Pearl']],
["Graveyard Cave", True, ['Pegasus Boots']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Pegasus Boots']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Checkerboard Cave", False, []],
["Checkerboard Cave", False, [], ['Progressive Glove']],
@@ -140,8 +150,6 @@ class TestLightWorld(TestVanillaOWG):
["Checkerboard Cave", True, ['Pegasus Boots', 'Progressive Glove']],
["Checkerboard Cave", True, ['Flute', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Generous Guy", True, []],
["Library", False, []],
["Library", False, [], ['Pegasus Boots']],
["Library", True, ['Pegasus Boots']],
@@ -152,7 +160,10 @@ class TestLightWorld(TestVanillaOWG):
["Potion Shop", False, [], ['Mushroom']],
["Potion Shop", True, ['Mushroom']],
["Maze Race", True, []],
["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Magic Mirror', 'Pegasus Boots']],
["Maze Race", True, ['Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Maze Race", True, ['Bomb Upgrade (+5)']],
["Maze Race", True, ['Pegasus Boots']],
["Desert Ledge", False, []],
["Desert Ledge", False, [], ['Pegasus Boots', 'Book of Mudora', 'Flute']],

View File

@@ -11,7 +11,9 @@ class TestVanillaOWG(TestBase, LTTPTestBase):
def setUp(self):
self.world_setup()
self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.multiworld.logic[1] = "owglitches"
self.multiworld.glitches_required[1] = "overworld_glitches"
self.multiworld.bombless_start[1].value = True
self.multiworld.shuffle_capacity_upgrades[1].value = True
self.multiworld.worlds[1].er_seed = 0
self.multiworld.worlds[1].create_regions()
self.multiworld.worlds[1].create_items()

View File

@@ -7,43 +7,48 @@ class TestDarkWorld(TestVanilla):
self.run_location_tests([
["Hype Cave - Top", False, []],
["Hype Cave - Top", False, [], ['Moon Pearl']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Top", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Top", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Right", False, []],
["Hype Cave - Middle Right", False, [], ['Moon Pearl']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Right", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Right", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Left", False, []],
["Hype Cave - Middle Left", False, [], ['Moon Pearl']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Left", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Middle Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Middle Left", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Bottom", False, []],
["Hype Cave - Bottom", False, [], ['Moon Pearl']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Bottom", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Bottom", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Generous Guy", False, []],
["Hype Cave - Generous Guy", False, [], ['Moon Pearl']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Hype Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Hype Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Stumpy", False, []],
["Stumpy", False, [], ['Moon Pearl']],
@@ -66,10 +71,11 @@ class TestDarkWorld(TestVanilla):
self.run_location_tests([
["Brewery", False, []],
["Brewery", False, [], ['Moon Pearl']],
["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Brewery", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']],
["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Brewery", True, ['Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Brewery", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Progressive Glove', 'Hammer']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Brewery", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["C-Shaped House", False, []],
["C-Shaped House", False, [], ['Moon Pearl']],

View File

@@ -48,7 +48,8 @@ class TestDeathMountain(TestVanilla):
["Mimic Cave", False, [], ['Moon Pearl']],
["Mimic Cave", False, [], ['Cane of Somaria']],
["Mimic Cave", False, ['Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Mimic Cave", True, ['Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Spiral Cave", False, []],
["Spiral Cave", False, [], ['Progressive Glove', 'Flute']],
@@ -73,10 +74,10 @@ class TestDeathMountain(TestVanilla):
["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Left", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Far Left", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Hookshot', 'Cane of Somaria']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Far Left", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']],
["Paradox Cave Lower - Left", False, []],
["Paradox Cave Lower - Left", False, [], ['Progressive Glove', 'Flute']],
@@ -87,10 +88,10 @@ class TestDeathMountain(TestVanilla):
["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Left", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Left", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Left", True, ['Flute', 'Hookshot', 'Cane of Somaria']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Left", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']],
["Paradox Cave Lower - Middle", False, []],
["Paradox Cave Lower - Middle", False, [], ['Progressive Glove', 'Flute']],
@@ -101,10 +102,10 @@ class TestDeathMountain(TestVanilla):
["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Middle", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Middle", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Hookshot', 'Cane of Somaria']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Middle", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']],
["Paradox Cave Lower - Right", False, []],
["Paradox Cave Lower - Right", False, [], ['Progressive Glove', 'Flute']],
@@ -115,10 +116,10 @@ class TestDeathMountain(TestVanilla):
["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Right", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Right", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Right", True, ['Flute', 'Hookshot', 'Cane of Somaria']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Right", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']],
["Paradox Cave Lower - Far Right", False, []],
["Paradox Cave Lower - Far Right", False, [], ['Progressive Glove', 'Flute']],
@@ -129,10 +130,10 @@ class TestDeathMountain(TestVanilla):
["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Lower - Far Right", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Lower - Far Right", False, ['Flute', 'Hammer']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Hookshot', 'Cane of Somaria']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Bomb Upgrade (+5)']],
["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer', 'Progressive Sword', 'Progressive Sword']],
["Paradox Cave Lower - Far Right", True, ['Flute', 'Magic Mirror', 'Hammer', 'Fire Rod']],
["Paradox Cave Upper - Left", False, []],
["Paradox Cave Upper - Left", False, [], ['Progressive Glove', 'Flute']],
@@ -143,10 +144,11 @@ class TestDeathMountain(TestVanilla):
["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Left", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Upper - Left", False, ['Flute', 'Hammer']],
["Paradox Cave Upper - Left", True, ['Flute', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Left", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Left", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", False, []],
["Paradox Cave Upper - Right", False, [], ['Progressive Glove', 'Flute']],
@@ -157,10 +159,11 @@ class TestDeathMountain(TestVanilla):
["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot']],
["Paradox Cave Upper - Right", False, ['Flute', 'Magic Mirror']],
["Paradox Cave Upper - Right", False, ['Flute', 'Hammer']],
["Paradox Cave Upper - Right", True, ['Flute', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", True, ['Flute', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Hookshot']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Lamp', 'Magic Mirror', 'Hammer']],
["Paradox Cave Upper - Right", True, ['Bomb Upgrade (+5)', 'Flute', 'Magic Mirror', 'Hammer']],
])
def testWestDarkWorldDeathMountain(self):

View File

@@ -29,17 +29,21 @@ class TestLightWorld(TestVanilla):
["Kakariko Tavern", True, []],
["Chicken House", True, []],
["Chicken House", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Chicken House", True, ['Bomb Upgrade (+5)']],
["Aginah's Cave", True, []],
["Aginah's Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Aginah's Cave", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Left", True, []],
["Sahasrahla's Hut - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Left", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Middle", True, ['Pegasus Boots']],
["Sahasrahla's Hut - Middle", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Pegasus Boots']],
["Sahasrahla's Hut - Right", True, ['Bomb Upgrade (+5)']],
["Sahasrahla's Hut - Right", True, ['Pegasus Boots']],
["Sahasrahla's Hut - Middle", True, []],
["Sahasrahla's Hut - Right", True, []],
["Kakariko Well - Top", True, []],
["Kakariko Well - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Kakariko Well - Top", True, ['Bomb Upgrade (+5)']],
["Kakariko Well - Left", True, []],
@@ -49,7 +53,8 @@ class TestLightWorld(TestVanilla):
["Kakariko Well - Bottom", True, []],
["Blind's Hideout - Top", True, []],
["Blind's Hideout - Top", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Blind's Hideout - Top", True, ['Bomb Upgrade (+5)']],
["Blind's Hideout - Left", True, []],
@@ -63,15 +68,19 @@ class TestLightWorld(TestVanilla):
["Bonk Rock Cave", False, [], ['Pegasus Boots']],
["Bonk Rock Cave", True, ['Pegasus Boots']],
["Mini Moldorm Cave - Far Left", True, []],
["Mini Moldorm Cave - Far Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Left", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Far Right", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Far Right", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Generous Guy", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mini Moldorm Cave - Generous Guy", True, ['Bomb Upgrade (+5)', 'Progressive Sword']],
["Mini Moldorm Cave - Left", True, []],
["Mini Moldorm Cave - Right", True, []],
["Mini Moldorm Cave - Far Right", True, []],
["Ice Rod Cave", True, []],
["Ice Rod Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Rod Cave", True, ['Bomb Upgrade (+5)']],
["Bottle Merchant", True, []],
@@ -136,11 +145,12 @@ class TestLightWorld(TestVanilla):
["Graveyard Cave", False, []],
["Graveyard Cave", False, [], ['Magic Mirror']],
["Graveyard Cave", False, [], ['Moon Pearl']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Graveyard Cave", True, ['Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Graveyard Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Progressive Glove', 'Hammer']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Hammer', 'Hookshot']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Progressive Glove', 'Hookshot']],
["Graveyard Cave", True, ['Bomb Upgrade (+5)', 'Moon Pearl', 'Magic Mirror', 'Beat Agahnim 1', 'Flippers', 'Hookshot']],
["Checkerboard Cave", False, []],
["Checkerboard Cave", False, [], ['Progressive Glove']],
@@ -148,7 +158,6 @@ class TestLightWorld(TestVanilla):
["Checkerboard Cave", False, [], ['Magic Mirror']],
["Checkerboard Cave", True, ['Flute', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Mini Moldorm Cave - Generous Guy", True, []],
["Library", False, []],
["Library", False, [], ['Pegasus Boots']],
@@ -160,7 +169,10 @@ class TestLightWorld(TestVanilla):
["Potion Shop", False, [], ['Mushroom']],
["Potion Shop", True, ['Mushroom']],
["Maze Race", True, []],
["Maze Race", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Magic Mirror', 'Pegasus Boots']],
["Maze Race", True, ['Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']],
["Maze Race", True, ['Bomb Upgrade (+5)']],
["Maze Race", True, ['Pegasus Boots']],
["Desert Ledge", False, []],
["Desert Ledge", False, [], ['Book of Mudora', 'Flute']],

View File

@@ -9,8 +9,10 @@ from worlds.alttp.test import LTTPTestBase
class TestVanilla(TestBase, LTTPTestBase):
def setUp(self):
self.world_setup()
self.multiworld.logic[1] = "noglitches"
self.multiworld.glitches_required[1] = "no_glitches"
self.multiworld.difficulty_requirements[1] = difficulties['normal']
self.multiworld.bombless_start[1].value = True
self.multiworld.shuffle_capacity_upgrades[1].value = True
self.multiworld.worlds[1].er_seed = 0
self.multiworld.worlds[1].create_regions()
self.multiworld.worlds[1].create_items()

View File

@@ -14,7 +14,7 @@ from worlds.generic.Rules import forbid_item
class BumpStikWeb(WebWorld):
tutorials = [Tutorial(
"Bumper Stickers Setup Tutorial",
"Bumper Stickers Setup Guide",
"A guide to setting up the Archipelago Bumper Stickers software on your computer.",
"English",
"setup_en.md",

View File

@@ -10,7 +10,7 @@ client_version = 7
class ChecksFinderWeb(WebWorld):
tutorials = [Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago ChecksFinder software on your computer. This guide covers "
"single-player, multiworld, and related software.",
"English",

View File

@@ -15,7 +15,7 @@ from .Options import RandomizeWeaponLevelOption, PoolTypeOption, EarlySmallLothr
class DarkSouls3Web(WebWorld):
bug_report_page = "https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client/issues"
setup_en = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago Dark Souls III randomizer on your computer.",
"English",
"setup_en.md",

View File

@@ -14,7 +14,7 @@ client_version = 0
class DLCqwebworld(WebWorld):
setup_en = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago DLCQuest game on your computer.",
"English",
"setup_en.md",

View File

@@ -53,7 +53,7 @@ If this file does exist, then it will be used.
class FactorioWeb(WebWorld):
tutorials = [Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago Factorio software on your computer.",
"English",
"setup_en.md",

View File

@@ -278,10 +278,10 @@
tag: forbid
check: True
achievement: The Seeker
BEAR:
BEAR (1):
id: Heteronym Room/Panel_bear_bear
tag: midwhite
MINE:
MINE (1):
id: Heteronym Room/Panel_mine_mine
tag: double midwhite
subtag: left
@@ -297,7 +297,7 @@
DOES:
id: Heteronym Room/Panel_does_does
tag: midwhite
MOBILE:
MOBILE (1):
id: Heteronym Room/Panel_mobile_mobile
tag: double midwhite
subtag: left
@@ -399,8 +399,7 @@
door: Crossroads Entrance
The Tenacious:
door: Tenacious Entrance
Warts Straw Area:
door: Symmetry Door
Near Far Area: True
Hedge Maze:
door: Shortcut to Hedge Maze
Orange Tower First Floor:
@@ -427,14 +426,6 @@
id: Palindrome Room/Panel_slaughter_laughter
colors: red
tag: midred
NEAR:
id: Symmetry Room/Panel_near_far
colors: black
tag: botblack
FAR:
id: Symmetry Room/Panel_far_near
colors: black
tag: botblack
TRACE:
id: Maze Room/Panel_trace_trace
tag: midwhite
@@ -477,14 +468,6 @@
group: Entrances to The Tenacious
panels:
- SLAUGHTER
Symmetry Door:
id:
- Symmetry Room Area Doors/Door_near_far
- Symmetry Room Area Doors/Door_far_near
group: Symmetry Doors
panels:
- NEAR
- FAR
Shortcut to Hedge Maze:
id: Maze Area Doors/Door_trace_trace
group: Hedge Maze Doors
@@ -548,7 +531,7 @@
id: Lingo Room/Panel_shortcut
colors: yellow
tag: midyellow
PILGRIMAGE:
PILGRIM:
id: Lingo Room/Panel_pilgrim
colors: blue
tag: midblue
@@ -569,7 +552,7 @@
Exit:
event: True
panels:
- PILGRIMAGE
- PILGRIM
Pilgrim Room:
entrances:
The Seeker:
@@ -755,7 +738,7 @@
panels:
- TURN
- room: Orange Tower Fourth Floor
panel: RUNT
panel: RUNT (1)
Words Sword Door:
id:
- Shuffle Room Area Doors/Door_words_shuffle_3
@@ -962,11 +945,36 @@
- LEVEL (White)
- RACECAR (White)
- SOLOS (White)
Near Far Area:
entrances:
Hub Room: True
Warts Straw Area:
door: Door
panels:
NEAR:
id: Symmetry Room/Panel_near_far
colors: black
tag: botblack
FAR:
id: Symmetry Room/Panel_far_near
colors: black
tag: botblack
doors:
Door:
id:
- Symmetry Room Area Doors/Door_near_far
- Symmetry Room Area Doors/Door_far_near
group: Symmetry Doors
item_name: Symmetry Room - Near Far Door
location_name: Symmetry Room - NEAR, FAR
panels:
- NEAR
- FAR
Warts Straw Area:
entrances:
Hub Room:
room: Hub Room
door: Symmetry Door
Near Far Area:
room: Near Far Area
door: Door
Leaf Feel Area:
door: Door
panels:
@@ -984,6 +992,8 @@
- Symmetry Room Area Doors/Door_warts_straw
- Symmetry Room Area Doors/Door_straw_warts
group: Symmetry Doors
item_name: Symmetry Room - Warts Straw Door
location_name: Symmetry Room - WARTS, STRAW
panels:
- WARTS
- STRAW
@@ -1009,6 +1019,8 @@
- Symmetry Room Area Doors/Door_leaf_feel
- Symmetry Room Area Doors/Door_feel_leaf
group: Symmetry Doors
item_name: Symmetry Room - Leaf Feel Door
location_name: Symmetry Room - LEAF, FEEL
panels:
- LEAF
- FEEL
@@ -1120,7 +1132,7 @@
tag: forbid
required_panel:
- room: Outside The Bold
panel: MOUTH
panel: SOUND
- room: Outside The Bold
panel: YEAST
- room: Outside The Bold
@@ -1166,7 +1178,7 @@
group: Color Hunt Barriers
skip_location: True
panels:
- room: Champion's Rest
- room: Color Hunt
panel: PURPLE
Hallway Door:
id: Red Blue Purple Room Area Doors/Door_room_2
@@ -1436,7 +1448,7 @@
entrances:
The Perceptive: True
panels:
NAPS:
SPAN:
id: Naps Room/Panel_naps_span
colors: black
tag: midblack
@@ -1462,7 +1474,7 @@
location_name: The Fearless - First Floor Puzzles
group: Fearless Doors
panels:
- NAPS
- SPAN
- TEAM
- TEEM
- IMPATIENT
@@ -1564,11 +1576,11 @@
required_door:
door: Stairs
achievement: The Observant
BACK:
FOUR (1):
id: Look Room/Panel_four_back
colors: green
tag: forbid
SIDE:
FOUR (2):
id: Look Room/Panel_four_side
colors: green
tag: forbid
@@ -1578,87 +1590,87 @@
hunt: True
required_door:
door: Backside Door
STAIRS:
SIX:
id: Look Room/Panel_six_stairs
colors: green
tag: forbid
WAYS:
FOUR (3):
id: Look Room/Panel_four_ways
colors: green
tag: forbid
"ON":
TWO (1):
id: Look Room/Panel_two_on
colors: green
tag: forbid
UP:
TWO (2):
id: Look Room/Panel_two_up
colors: green
tag: forbid
SWIMS:
FIVE:
id: Look Room/Panel_five_swims
colors: green
tag: forbid
UPSTAIRS:
BELOW (1):
id: Look Room/Panel_eight_upstairs
colors: green
tag: forbid
required_door:
door: Stairs
TOIL:
BLUE:
id: Look Room/Panel_blue_toil
colors: green
tag: forbid
required_door:
door: Stairs
STOP:
BELOW (2):
id: Look Room/Panel_four_stop
colors: green
tag: forbid
required_door:
door: Stairs
TOP:
MINT (1):
id: Look Room/Panel_aqua_top
colors: green
tag: forbid
required_door:
door: Stairs
HI:
ESACREWOL:
id: Look Room/Panel_blue_hi
colors: green
tag: forbid
required_door:
door: Stairs
HI (2):
EULB:
id: Look Room/Panel_blue_hi2
colors: green
tag: forbid
required_door:
door: Stairs
"31":
NUMBERS (1):
id: Look Room/Panel_numbers_31
colors: green
tag: forbid
required_door:
door: Stairs
"52":
NUMBERS (2):
id: Look Room/Panel_numbers_52
colors: green
tag: forbid
required_door:
door: Stairs
OIL:
MINT (2):
id: Look Room/Panel_aqua_oil
colors: green
tag: forbid
required_door:
door: Stairs
BACKSIDE (GREEN):
GREEN (1):
id: Look Room/Panel_eight_backside
colors: green
tag: forbid
required_door:
door: Stairs
SIDEWAYS:
GREEN (2):
id: Look Room/Panel_eight_sideways
colors: green
tag: forbid
@@ -1669,13 +1681,13 @@
id: Maze Area Doors/Door_backside
group: Backside Doors
panels:
- BACK
- SIDE
- FOUR (1)
- FOUR (2)
Stairs:
id: Maze Area Doors/Door_stairs
group: Observant Doors
panels:
- STAIRS
- SIX
The Incomparable:
entrances:
The Observant: True # Assuming that access to The Observant includes access to the right entrance
@@ -1957,7 +1969,7 @@
group: Color Hunt Barriers
skip_location: True
panels:
- room: Champion's Rest
- room: Color Hunt
panel: RED
Rhyme Room Entrance:
id: Double Room Area Doors/Door_room_entry_stairs2
@@ -1975,9 +1987,9 @@
- Color Arrow Room Doors/Door_orange_hider_1
- Color Arrow Room Doors/Door_orange_hider_2
- Color Arrow Room Doors/Door_orange_hider_3
location_name: Color Hunt - RED and YELLOW
group: Champion's Rest - Color Barriers
item_name: Champion's Rest - Orange Barrier
location_name: Color Barriers - RED and YELLOW
group: Color Hunt Barriers
item_name: Color Hunt - Orange Barrier
panels:
- RED
- room: Directional Gallery
@@ -2005,7 +2017,7 @@
Courtyard: True
Roof: True # through the sunwarp
panels:
RUNT:
RUNT (1):
id: Shuffle Room/Panel_turn_runt2
colors: yellow
tag: midyellow
@@ -2219,6 +2231,7 @@
- Master Room Doors/Door_master_down
- Master Room Doors/Door_master_down2
skip_location: True
item_name: Mastery
panels:
- THE MASTER
Mastery Panels:
@@ -2235,25 +2248,25 @@
panel: MASTERY
- room: Hedge Maze
panel: MASTERY (1)
- room: Roof
panel: MASTERY (1)
- room: Roof
panel: MASTERY (2)
- room: Behind A Smile
panel: MASTERY
- room: Sixteen Colorful Squares
panel: MASTERY
- MASTERY
- room: Hedge Maze
panel: MASTERY (2)
- room: Roof
panel: MASTERY (3)
- room: Roof
panel: MASTERY (4)
- room: Roof
panel: MASTERY (5)
- room: Among Treetops
panel: MASTERY
- room: Horizon's Edge
panel: MASTERY
- room: Beneath The Lookout
panel: MASTERY
- room: Elements Area
panel: MASTERY
- room: Pilgrim Antechamber
panel: MASTERY
- room: Roof
panel: MASTERY (6)
- room: Rooftop Staircase
panel: MASTERY
paintings:
- id: map_painting2
orientation: north
@@ -2265,52 +2278,75 @@
Crossroads:
room: Crossroads
door: Roof Access
Behind A Smile:
entrances:
Roof: True
panels:
MASTERY (1):
MASTERY:
id: Master Room/Panel_mastery_mastery6
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
MASTERY (2):
id: Master Room/Panel_mastery_mastery7
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
MASTERY (3):
id: Master Room/Panel_mastery_mastery10
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
MASTERY (4):
id: Master Room/Panel_mastery_mastery11
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
MASTERY (5):
id: Master Room/Panel_mastery_mastery12
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
MASTERY (6):
id: Master Room/Panel_mastery_mastery15
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
STAIRCASE:
id: Open Areas/Panel_staircase
tag: midwhite
Sixteen Colorful Squares:
entrances:
Roof: True
panels:
MASTERY:
id: Master Room/Panel_mastery_mastery7
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
Among Treetops:
entrances:
Roof: True
panels:
MASTERY:
id: Master Room/Panel_mastery_mastery10
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
Horizon's Edge:
entrances:
Roof: True
panels:
MASTERY:
id: Master Room/Panel_mastery_mastery11
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
Beneath The Lookout:
entrances:
Roof: True
panels:
MASTERY:
id: Master Room/Panel_mastery_mastery12
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
Rooftop Staircase:
entrances:
Roof: True
panels:
MASTERY:
id: Master Room/Panel_mastery_mastery15
tag: midwhite
hunt: True
required_door:
room: Orange Tower Seventh Floor
door: Mastery
Orange Tower Basement:
entrances:
Orange Tower Sixth Floor:
@@ -2382,7 +2418,7 @@
group: Color Hunt Barriers
skip_location: True
panels:
- room: Champion's Rest
- room: Color Hunt
panel: GREEN
paintings:
- id: flower_painting_7
@@ -2632,9 +2668,7 @@
tag: forbid
doors:
Progress Door:
id:
- Doorway Room Doors/Door_gray
- Doorway Room Doors/Door_gray2 # See comment below
id: Doorway Room Doors/Door_gray
item_name: The Colorful - Gray Door
location_name: The Colorful - Gray
group: Colorful Doors
@@ -2784,6 +2818,7 @@
door: Exit
Eight Alcove:
door: Eight Door
The Optimistic: True
panels:
SEVEN (1):
id: Backside Room/Panel_seven_seven_5
@@ -2833,21 +2868,11 @@
id: Rhyme Room/Panel_locked_knocked
colors: purple
tag: midpurp
BACKSIDE:
id: Backside Room/Panel_backside_1
tag: midwhite
The Optimistic:
id: Countdown Panels/Panel_optimistic_optimistic
check: True
tag: forbid
required_door:
door: Backsides
achievement: The Optimistic
PAST:
PAST (1):
id: Shuffle Room/Panel_past_present
colors: brown
tag: botbrown
FUTURE:
FUTURE (1):
id: Shuffle Room/Panel_future_present
colors:
- brown
@@ -2893,14 +2918,14 @@
group: Color Hunt Barriers
skip_location: True
panels:
- room: Champion's Rest
- room: Color Hunt
panel: BLUE
Orange Barrier:
id: Color Arrow Room Doors/Door_orange_3
group: Color Hunt Barriers
skip_location: True
panels:
- room: Champion's Rest
- room: Color Hunt
panel: ORANGE
Initiated Entrance:
id: Red Blue Purple Room Area Doors/Door_locked_knocked
@@ -2912,9 +2937,9 @@
# containing region.
Green Barrier:
id: Color Arrow Room Doors/Door_green_hider_1
location_name: Color Hunt - BLUE and YELLOW
item_name: Champion's Rest - Green Barrier
group: Champion's Rest - Color Barriers
location_name: Color Barriers - BLUE and YELLOW
item_name: Color Hunt - Green Barrier
group: Color Hunt Barriers
panels:
- BLUE
- room: Directional Gallery
@@ -2924,9 +2949,9 @@
- Color Arrow Room Doors/Door_purple_hider_1
- Color Arrow Room Doors/Door_purple_hider_2
- Color Arrow Room Doors/Door_purple_hider_3
location_name: Color Hunt - RED and BLUE
item_name: Champion's Rest - Purple Barrier
group: Champion's Rest - Color Barriers
location_name: Color Barriers - RED and BLUE
item_name: Color Hunt - Purple Barrier
group: Color Hunt Barriers
panels:
- BLUE
- room: Orange Tower Third Floor
@@ -2936,7 +2961,7 @@
- Color Arrow Room Doors/Door_all_hider_1
- Color Arrow Room Doors/Door_all_hider_2
- Color Arrow Room Doors/Door_all_hider_3
location_name: Color Hunt - GREEN, ORANGE and PURPLE
location_name: Color Barriers - GREEN, ORANGE and PURPLE
item_name: Champion's Rest - Entrance
panels:
- ORANGE
@@ -2944,17 +2969,6 @@
panel: GREEN
- room: Outside The Agreeable
panel: PURPLE
Backsides:
event: True
panels:
- room: The Observant
panel: BACKSIDE
- room: Yellow Backside Area
panel: BACKSIDE
- room: Directional Gallery
panel: BACKSIDE
- room: The Bearer
panel: BACKSIDE
Eight Door:
id: Red Blue Purple Room Area Doors/Door_a_strands2
skip_location: True
@@ -3064,6 +3078,28 @@
id: Rhyme Room/Panel_bed_dead
colors: purple
tag: toppurp
The Optimistic:
entrances:
Outside The Initiated: True
panels:
BACKSIDE:
id: Backside Room/Panel_backside_1
tag: midwhite
Achievement:
id: Countdown Panels/Panel_optimistic_optimistic
check: True
tag: forbid
required_panel:
- panel: BACKSIDE
- room: The Observant
panel: BACKSIDE
- room: Yellow Backside Area
panel: BACKSIDE
- room: Directional Gallery
panel: BACKSIDE
- room: The Bearer
panel: BACKSIDE
achievement: The Optimistic
The Traveled:
entrances:
Hub Room:
@@ -3164,7 +3200,7 @@
Outside The Undeterred: True
Crossroads: True
Hedge Maze: True
Outside The Initiated: True # backside
The Optimistic: True # backside
Directional Gallery: True # backside
Yellow Backside Area: True
The Bearer:
@@ -3176,12 +3212,12 @@
Outside The Bold:
entrances:
Color Hallways: True
Champion's Rest:
room: Champion's Rest
Color Hunt:
room: Color Hunt
door: Shortcut to The Steady
The Bearer:
room: The Bearer
door: Shortcut to The Bold
door: Entrance
Directional Gallery:
# There is a painting warp here from the Directional Gallery, but it
# only appears when the sixes are revealed. It could be its own item if
@@ -3252,7 +3288,7 @@
tag: midwhite
required_door:
door: Stargazer Door
MOUTH:
SOUND:
id: Cross Room/Panel_mouth_south
colors: purple
tag: midpurp
@@ -3596,7 +3632,7 @@
id: Blue Room/Panel_bone_skeleton
colors: blue
tag: botblue
EYE:
EYE (1):
id: Blue Room/Panel_mouth_face
colors: blue
tag: double botblue
@@ -4002,7 +4038,7 @@
group: Color Hunt Barriers
skip_location: True
panels:
- room: Champion's Rest
- room: Color Hunt
panel: YELLOW
paintings:
- id: smile_painting_7
@@ -4020,12 +4056,15 @@
orientation: south
- id: cherry_painting
orientation: east
Champion's Rest:
Color Hunt:
entrances:
Outside The Bold:
door: Shortcut to The Steady
Orange Tower Fourth Floor: True # sunwarp
Roof: True # through ceiling of sunwarp
Champion's Rest:
room: Outside The Initiated
door: Entrance
panels:
EXIT:
id: Rock Room/Panel_red_red
@@ -4066,41 +4105,6 @@
required_door:
room: Orange Tower Third Floor
door: Orange Barrier
YOU:
id: Color Arrow Room/Panel_you
required_door:
room: Outside The Initiated
door: Entrance
check: True
colors: gray
tag: forbid
ME:
id: Color Arrow Room/Panel_me
colors: gray
tag: forbid
required_door:
room: Outside The Initiated
door: Entrance
SECRET BLUE:
# Pretend this and the other two are white, because they are snipes.
# TODO: Extract them and randomize them?
id: Color Arrow Room/Panel_secret_blue
tag: forbid
required_door:
room: Outside The Initiated
door: Entrance
SECRET YELLOW:
id: Color Arrow Room/Panel_secret_yellow
tag: forbid
required_door:
room: Outside The Initiated
door: Entrance
SECRET RED:
id: Color Arrow Room/Panel_secret_red
tag: forbid
required_door:
room: Outside The Initiated
door: Entrance
doors:
Shortcut to The Steady:
id: Rock Room Doors/Door_hint
@@ -4115,16 +4119,39 @@
required_door:
room: Outside The Initiated
door: Entrance
Champion's Rest:
entrances:
Color Hunt:
room: Outside The Initiated
door: Entrance
panels:
YOU:
id: Color Arrow Room/Panel_you
check: True
colors: gray
tag: forbid
ME:
id: Color Arrow Room/Panel_me
colors: gray
tag: forbid
SECRET BLUE:
# Pretend this and the other two are white, because they are snipes.
# TODO: Extract them and randomize them?
id: Color Arrow Room/Panel_secret_blue
tag: forbid
SECRET YELLOW:
id: Color Arrow Room/Panel_secret_yellow
tag: forbid
SECRET RED:
id: Color Arrow Room/Panel_secret_red
tag: forbid
paintings:
- id: colors_painting
orientation: south
enter_only: True
required_door:
room: Outside The Initiated
door: Entrance
The Bearer:
entrances:
Outside The Bold:
door: Shortcut to The Bold
door: Entrance
Orange Tower Fifth Floor:
room: Art Gallery
door: Exit
@@ -4201,7 +4228,7 @@
- yellow
tag: mid red yellow
doors:
Shortcut to The Bold:
Entrance:
id: Red Blue Purple Room Area Doors/Door_middle_middle
panels:
- MIDDLE
@@ -4330,21 +4357,21 @@
door: Side Area Shortcut
Roof: True
panels:
SNOW:
SMILE:
id: Cross Room/Panel_smile_lime
colors:
- red
- yellow
tag: mid yellow red
SMILE:
required_panel:
room: The Bearer (North)
panel: WARTS
SNOW:
id: Cross Room/Panel_snow_won
colors:
- red
- yellow
tag: mid red yellow
required_panel:
room: The Bearer (North)
panel: WARTS
doors:
Side Area Shortcut:
event: True
@@ -4423,7 +4450,7 @@
- room: The Bearer (West)
panel: SMILE
- room: Outside The Bold
panel: MOUTH
panel: SOUND
- room: Outside The Bold
panel: YEAST
- room: Outside The Bold
@@ -6138,7 +6165,7 @@
id: Painting Room/Panel_our_four
colors: blue
tag: midblue
ONE ROAD MANY TURNS:
ORDER:
id: Painting Room/Panel_order_onepathmanyturns
tag: forbid
colors:
@@ -6193,8 +6220,9 @@
Exit:
id: Tower Room Area Doors/Door_painting_exit
include_reduce: True
item_name: Orange Tower Fifth Floor - Quadruple Intersection
panels:
- ONE ROAD MANY TURNS
- ORDER
paintings:
- id: smile_painting_3
orientation: west
@@ -6212,7 +6240,6 @@
- Third Floor
- Fourth Floor
- Fifth Floor
- Exit
Art Gallery (Second Floor):
entrances:
Art Gallery:
@@ -6687,7 +6714,7 @@
- room: Rhyme Room (Target)
panel: PISTOL
- room: Rhyme Room (Target)
panel: QUARTZ
panel: GEM
Rhyme Room (Target):
entrances:
Rhyme Room (Smiley): # one-way
@@ -6715,7 +6742,7 @@
tag: syn rhyme
subtag: top
link: rhyme CRYSTAL
QUARTZ:
GEM:
id: Double Room/Panel_crystal_syn
colors: purple
tag: syn rhyme
@@ -6740,7 +6767,7 @@
group: Rhyme Room Doors
panels:
- PISTOL
- QUARTZ
- GEM
- INNOVATIVE (Top)
- INNOVATIVE (Bottom)
paintings:
@@ -6798,22 +6825,18 @@
id: Panel Room/Panel_room_floor_5
colors: gray
tag: forbid
FLOOR (7):
FLOOR (6):
id: Panel Room/Panel_room_floor_7
colors: gray
tag: forbid
FLOOR (8):
FLOOR (7):
id: Panel Room/Panel_room_floor_8
colors: gray
tag: forbid
FLOOR (9):
FLOOR (8):
id: Panel Room/Panel_room_floor_9
colors: gray
tag: forbid
FLOOR (10):
id: Panel Room/Panel_room_floor_10
colors: gray
tag: forbid
CEILING (1):
id: Panel Room/Panel_room_ceiling_1
colors: gray
@@ -6834,6 +6857,10 @@
id: Panel Room/Panel_room_ceiling_5
colors: gray
tag: forbid
CEILING (6):
id: Panel Room/Panel_room_floor_10
colors: gray
tag: forbid
WALL (1):
id: Panel Room/Panel_room_wall_1
colors: gray
@@ -6939,10 +6966,12 @@
MASTERY:
id: Master Room/Panel_mastery_mastery
tag: midwhite
colors: gray
required_door:
room: Orange Tower Seventh Floor
door: Mastery
required_panel:
room: Room Room
panel: WALL (2)
doors:
Excavation:
event: True
@@ -7092,7 +7121,7 @@
id: Hangry Room/Panel_red_top_3
colors: red
tag: topred
FLUMMOXED:
FLUSTERED:
id: Hangry Room/Panel_red_top_4
colors: red
tag: topred
@@ -7595,7 +7624,7 @@
- black
- blue
tag: chain mid black blue
BREAD:
CHEESE:
id: Challenge Room/Panel_bread_mold
colors: brown
tag: double botbrown
@@ -7642,7 +7671,7 @@
id: Challenge Room/Panel_double_anagram_5
colors: yellow
tag: midyellow
FACTS:
FACTS (Chain):
id: Challenge Room/Panel_facts
colors:
- red
@@ -7652,18 +7681,18 @@
id: Challenge Room/Panel_facts2
colors: red
tag: forbid
FACTS (3):
FACTS (2):
id: Challenge Room/Panel_facts3
tag: forbid
FACTS (4):
FACTS (3):
id: Challenge Room/Panel_facts4
colors: blue
tag: forbid
FACTS (5):
FACTS (4):
id: Challenge Room/Panel_facts5
colors: blue
tag: forbid
FACTS (6):
FACTS (5):
id: Challenge Room/Panel_facts6
colors: blue
tag: forbid

View File

@@ -31,12 +31,12 @@ panels:
LIES: 444408
The Seeker:
Achievement: 444409
BEAR: 444410
MINE: 444411
BEAR (1): 444410
MINE (1): 444411
MINE (2): 444412
BOW: 444413
DOES: 444414
MOBILE: 444415
MOBILE (1): 444415
MOBILE (2): 444416
DESERT: 444417
DESSERT: 444418
@@ -57,8 +57,6 @@ panels:
Hub Room:
ORDER: 444432
SLAUGHTER: 444433
NEAR: 444434
FAR: 444435
TRACE: 444436
RAT: 444437
OPEN: 444438
@@ -72,7 +70,7 @@ panels:
EIGHT: 444445
Pilgrim Antechamber:
HOT CRUST: 444446
PILGRIMAGE: 444447
PILGRIM: 444447
MASTERY: 444448
Pilgrim Room:
THIS: 444449
@@ -123,6 +121,9 @@ panels:
RACECAR (White): 444489
SOLOS (White): 444490
Achievement: 444491
Near Far Area:
NEAR: 444434
FAR: 444435
Warts Straw Area:
WARTS: 444492
STRAW: 444493
@@ -187,7 +188,7 @@ panels:
Achievement: 444546
GAZE: 444547
The Fearless (First Floor):
NAPS: 444548
SPAN: 444548
TEAM: 444549
TEEM: 444550
IMPATIENT: 444551
@@ -208,25 +209,25 @@ panels:
EVEN: 444564
The Observant:
Achievement: 444565
BACK: 444566
SIDE: 444567
FOUR (1): 444566
FOUR (2): 444567
BACKSIDE: 444568
STAIRS: 444569
WAYS: 444570
'ON': 444571
UP: 444572
SWIMS: 444573
UPSTAIRS: 444574
TOIL: 444575
STOP: 444576
TOP: 444577
HI: 444578
HI (2): 444579
'31': 444580
'52': 444581
OIL: 444582
BACKSIDE (GREEN): 444583
SIDEWAYS: 444584
SIX: 444569
FOUR (3): 444570
TWO (1): 444571
TWO (2): 444572
FIVE: 444573
BELOW (1): 444574
BLUE: 444575
BELOW (2): 444576
MINT (1): 444577
ESACREWOL: 444578
EULB: 444579
NUMBERS (1): 444580
NUMBERS (2): 444581
MINT (2): 444582
GREEN (1): 444583
GREEN (2): 444584
The Incomparable:
Achievement: 444585
A (One): 444586
@@ -254,7 +255,7 @@ panels:
RED: 444605
DEER + WREN: 444606
Orange Tower Fourth Floor:
RUNT: 444607
RUNT (1): 444607
RUNT (2): 444608
LEARNS + UNSEW: 444609
HOT CRUSTS: 444610
@@ -279,14 +280,19 @@ panels:
THE END: 444620
THE MASTER: 444621
MASTERY: 444622
Roof:
MASTERY (1): 444623
MASTERY (2): 444624
MASTERY (3): 444625
MASTERY (4): 444626
MASTERY (5): 444627
MASTERY (6): 444628
Behind A Smile:
MASTERY: 444623
STAIRCASE: 444629
Sixteen Colorful Squares:
MASTERY: 444624
Among Treetops:
MASTERY: 444625
Horizon's Edge:
MASTERY: 444626
Beneath The Lookout:
MASTERY: 444627
Rooftop Staircase:
MASTERY: 444628
Orange Tower Basement:
MASTERY: 444630
THE LIBRARY: 444631
@@ -341,16 +347,17 @@ panels:
ORANGE: 444663
UNCOVER: 444664
OXEN: 444665
BACKSIDE: 444666
The Optimistic: 444667
PAST: 444668
FUTURE: 444669
PAST (1): 444668
FUTURE (1): 444669
FUTURE (2): 444670
PAST (2): 444671
PRESENT: 444672
SMILE: 444673
ANGERED: 444674
VOTE: 444675
The Optimistic:
BACKSIDE: 444666
Achievement: 444667
The Initiated:
Achievement: 444676
DAUGHTER: 444677
@@ -400,7 +407,7 @@ panels:
ZEN: 444719
SON: 444720
STARGAZER: 444721
MOUTH: 444722
SOUND: 444722
YEAST: 444723
WET: 444724
The Bold:
@@ -442,7 +449,7 @@ panels:
The Undeterred:
Achievement: 444759
BONE: 444760
EYE: 444761
EYE (1): 444761
MOUTH: 444762
IRIS: 444763
EYE (2): 444764
@@ -489,7 +496,7 @@ panels:
WINDWARD: 444803
LIGHT: 444804
REWIND: 444805
Champion's Rest:
Color Hunt:
EXIT: 444806
HUES: 444807
RED: 444808
@@ -498,6 +505,7 @@ panels:
GREEN: 444811
PURPLE: 444812
ORANGE: 444813
Champion's Rest:
YOU: 444814
ME: 444815
SECRET BLUE: 444816
@@ -523,8 +531,8 @@ panels:
TENT: 444832
BOWL: 444833
The Bearer (West):
SNOW: 444834
SMILE: 444835
SMILE: 444834
SNOW: 444835
Bearer Side Area:
SHORTCUT: 444836
POTS: 444837
@@ -719,7 +727,7 @@ panels:
TRUSTWORTHY: 444978
FREE: 444979
OUR: 444980
ONE ROAD MANY TURNS: 444981
ORDER: 444981
Art Gallery (Second Floor):
HOUSE: 444982
PATH: 444983
@@ -777,7 +785,7 @@ panels:
WILD: 445028
KID: 445029
PISTOL: 445030
QUARTZ: 445031
GEM: 445031
INNOVATIVE (Top): 445032
INNOVATIVE (Bottom): 445033
Room Room:
@@ -791,15 +799,15 @@ panels:
FLOOR (3): 445041
FLOOR (4): 445042
FLOOR (5): 445043
FLOOR (7): 445044
FLOOR (8): 445045
FLOOR (9): 445046
FLOOR (10): 445047
FLOOR (6): 445044
FLOOR (7): 445045
FLOOR (8): 445046
CEILING (1): 445048
CEILING (2): 445049
CEILING (3): 445050
CEILING (4): 445051
CEILING (5): 445052
CEILING (6): 445047
WALL (1): 445053
WALL (2): 445054
WALL (3): 445055
@@ -847,7 +855,7 @@ panels:
PANDEMIC (1): 445100
TRINITY: 445101
CHEMISTRY: 445102
FLUMMOXED: 445103
FLUSTERED: 445103
PANDEMIC (2): 445104
COUNTERCLOCKWISE: 445105
FEARLESS: 445106
@@ -933,7 +941,7 @@ panels:
CORNER: 445182
STRAWBERRIES: 445183
GRUB: 445184
BREAD: 445185
CHEESE: 445185
COLOR: 445186
WRITER: 445187
'02759': 445188
@@ -944,12 +952,12 @@ panels:
DUCK LOGO: 445193
AVIAN GREEN: 445194
FEVER TEAR: 445195
FACTS: 445196
FACTS (Chain): 445196
FACTS (1): 445197
FACTS (3): 445198
FACTS (4): 445199
FACTS (5): 445200
FACTS (6): 445201
FACTS (2): 445198
FACTS (3): 445199
FACTS (4): 445200
FACTS (5): 445201
LAPEL SHEEP: 445202
doors:
Starting Room:
@@ -979,9 +987,6 @@ doors:
Tenacious Entrance:
item: 444426
location: 444433
Symmetry Door:
item: 444428
location: 445204
Shortcut to Hedge Maze:
item: 444430
location: 444436
@@ -1038,6 +1043,10 @@ doors:
location: 445210
White Palindromes:
location: 445211
Near Far Area:
Door:
item: 444428
location: 445204
Warts Straw Area:
Door:
item: 444451
@@ -1286,12 +1295,12 @@ doors:
location: 445246
Yellow Barrier:
item: 444538
Champion's Rest:
Color Hunt:
Shortcut to The Steady:
item: 444539
location: 444806
The Bearer:
Shortcut to The Bold:
Entrance:
item: 444540
location: 444820
Backside Door:
@@ -1442,7 +1451,6 @@ door_groups:
Fearless Doors: 444469
Backside Doors: 444473
Orange Tower First Floor - Shortcuts: 444484
Champion's Rest - Color Barriers: 444489
Welcome Back Doors: 444492
Colorful Doors: 444498
Directional Gallery Doors: 444531

View File

@@ -24,14 +24,6 @@ class ItemData(NamedTuple):
return world.options.shuffle_colors > 0
elif self.mode == "doors":
return world.options.shuffle_doors != ShuffleDoors.option_none
elif self.mode == "orange tower":
# door shuffle is on and tower isn't progressive
return world.options.shuffle_doors != ShuffleDoors.option_none \
and not world.options.progressive_orange_tower
elif self.mode == "the colorful":
# complex door shuffle is on and colorful isn't progressive
return world.options.shuffle_doors == ShuffleDoors.option_complex \
and not world.options.progressive_colorful
elif self.mode == "complex door":
return world.options.shuffle_doors == ShuffleDoors.option_complex
elif self.mode == "door group":
@@ -72,12 +64,7 @@ def load_item_data():
door_groups.setdefault(door.group, []).extend(door.door_ids)
if room_name in PROGRESSION_BY_ROOM and door_name in PROGRESSION_BY_ROOM[room_name]:
if room_name == "Orange Tower":
door_mode = "orange tower"
elif room_name == "The Colorful":
door_mode = "the colorful"
else:
door_mode = "special"
door_mode = "special"
ALL_ITEM_TABLE[door.item_name] = \
ItemData(get_door_item_id(room_name, door_name),

View File

@@ -1,3 +1,4 @@
from enum import Enum
from typing import Dict, List, NamedTuple, Optional, Set, Tuple, TYPE_CHECKING
from .items import ALL_ITEM_TABLE
@@ -36,6 +37,27 @@ class PlayerLocation(NamedTuple):
access: AccessRequirements
class ProgressiveItemBehavior(Enum):
DISABLE = 1
SPLIT = 2
PROGRESSIVE = 3
def should_split_progression(progression_name: str, world: "LingoWorld") -> ProgressiveItemBehavior:
if progression_name == "Progressive Orange Tower":
if world.options.progressive_orange_tower:
return ProgressiveItemBehavior.PROGRESSIVE
else:
return ProgressiveItemBehavior.SPLIT
elif progression_name == "Progressive Colorful":
if world.options.progressive_colorful:
return ProgressiveItemBehavior.PROGRESSIVE
else:
return ProgressiveItemBehavior.SPLIT
return ProgressiveItemBehavior.PROGRESSIVE
class LingoPlayerLogic:
"""
Defines logic after a player's options have been applied
@@ -83,10 +105,13 @@ class LingoPlayerLogic:
def handle_non_grouped_door(self, room_name: str, door_data: Door, world: "LingoWorld"):
if room_name in PROGRESSION_BY_ROOM and door_data.name in PROGRESSION_BY_ROOM[room_name]:
if (room_name == "Orange Tower" and not world.options.progressive_orange_tower)\
or (room_name == "The Colorful" and not world.options.progressive_colorful):
progression_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name
progression_handling = should_split_progression(progression_name, world)
if progression_handling == ProgressiveItemBehavior.SPLIT:
self.set_door_item(room_name, door_data.name, door_data.item_name)
else:
self.real_items.append(door_data.item_name)
elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE:
progressive_item_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name
self.set_door_item(room_name, door_data.name, progressive_item_name)
self.real_items.append(progressive_item_name)
@@ -196,9 +221,8 @@ class LingoPlayerLogic:
["Orange Tower Fourth Floor", "Hot Crusts Door"], ["Outside The Initiated", "Shortcut to Hub Room"],
["Orange Tower First Floor", "Shortcut to Hub Room"], ["Directional Gallery", "Shortcut to The Undeterred"],
["Orange Tower First Floor", "Salt Pepper Door"], ["Hub Room", "Crossroads Entrance"],
["Champion's Rest", "Shortcut to The Steady"], ["The Bearer", "Shortcut to The Bold"],
["Art Gallery", "Exit"], ["The Tenacious", "Shortcut to Hub Room"],
["Outside The Agreeable", "Tenacious Entrance"]
["Color Hunt", "Shortcut to The Steady"], ["The Bearer", "Entrance"], ["Art Gallery", "Exit"],
["The Tenacious", "Shortcut to Hub Room"], ["Outside The Agreeable", "Tenacious Entrance"]
]
pilgrimage_reqs = AccessRequirements()
for door in fake_pilgrimage:

View File

@@ -96,7 +96,7 @@ class TestProgressiveArtGallery(LingoTestBase):
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name(["Second Room - Exit Door", "Crossroads - Tower Entrance",
@@ -105,7 +105,7 @@ class TestProgressiveArtGallery(LingoTestBase):
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
progressive_gallery_room = self.get_items_by_name("Progressive Art Gallery")
@@ -115,7 +115,7 @@ class TestProgressiveArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect(progressive_gallery_room[1])
@@ -123,7 +123,7 @@ class TestProgressiveArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect(progressive_gallery_room[2])
@@ -131,7 +131,7 @@ class TestProgressiveArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect(progressive_gallery_room[3])
@@ -139,15 +139,15 @@ class TestProgressiveArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertTrue(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertTrue(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect(progressive_gallery_room[4])
self.collect_by_name("Orange Tower Fifth Floor - Quadruple Intersection")
self.assertTrue(self.multiworld.state.can_reach("Art Gallery", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertTrue(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertTrue(self.can_reach_location("Art Gallery - ORDER"))
self.assertTrue(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
@@ -162,7 +162,7 @@ class TestNoDoorsArtGallery(LingoTestBase):
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name("Yellow")
@@ -170,7 +170,7 @@ class TestNoDoorsArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name("Brown")
@@ -178,7 +178,7 @@ class TestNoDoorsArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name("Blue")
@@ -186,7 +186,7 @@ class TestNoDoorsArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertFalse(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertFalse(self.can_reach_location("Art Gallery - ORDER"))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))
self.collect_by_name(["Orange", "Gray"])
@@ -194,5 +194,5 @@ class TestNoDoorsArtGallery(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))
self.assertTrue(self.multiworld.state.can_reach("Art Gallery (Fourth Floor)", "Region", self.player))
self.assertTrue(self.can_reach_location("Art Gallery - ONE ROAD MANY TURNS"))
self.assertTrue(self.can_reach_location("Art Gallery - ORDER"))
self.assertTrue(self.multiworld.state.can_reach("Orange Tower Fifth Floor", "Region", self.player))

View File

@@ -8,7 +8,8 @@ require 'set'
require 'yaml'
configpath = ARGV[0]
mappath = ARGV[1]
idspath = ARGV[1]
mappath = ARGV[2]
panels = Set["Countdown Panels/Panel_1234567890_wanderlust"]
doors = Set["Naps Room Doors/Door_hider_new1", "Tower Room Area Doors/Door_wanderer_entrance"]
@@ -46,6 +47,8 @@ painting_directives = Set["id", "enter_only", "exit_only", "orientation", "requi
non_counting = 0
ids = YAML.load_file(idspath)
config = YAML.load_file(configpath)
config.each do |room_name, room|
configured_rooms.add(room_name)
@@ -162,6 +165,10 @@ config.each do |room_name, room|
unless bad_subdirectives.empty? then
puts "#{room_name} - #{panel_name} :::: Panel has the following invalid subdirectives: #{bad_subdirectives.join(", ")}"
end
unless ids.include?("panels") and ids["panels"].include?(room_name) and ids["panels"][room_name].include?(panel_name)
puts "#{room_name} - #{panel_name} :::: Panel is missing a location ID"
end
end
(room["doors"] || {}).each do |door_name, door|
@@ -229,6 +236,18 @@ config.each do |room_name, room|
unless bad_subdirectives.empty? then
puts "#{room_name} - #{door_name} :::: Door has the following invalid subdirectives: #{bad_subdirectives.join(", ")}"
end
unless door["skip_item"] or door["event"]
unless ids.include?("doors") and ids["doors"].include?(room_name) and ids["doors"][room_name].include?(door_name) and ids["doors"][room_name][door_name].include?("item")
puts "#{room_name} - #{door_name} :::: Door is missing an item ID"
end
end
unless door["skip_location"] or door["event"]
unless ids.include?("doors") and ids["doors"].include?(room_name) and ids["doors"][room_name].include?(door_name) and ids["doors"][room_name][door_name].include?("location")
puts "#{room_name} - #{door_name} :::: Door is missing a location ID"
end
end
end
(room["paintings"] || []).each do |painting|
@@ -281,6 +300,10 @@ config.each do |room_name, room|
mentioned_doors.add("#{room_name} - #{door}")
end
end
unless ids.include?("progression") and ids["progression"].include?(progression_name)
puts "#{room_name} - #{progression_name} :::: Progression is missing an item ID"
end
end
end
@@ -303,6 +326,10 @@ door_groups.each do |group,num|
if num == 1 then
puts "Door group \"#{group}\" only has one door in it"
end
unless ids.include?("door_groups") and ids["door_groups"].include?(group)
puts "#{group} :::: Door group is missing an item ID"
end
end
slashed_rooms = configured_rooms.select do |room|

View File

@@ -17,7 +17,7 @@ client_version = 1
class MeritousWeb(WebWorld):
tutorials = [Tutorial(
"Meritous Setup Tutorial",
"Meritous Setup Guide",
"A guide to setting up the Archipelago Meritous software on your computer.",
"English",
"setup_en.md",

View File

@@ -17,7 +17,7 @@ class MessengerWeb(WebWorld):
bug_report_page = "https://github.com/alwaysintreble/TheMessengerRandomizerModAP/issues"
tut_en = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up The Messenger randomizer on your computer.",
"English",
"setup_en.md",

View File

@@ -1,6 +1,7 @@
from test.TestBase import WorldTestBase
from .. import MessengerWorld
class MessengerTestBase(WorldTestBase):
game = "The Messenger"
player: int = 1
world: MessengerWorld

View File

@@ -12,5 +12,5 @@ class LocationsTest(MessengerTestBase):
return False
def test_locations_exist(self) -> None:
for location in self.multiworld.worlds[1].location_name_to_id:
for location in self.world.location_name_to_id:
self.assertIsInstance(self.multiworld.get_location(location, self.player), MessengerLocation)

View File

@@ -17,7 +17,7 @@ class ShopCostTest(MessengerTestBase):
self.assertFalse(self.can_reach_location(loc))
def test_shop_prices(self) -> None:
prices: Dict[str, int] = self.multiworld.worlds[self.player].shop_prices
prices: Dict[str, int] = self.world.shop_prices
for loc, price in prices.items():
with self.subTest("prices", loc=loc):
self.assertLessEqual(price, self.multiworld.get_location(f"The Shop - {loc}", self.player).cost)
@@ -51,7 +51,7 @@ class ShopCostMinTest(ShopCostTest):
}
def test_shop_rules(self) -> None:
if self.multiworld.worlds[self.player].total_shards:
if self.world.total_shards:
super().test_shop_rules()
else:
for loc in SHOP_ITEMS:
@@ -85,7 +85,7 @@ class PlandoTest(MessengerTestBase):
with self.subTest("has cost", loc=loc):
self.assertFalse(self.can_reach_location(loc))
prices = self.multiworld.worlds[self.player].shop_prices
prices = self.world.shop_prices
for loc, price in prices.items():
with self.subTest("prices", loc=loc):
if loc == "Karuta Plates":
@@ -98,7 +98,7 @@ class PlandoTest(MessengerTestBase):
self.assertTrue(loc.replace("The Shop - ", "") in SHOP_ITEMS)
self.assertEqual(len(prices), len(SHOP_ITEMS))
figures = self.multiworld.worlds[self.player].figurine_prices
figures = self.world.figurine_prices
for loc, price in figures.items():
with self.subTest("figure prices", loc=loc):
if loc == "Barmath'azel Figurine":

View File

@@ -41,8 +41,8 @@ class HalfSealsRequired(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should have 45 power seals in the item pool and half that required"""
self.assertEqual(self.multiworld.total_seals[self.player], 45)
self.assertEqual(self.multiworld.worlds[self.player].total_seals, 45)
self.assertEqual(self.multiworld.worlds[self.player].required_seals, 22)
self.assertEqual(self.world.total_seals, 45)
self.assertEqual(self.world.required_seals, 22)
total_seals = [seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]
required_seals = [seal for seal in total_seals
if seal.classification == ItemClassification.progression_skip_balancing]
@@ -60,8 +60,8 @@ class ThirtyThirtySeals(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should have 30 power seals in the pool and 33 percent of that required."""
self.assertEqual(self.multiworld.total_seals[self.player], 30)
self.assertEqual(self.multiworld.worlds[self.player].total_seals, 30)
self.assertEqual(self.multiworld.worlds[self.player].required_seals, 10)
self.assertEqual(self.world.total_seals, 30)
self.assertEqual(self.world.required_seals, 10)
total_seals = [seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]
required_seals = [seal for seal in total_seals
if seal.classification == ItemClassification.progression_skip_balancing]
@@ -78,7 +78,7 @@ class MaxSealsNoShards(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should set total seals to 70 since shards aren't shuffled."""
self.assertEqual(self.multiworld.total_seals[self.player], 85)
self.assertEqual(self.multiworld.worlds[self.player].total_seals, 70)
self.assertEqual(self.world.total_seals, 70)
class MaxSealsWithShards(MessengerTestBase):
@@ -91,8 +91,8 @@ class MaxSealsWithShards(MessengerTestBase):
def test_seals_amount(self) -> None:
"""Should have 85 seals in the pool with all required and be a valid seed."""
self.assertEqual(self.multiworld.total_seals[self.player], 85)
self.assertEqual(self.multiworld.worlds[self.player].total_seals, 85)
self.assertEqual(self.multiworld.worlds[self.player].required_seals, 85)
self.assertEqual(self.world.total_seals, 85)
self.assertEqual(self.world.required_seals, 85)
total_seals = [seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]
required_seals = [seal for seal in total_seals
if seal.classification == ItemClassification.progression_skip_balancing]

View File

@@ -37,7 +37,7 @@ class MinecraftWebWorld(WebWorld):
bug_report_page = "https://github.com/KonoTyran/Minecraft_AP_Randomizer/issues/new?assignees=&labels=bug&template=bug_report.yaml&title=%5BBug%5D%3A+Brief+Description+of+bug+here"
setup = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago Minecraft software on your computer. This guide covers"
"single-player, multiworld, and related software.",
"English",

View File

@@ -92,7 +92,7 @@ class OOTSettings(settings.Group):
class OOTWeb(WebWorld):
setup = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago Ocarina of Time software on your computer.",
"English",
"setup_en.md",

View File

@@ -16,7 +16,7 @@ class Overcooked2Web(WebWorld):
bug_report_page = "https://github.com/toasterparty/oc2-modding/issues"
setup_en = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Overcooked! 2 randomizer on your computer.",
"English",
"setup_en.md",

View File

@@ -279,6 +279,7 @@ class PokemonEmeraldWorld(World):
def refresh_tm_choices() -> None:
fill_item_candidates_by_category["TM"] = all_tm_choices.copy()
self.random.shuffle(fill_item_candidates_by_category["TM"])
refresh_tm_choices()
# Create items
for item in default_itempool:
@@ -329,6 +330,7 @@ class PokemonEmeraldWorld(World):
for location in locations:
if location.tags is not None and tag in location.tags:
location.place_locked_item(self.create_event(self.item_id_to_name[location.default_item_code]))
location.progress_type = LocationProgressType.DEFAULT
location.address = None
if self.options.badges == RandomizeBadges.option_vanilla:
@@ -365,6 +367,12 @@ class PokemonEmeraldWorld(World):
}
badge_items.sort(key=lambda item: badge_priority.get(item.name, 0))
# Un-exclude badge locations, since we need to put progression items on them
for location in badge_locations:
location.progress_type = LocationProgressType.DEFAULT \
if location.progress_type == LocationProgressType.EXCLUDED \
else location.progress_type
collection_state = self.multiworld.get_all_state(False)
if self.hm_shuffle_info is not None:
for _, item in self.hm_shuffle_info:
@@ -409,6 +417,12 @@ class PokemonEmeraldWorld(World):
}
hm_items.sort(key=lambda item: hm_priority.get(item.name, 0))
# Un-exclude HM locations, since we need to put progression items on them
for location in hm_locations:
location.progress_type = LocationProgressType.DEFAULT \
if location.progress_type == LocationProgressType.EXCLUDED \
else location.progress_type
collection_state = self.multiworld.get_all_state(False)
# In specific very constrained conditions, fill_restrictive may run

View File

@@ -46,7 +46,7 @@ class PokemonSettings(settings.Group):
class PokemonWebWorld(WebWorld):
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to playing Pokemon Red and Blue with Archipelago.",
"A guide to playing Pokémon Red and Blue with Archipelago.",
"English",
"setup_en.md",
"setup/en",

View File

@@ -45,7 +45,7 @@ class TLoZSettings(settings.Group):
class TLoZWeb(WebWorld):
theme = "stone"
setup = Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up The Legend of Zelda for Archipelago on your computer.",
"English",
"multiworld_en.md",

View File

@@ -59,6 +59,20 @@ class TunicWorld(World):
er_portal_hints: Dict[int, str]
def generate_early(self) -> None:
# Universal tracker stuff, shouldn't do anything in standard gen
if hasattr(self.multiworld, "re_gen_passthrough"):
if "TUNIC" in self.multiworld.re_gen_passthrough:
passthrough = self.multiworld.re_gen_passthrough["TUNIC"]
self.options.start_with_sword.value = passthrough["start_with_sword"]
self.options.keys_behind_bosses.value = passthrough["keys_behind_bosses"]
self.options.sword_progression.value = passthrough["sword_progression"]
self.options.ability_shuffling.value = passthrough["ability_shuffling"]
self.options.logic_rules.value = passthrough["logic_rules"]
self.options.lanternless.value = passthrough["lanternless"]
self.options.maskless.value = passthrough["maskless"]
self.options.hexagon_quest.value = passthrough["hexagon_quest"]
self.options.entrance_rando.value = passthrough["entrance_rando"]
if self.options.start_with_sword and "Sword" not in self.options.start_inventory:
self.options.start_inventory.value["Sword"] = 1
@@ -150,10 +164,20 @@ class TunicWorld(World):
self.tunic_portal_pairs = {}
self.er_portal_hints = {}
self.ability_unlocks = randomize_ability_unlocks(self.random, self.options)
# stuff for universal tracker support, can be ignored for standard gen
if hasattr(self.multiworld, "re_gen_passthrough"):
if "TUNIC" in self.multiworld.re_gen_passthrough:
passthrough = self.multiworld.re_gen_passthrough["TUNIC"]
self.ability_unlocks["Pages 24-25 (Prayer)"] = passthrough["Hexagon Quest Prayer"]
self.ability_unlocks["Pages 42-43 (Holy Cross)"] = passthrough["Hexagon Quest Holy Cross"]
self.ability_unlocks["Pages 52-53 (Icebolt)"] = passthrough["Hexagon Quest Icebolt"]
if self.options.entrance_rando:
portal_pairs, portal_hints = create_er_regions(self)
for portal1, portal2 in portal_pairs.items():
self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
self.er_portal_hints = portal_hints
else:
@@ -199,10 +223,13 @@ class TunicWorld(World):
"ability_shuffling": self.options.ability_shuffling.value,
"hexagon_quest": self.options.hexagon_quest.value,
"fool_traps": self.options.fool_traps.value,
"logic_rules": self.options.logic_rules.value,
"lanternless": self.options.lanternless.value,
"maskless": self.options.maskless.value,
"entrance_rando": self.options.entrance_rando.value,
"Hexagon Quest Prayer": self.ability_unlocks["Pages 24-25 (Prayer)"],
"Hexagon Quest Holy Cross": self.ability_unlocks["Pages 42-43 (Holy Cross)"],
"Hexagon Quest Ice Rod": self.ability_unlocks["Pages 52-53 (Ice Rod)"],
"Hexagon Quest Icebolt": self.ability_unlocks["Pages 52-53 (Icebolt)"],
"Hexagon Quest Goal": self.options.hexagon_goal.value,
"Entrance Rando": self.tunic_portal_pairs
}
@@ -236,44 +263,7 @@ class TunicWorld(World):
return slot_data
# for the universal tracker, doesn't get called in standard gen
def interpret_slot_data(self, slot_data: Dict[str, Any]) -> None:
# bypassing random yaml settings
self.options.start_with_sword.value = slot_data["start_with_sword"]
self.options.keys_behind_bosses.value = slot_data["keys_behind_bosses"]
self.options.sword_progression.value = slot_data["sword_progression"]
self.options.ability_shuffling.value = slot_data["ability_shuffling"]
self.options.hexagon_quest.value = slot_data["hexagon_quest"]
self.ability_unlocks["Pages 24-25 (Prayer)"] = slot_data["Hexagon Quest Prayer"]
self.ability_unlocks["Pages 42-43 (Holy Cross)"] = slot_data["Hexagon Quest Holy Cross"]
self.ability_unlocks["Pages 52-53 (Ice Rod)"] = slot_data["Hexagon Quest Ice Rod"]
# swapping entrances around so the mapping matches what was generated
if slot_data["entrance_rando"]:
from BaseClasses import Entrance
from .er_data import portal_mapping
entrance_dict: Dict[str, Entrance] = {entrance.name: entrance
for region in self.multiworld.get_regions(self.player)
for entrance in region.entrances}
slot_portals: Dict[str, str] = slot_data["Entrance Rando"]
for portal1, portal2 in slot_portals.items():
portal_name1: str = ""
portal_name2: str = ""
entrance1 = None
entrance2 = None
for portal in portal_mapping:
if portal.scene_destination() == portal1:
portal_name1 = portal.name
if portal.scene_destination() == portal2:
portal_name2 = portal.name
for entrance_name, entrance in entrance_dict.items():
if entrance_name.startswith(portal_name1):
entrance1 = entrance
if entrance_name.startswith(portal_name2):
entrance2 = entrance
if entrance1 is None:
raise Exception("entrance1 not found, portal1 is " + portal1)
if entrance2 is None:
raise Exception("entrance2 not found, portal2 is " + portal2)
entrance1.connected_region = entrance2.parent_region
entrance2.connected_region = entrance1.parent_region
@staticmethod
def interpret_slot_data(slot_data: Dict[str, Any]) -> Dict[str, Any]:
# returning slot_data so it regens, giving it back in multiworld.re_gen_passthrough
return slot_data

View File

@@ -730,7 +730,7 @@ for p1, p2 in hallways_ur.items():
# the key is the region you have, the value is the regions you get for having that region
# this is mostly so we don't have to do something overly complex to get this information
dependent_regions: Dict[Tuple[str, ...], List[str]] = {
dependent_regions_restricted: Dict[Tuple[str, ...], List[str]] = {
("Overworld", "Overworld Belltower", "Overworld Swamp Upper Entry", "Overworld Special Shop Entry",
"Overworld West Garden Laurels Entry", "Overworld Southeast Cross Door", "Overworld Temple Door",
"Overworld Fountain Cross Door", "Overworld Town Portal", "Overworld Spawn Portal"):

View File

@@ -16,7 +16,7 @@ fairies = "Fairy"
coins = "Golden Coin"
prayer = "Pages 24-25 (Prayer)"
holy_cross = "Pages 42-43 (Holy Cross)"
ice_rod = "Pages 52-53 (Ice Rod)"
icebolt = "Pages 52-53 (Icebolt)"
key = "Key"
house_key = "Old House Key"
vault_key = "Fortress Vault Key"
@@ -377,13 +377,13 @@ def set_er_region_rules(world: "TunicWorld", ability_unlocks: Dict[str, int], re
# nmg: ice grapple through the big gold door, can do it both ways
regions["Eastern Vault Fortress"].connect(
connecting_region=regions["Eastern Vault Fortress Gold Door"],
name="Fortress Gold Door",
name="Fortress to Gold Door",
rule=lambda state: state.has_all({"Activate Eastern Vault West Fuses",
"Activate Eastern Vault East Fuse"}, player)
or has_ice_grapple_logic(False, state, player, options, ability_unlocks))
regions["Eastern Vault Fortress Gold Door"].connect(
connecting_region=regions["Eastern Vault Fortress"],
name="Fortress Gold Door",
name="Gold Door to Fortress",
rule=lambda state: has_ice_grapple_logic(True, state, player, options, ability_unlocks))
regions["Fortress Grave Path"].connect(
@@ -884,7 +884,7 @@ def set_er_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int])
lambda state: state.has_all({grapple, laurels}, player))
set_rule(multiworld.get_location("East Forest - Ice Rod Grapple Chest", player), lambda state: (
state.has_all({grapple, ice_dagger, fire_wand}, player) and
has_ability(state, player, ice_rod, options, ability_unlocks)))
has_ability(state, player, icebolt, options, ability_unlocks)))
# West Garden
set_rule(multiworld.get_location("West Garden - [North] Across From Page Pickup", player),

View File

@@ -2,8 +2,9 @@ from typing import Dict, List, Set, Tuple, TYPE_CHECKING
from BaseClasses import Region, ItemClassification, Item, Location
from .locations import location_table
from .er_data import Portal, tunic_er_regions, portal_mapping, hallway_helper, hallway_helper_ur, \
dependent_regions, dependent_regions_nmg, dependent_regions_ur
dependent_regions_restricted, dependent_regions_nmg, dependent_regions_ur
from .er_rules import set_er_region_rules
from worlds.generic import PlandoConnection
if TYPE_CHECKING:
from . import TunicWorld
@@ -22,12 +23,17 @@ def create_er_regions(world: "TunicWorld") -> Tuple[Dict[Portal, Portal], Dict[i
portal_pairs: Dict[Portal, Portal] = pair_portals(world)
logic_rules = world.options.logic_rules
# output the entrances to the spoiler log here for convenience
for portal1, portal2 in portal_pairs.items():
world.multiworld.spoiler.set_entrance(portal1.name, portal2.name, "both", world.player)
# check if a portal leads to a hallway. if it does, update the hint text accordingly
def hint_helper(portal: Portal, hint_string: str = "") -> str:
# start by setting it as the name of the portal, for the case we're not using the hallway helper
if hint_string == "":
hint_string = portal.name
# unrestricted has fewer hallways, like the well rail
if logic_rules == "unrestricted":
hallways = hallway_helper_ur
else:
@@ -69,6 +75,7 @@ def create_er_regions(world: "TunicWorld") -> Tuple[Dict[Portal, Portal], Dict[i
return hint_string
# create our regions, give them hint text if they're in a spot where it makes sense to
# we're limiting which ones get hints so that it still gets that ER feel with a little less BS
for region_name, region_data in tunic_er_regions.items():
hint_text = "error"
if region_data.hint == 1:
@@ -90,7 +97,7 @@ def create_er_regions(world: "TunicWorld") -> Tuple[Dict[Portal, Portal], Dict[i
break
regions[region_name] = Region(region_name, world.player, world.multiworld, hint_text)
elif region_data.hint == 3:
# only the west garden portal item for now
# west garden portal item is at a dead end in restricted, otherwise just in west garden
if region_name == "West Garden Portal Item":
if world.options.logic_rules:
for portal1, portal2 in portal_pairs.items():
@@ -178,9 +185,17 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
portal_pairs: Dict[Portal, Portal] = {}
dead_ends: List[Portal] = []
two_plus: List[Portal] = []
plando_connections: List[PlandoConnection] = []
fixed_shop = False
logic_rules = world.options.logic_rules.value
if not logic_rules:
dependent_regions = dependent_regions_restricted
elif logic_rules == 1:
dependent_regions = dependent_regions_nmg
else:
dependent_regions = dependent_regions_ur
# create separate lists for dead ends and non-dead ends
if logic_rules:
for portal in portal_mapping:
@@ -200,8 +215,46 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
start_region = "Overworld"
connected_regions.update(add_dependent_regions(start_region, logic_rules))
# universal tracker support stuff, don't need to care about region dependency
if hasattr(world.multiworld, "re_gen_passthrough"):
if "TUNIC" in world.multiworld.re_gen_passthrough:
# universal tracker stuff, won't do anything in normal gen
for portal1, portal2 in world.multiworld.re_gen_passthrough["TUNIC"]["Entrance Rando"].items():
portal_name1 = ""
portal_name2 = ""
# skip this if 10 fairies laurels location is on, it can be handled normally
if portal1 == "Overworld Redux, Waterfall_" and portal2 == "Waterfall, Overworld Redux_" \
and world.options.laurels_location == "10_fairies":
continue
for portal in portal_mapping:
if portal.scene_destination() == portal1:
portal_name1 = portal.name
# connected_regions.update(add_dependent_regions(portal.region, logic_rules))
if portal.scene_destination() == portal2:
portal_name2 = portal.name
# connected_regions.update(add_dependent_regions(portal.region, logic_rules))
# shops have special handling
if not portal_name2 and portal2 == "Shop, Previous Region_":
portal_name2 = "Shop Portal"
plando_connections.append(PlandoConnection(portal_name1, portal_name2, "both"))
if plando_connections:
portal_pairs, dependent_regions, dead_ends, two_plus = \
create_plando_connections(plando_connections, dependent_regions, dead_ends, two_plus)
# if we have plando connections, our connected regions may change somewhat
while True:
test1 = len(connected_regions)
for region in connected_regions.copy():
connected_regions.update(add_dependent_regions(region, logic_rules))
test2 = len(connected_regions)
if test1 == test2:
break
# need to plando fairy cave, or it could end up laurels locked
# fix this later to be random? probably not?
# fix this later to be random after adding some item logic to dependent regions
if world.options.laurels_location == "10_fairies":
portal1 = None
portal2 = None
@@ -217,7 +270,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
two_plus.remove(portal1)
dead_ends.remove(portal2)
if world.options.fixed_shop:
if world.options.fixed_shop and not hasattr(world.multiworld, "re_gen_passthrough"):
fixed_shop = True
portal1 = None
for portal in two_plus:
@@ -283,6 +336,11 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
shop_count = 1
shop_scenes.add("Overworld Redux")
# for universal tracker, we want to skip shop gen
if hasattr(world.multiworld, "re_gen_passthrough"):
if "TUNIC" in world.multiworld.re_gen_passthrough:
shop_count = 0
for i in range(shop_count):
portal1 = None
for portal in two_plus:
@@ -311,10 +369,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
portal_pairs[portal1] = portal2
if len(two_plus) == 1:
raise Exception("two plus had an odd number of portals, investigate this")
for portal1, portal2 in portal_pairs.items():
world.multiworld.spoiler.set_entrance(portal1.name, portal2.name, "both", world.player)
raise Exception("two plus had an odd number of portals, investigate this. last portal is " + two_plus[0].name)
return portal_pairs
@@ -331,10 +386,11 @@ def create_randomized_entrances(portal_pairs: Dict[Portal, Portal], regions: Dic
# loop through the static connections, return regions you can reach from this region
# todo: refactor to take region_name and dependent_regions
def add_dependent_regions(region_name: str, logic_rules: int) -> Set[str]:
region_set = set()
if not logic_rules:
regions_to_add = dependent_regions
regions_to_add = dependent_regions_restricted
elif logic_rules == 1:
regions_to_add = dependent_regions_nmg
else:
@@ -451,3 +507,65 @@ def gate_before_switch(check_portal: Portal, two_plus: List[Portal]) -> bool:
# false means you're good to place the portal
return False
# this is for making the connections themselves
def create_plando_connections(plando_connections: List[PlandoConnection],
dependent_regions: Dict[Tuple[str, ...], List[str]], dead_ends: List[Portal],
two_plus: List[Portal]) \
-> Tuple[Dict[Portal, Portal], Dict[Tuple[str, ...], List[str]], List[Portal], List[Portal]]:
portal_pairs: Dict[Portal, Portal] = {}
shop_num = 1
for connection in plando_connections:
p_entrance = connection.entrance
p_exit = connection.exit
portal1 = None
portal2 = None
# search two_plus for both at once
for portal in two_plus:
if p_entrance == portal.name:
portal1 = portal
if p_exit == portal.name:
portal2 = portal
# search dead_ends individually since we can't really remove items from two_plus during the loop
if not portal1:
for portal in dead_ends:
if p_entrance == portal.name:
portal1 = portal
break
dead_ends.remove(portal1)
else:
two_plus.remove(portal1)
if not portal2:
for portal in dead_ends:
if p_exit == portal.name:
portal2 = portal
break
if p_exit == "Shop Portal":
portal2 = Portal(name="Shop Portal", region=f"Shop Entrance {shop_num}", destination="Previous Region_")
shop_num += 1
else:
dead_ends.remove(portal2)
else:
two_plus.remove(portal2)
if not portal1:
raise Exception("could not find entrance named " + p_entrance + " for Tunic player's plando")
if not portal2:
raise Exception("could not find entrance named " + p_exit + " for Tunic player's plando")
portal_pairs[portal1] = portal2
# update dependent regions based on the plando'd connections, to make sure the portals connect well, logically
for origins, destinations in dependent_regions.items():
if portal1.region in origins:
destinations.append(portal2.region)
if portal2.region in origins:
destinations.append(portal1.region)
return portal_pairs, dependent_regions, dead_ends, two_plus

View File

@@ -141,7 +141,7 @@ item_table: Dict[str, TunicItemData] = {
"Pages 46-47": TunicItemData(ItemClassification.useful, 1, 125, "pages"),
"Pages 48-49": TunicItemData(ItemClassification.useful, 1, 126, "pages"),
"Pages 50-51": TunicItemData(ItemClassification.useful, 1, 127, "pages"),
"Pages 52-53 (Ice Rod)": TunicItemData(ItemClassification.progression, 1, 128, "pages"),
"Pages 52-53 (Icebolt)": TunicItemData(ItemClassification.progression, 1, 128, "pages"),
"Pages 54-55": TunicItemData(ItemClassification.useful, 1, 129, "pages"),
}
@@ -176,7 +176,7 @@ slot_data_item_names = [
"Hero Relic - MP",
"Pages 24-25 (Prayer)",
"Pages 42-43 (Holy Cross)",
"Pages 52-53 (Ice Rod)",
"Pages 52-53 (Icebolt)",
"Red Questagon",
"Green Questagon",
"Blue Questagon",
@@ -204,10 +204,11 @@ extra_groups: Dict[str, Set[str]] = {
"magic rod": {"Magic Wand"},
"holy cross": {"Pages 42-43 (Holy Cross)"},
"prayer": {"Pages 24-25 (Prayer)"},
"ice rod": {"Pages 52-53 (Ice Rod)"},
"icebolt": {"Pages 52-53 (Icebolt)"},
"ice rod": {"Pages 52-53 (Icebolt)"},
"melee weapons": {"Stick", "Sword", "Sword Upgrade"},
"progressive sword": {"Sword Upgrade"},
"abilities": {"Pages 24-25 (Prayer)", "Pages 42-43 (Holy Cross)", "Pages 52-53 (Ice Rod)"},
"abilities": {"Pages 24-25 (Prayer)", "Pages 42-43 (Holy Cross)", "Pages 52-53 (Icebolt)"},
"questagons": {"Red Questagon", "Green Questagon", "Blue Questagon", "Gold Questagon"}
}

View File

@@ -23,7 +23,7 @@ class KeysBehindBosses(Toggle):
class AbilityShuffling(Toggle):
"""Locks the usage of Prayer, Holy Cross*, and Ice Rod until the relevant pages of the manual have been found.
"""Locks the usage of Prayer, Holy Cross*, and the Icebolt combo until the relevant pages of the manual have been found.
If playing Hexagon Quest, abilities are instead randomly unlocked after obtaining 25%, 50%, and 75% of the required
Hexagon goal amount.
*Certain Holy Cross usages are still allowed, such as the free bomb codes, the seeking spell, and other
@@ -36,8 +36,8 @@ class AbilityShuffling(Toggle):
class LogicRules(Choice):
"""Set which logic rules to use for your world.
Restricted: Standard logic, no glitches.
No Major Glitches: Ice grapples through doors, shooting the west bell, and boss quick kills are included in logic.
* Ice grappling through the Ziggurat door is not in logic since you will get stuck in there without Prayer
No Major Glitches: Sneaky Laurels zips, ice grapples through doors, shooting the west bell, and boss quick kills are included in logic.
* Ice grappling through the Ziggurat door is not in logic since you will get stuck in there without Prayer.
Unrestricted: Logic in No Major Glitches, as well as ladder storage to get to certain places early.
*Special Shop is not in logic without the Hero's Laurels due to soft lock potential.
*Using Ladder Storage to get to individual chests is not in logic to avoid tedium.
@@ -47,7 +47,9 @@ class LogicRules(Choice):
display_name = "Logic Rules"
option_restricted = 0
option_no_major_glitches = 1
alias_nmg = 1
option_unrestricted = 2
alias_ur = 2
default = 0

View File

@@ -16,7 +16,7 @@ fairies = "Fairy"
coins = "Golden Coin"
prayer = "Pages 24-25 (Prayer)"
holy_cross = "Pages 42-43 (Holy Cross)"
ice_rod = "Pages 52-53 (Ice Rod)"
icebolt = "Pages 52-53 (Icebolt)"
key = "Key"
house_key = "Old House Key"
vault_key = "Fortress Vault Key"
@@ -33,7 +33,7 @@ def randomize_ability_unlocks(random: Random, options: TunicOptions) -> Dict[str
hexagon_goal = options.hexagon_goal.value
# Set ability unlocks to 25, 50, and 75% of goal amount
ability_requirement = [hexagon_goal // 4, hexagon_goal // 2, hexagon_goal * 3 // 4]
abilities = [prayer, holy_cross, ice_rod]
abilities = [prayer, holy_cross, icebolt]
random.shuffle(abilities)
return dict(zip(abilities, ability_requirement))
@@ -65,7 +65,7 @@ def has_ice_grapple_logic(long_range: bool, state: CollectionState, player: int,
return state.has_all({ice_dagger, grapple}, player)
else:
return state.has_all({ice_dagger, fire_wand, grapple}, player) and \
has_ability(state, player, ice_rod, options, ability_unlocks)
has_ability(state, player, icebolt, options, ability_unlocks)
def can_ladder_storage(state: CollectionState, player: int, options: TunicOptions) -> bool:
@@ -251,7 +251,7 @@ def set_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) ->
lambda state: state.has_all({grapple, laurels}, player))
set_rule(multiworld.get_location("East Forest - Ice Rod Grapple Chest", player),
lambda state: state.has_all({grapple, ice_dagger, fire_wand}, player)
and has_ability(state, player, ice_rod, options, ability_unlocks))
and has_ability(state, player, icebolt, options, ability_unlocks))
# West Garden
set_rule(multiworld.get_location("West Garden - [North] Across From Page Pickup", player),

View File

@@ -29,7 +29,7 @@ def data_path(file_name: str):
class UndertaleWeb(WebWorld):
tutorials = [Tutorial(
"Multiworld Setup Tutorial",
"Multiworld Setup Guide",
"A guide to setting up the Archipelago Undertale software on your computer. This guide covers "
"single-player, multiworld, and related software.",
"English",