Compare commits

..

14 Commits
main ... main

Author SHA1 Message Date
Silvris
3ecd856e29 MultiServer: fix Windows compatibility (#6010) 2026-03-06 01:41:48 +01:00
Silvris
b372b02273 OptionCreator: 0.6.6 reported issues (#5949) 2026-03-04 19:47:30 +01:00
black-sliver
f26313367e MultiServer: graceful shutdown for ctrl+c and sigterm (#5996) 2026-03-04 00:02:12 +01:00
Fabian Dill
a3e8f69909 Core: introduce finalize_multiworld and pre_output stages (#5700)
Co-authored-by: Ishigh1 <bonjour940@yahoo.fr>
Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com>
Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
2026-03-01 17:53:41 +01:00
Fabian Dill
922c7fe86a Core: allow async def functions as commands (#5859) 2026-03-01 17:51:56 +01:00
Duck
e49ba2ff6f Undertale: Use check_locations helper to avoid redundant sends (#5993) 2026-03-01 01:30:26 +01:00
Doug Hoskisson
61d5120f66 Core: use typing_extensions deprecated (#5989) 2026-03-01 00:14:33 +01:00
Chris W
ff5402c410 Fix(undertale): prevent massive bounce msg spam for position updates (#5990)
* fix(undertale): prevent massive bounce msg spam for position updates

* make sure player is removed on leaving / timing out

* do not check for tags: online, as bounce evaluation is or'd
2026-02-28 22:56:28 +00:00
black-sliver
fcccbfca65 MultiServer: don't keep multidata alive for race_mode (#5980) 2026-02-26 18:31:39 +00:00
black-sliver
2db5435474 CI: upgrade InnoSetup to 6.7.0 (#5979) 2026-02-26 10:34:23 +01:00
Aaron Wagener
eeb022fa0c The Messenger: minor maintenance (#5965) 2026-02-26 02:24:50 +01:00
Duck
b30b2ecb07 Return new state man (Vi's note: I have chosen not to change this title) (#5978) 2026-02-25 20:52:34 +01:00
DrAwesome4333
699ca8adf6 WebHost: Add CORS headers to API Endpoints (#5777) 2026-02-25 02:47:54 +01:00
Silvris
fefd790de6 ALTTP: remove world: MultiWorld and typing (#5974) 2026-02-24 18:43:42 +01:00
29 changed files with 1978 additions and 1798 deletions

View File

@@ -51,7 +51,7 @@ jobs:
run: |
Invoke-WebRequest -Uri https://github.com/Ijwu/Enemizer/releases/download/${Env:ENEMIZER_VERSION}/win-x64.zip -OutFile enemizer.zip
Expand-Archive -Path enemizer.zip -DestinationPath EnemizerCLI -Force
choco install innosetup --version=6.2.2 --allow-downgrade
choco install innosetup --version=6.7.0 --allow-downgrade
- name: Build
run: |
python -m pip install --upgrade pip

View File

@@ -207,6 +207,9 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
else:
logger.info("Progression balancing skipped.")
AutoWorld.call_all(multiworld, "finalize_multiworld")
AutoWorld.call_all(multiworld, "pre_output")
# we're about to output using multithreading, so we're removing the global random state to prevent accidental use
multiworld.random.passthrough = False

View File

@@ -21,6 +21,7 @@ import time
import typing
import weakref
import zlib
from signal import SIGINT, SIGTERM, signal
import ModuleUpdate
@@ -496,7 +497,8 @@ class Context:
self.read_data = {}
# there might be a better place to put this.
self.read_data["race_mode"] = lambda: decoded_obj.get("race_mode", 0)
race_mode = decoded_obj.get("race_mode", 0)
self.read_data["race_mode"] = lambda: race_mode
mdata_ver = decoded_obj["minimum_versions"]["server"]
if mdata_ver > version_tuple:
raise RuntimeError(f"Supplied Multidata (.archipelago) requires a server of at least version {mdata_ver}, "
@@ -1301,6 +1303,13 @@ class CommandMeta(type):
commands.update(base.commands)
commands.update({command_name[5:]: method for command_name, method in attrs.items() if
command_name.startswith("_cmd_")})
for command_name, method in commands.items():
# wrap async def functions so they run on default asyncio loop
if inspect.iscoroutinefunction(method):
def _wrapper(self, *args, _method=method, **kwargs):
return async_start(_method(self, *args, **kwargs))
functools.update_wrapper(_wrapper, method)
commands[command_name] = _wrapper
return super(CommandMeta, cls).__new__(cls, name, bases, attrs)
@@ -2563,6 +2572,8 @@ async def console(ctx: Context):
input_text = await queue.get()
queue.task_done()
ctx.commandprocessor(input_text)
except asyncio.exceptions.CancelledError:
ctx.logger.info("ConsoleTask cancelled")
except:
import traceback
traceback.print_exc()
@@ -2729,6 +2740,26 @@ async def main(args: argparse.Namespace):
console_task = asyncio.create_task(console(ctx))
if ctx.auto_shutdown:
ctx.shutdown_task = asyncio.create_task(auto_shutdown(ctx, [console_task]))
def stop():
try:
for remove_signal in [SIGINT, SIGTERM]:
asyncio.get_event_loop().remove_signal_handler(remove_signal)
except NotImplementedError:
pass
ctx.commandprocessor._cmd_exit()
def shutdown(signum, frame):
stop()
try:
for sig in [SIGINT, SIGTERM]:
asyncio.get_event_loop().add_signal_handler(sig, stop)
except NotImplementedError:
# add_signal_handler is only implemented for UNIX platforms
for sig in [SIGINT, SIGTERM]:
signal(sig, shutdown)
await ctx.exit_event.wait()
console_task.cancel()
if ctx.shutdown_task:

View File

@@ -29,7 +29,7 @@ import webbrowser
import re
from urllib.parse import urlparse
from worlds.AutoWorld import AutoWorldRegister, World
from Options import (Option, Toggle, TextChoice, Choice, FreeText, NamedRange, Range, OptionSet, OptionList, Removed,
from Options import (Option, Toggle, TextChoice, Choice, FreeText, NamedRange, Range, OptionSet, OptionList,
OptionCounter, Visibility)
@@ -318,26 +318,28 @@ class OptionsCreator(ThemedApp):
else:
self.show_result_snack("Name cannot be longer than 16 characters.")
def create_range(self, option: typing.Type[Range], name: str):
def create_range(self, option: typing.Type[Range], name: str, bind=True):
def update_text(range_box: VisualRange):
self.options[name] = int(range_box.slider.value)
range_box.tag.text = str(int(range_box.slider.value))
return
box = VisualRange(option=option, name=name)
box.slider.bind(on_touch_move=lambda _, _1: update_text(box))
if bind:
box.slider.bind(value=lambda _, _1: update_text(box))
self.options[name] = option.default
return box
def create_named_range(self, option: typing.Type[NamedRange], name: str):
def set_to_custom(range_box: VisualNamedRange):
if (not self.options[name] == range_box.range.slider.value) \
and (not self.options[name] in option.special_range_names or
range_box.range.slider.value != option.special_range_names[self.options[name]]):
# we should validate the touch here,
# but this is much cheaper
range_box.range.tag.text = str(int(range_box.range.slider.value))
if range_box.range.slider.value in option.special_range_names.values():
value = next(key for key, val in option.special_range_names.items()
if val == range_box.range.slider.value)
self.options[name] = value
set_button_text(box.choice, value.title())
else:
self.options[name] = int(range_box.range.slider.value)
range_box.range.tag.text = str(int(range_box.range.slider.value))
set_button_text(range_box.choice, "Custom")
def set_button_text(button: MDButton, text: str):
@@ -346,7 +348,7 @@ class OptionsCreator(ThemedApp):
def set_value(text: str, range_box: VisualNamedRange):
range_box.range.slider.value = min(max(option.special_range_names[text.lower()], option.range_start),
option.range_end)
range_box.range.tag.text = str(int(range_box.range.slider.value))
range_box.range.tag.text = str(option.special_range_names[text.lower()])
set_button_text(range_box.choice, text)
self.options[name] = text.lower()
range_box.range.slider.dropdown.dismiss()
@@ -355,13 +357,18 @@ class OptionsCreator(ThemedApp):
# for some reason this fixes an issue causing some to not open
box.range.slider.dropdown.open()
box = VisualNamedRange(option=option, name=name, range_widget=self.create_range(option, name))
if option.default in option.special_range_names:
box = VisualNamedRange(option=option, name=name, range_widget=self.create_range(option, name, bind=False))
default: int | str = option.default
if default in option.special_range_names:
# value can get mismatched in this case
box.range.slider.value = min(max(option.special_range_names[option.default], option.range_start),
box.range.slider.value = min(max(option.special_range_names[default], option.range_start),
option.range_end)
box.range.tag.text = str(int(box.range.slider.value))
box.range.slider.bind(on_touch_move=lambda _, _2: set_to_custom(box))
elif default in option.special_range_names.values():
# better visual
default = next(key for key, val in option.special_range_names.items() if val == option.default)
set_button_text(box.choice, default.title())
box.range.slider.bind(value=lambda _, _2: set_to_custom(box))
items = [
{
"text": choice.title(),
@@ -371,7 +378,7 @@ class OptionsCreator(ThemedApp):
]
box.range.slider.dropdown = MDDropdownMenu(caller=box.choice, items=items)
box.choice.bind(on_release=open_dropdown)
self.options[name] = option.default
self.options[name] = default
return box
def create_free_text(self, option: typing.Type[FreeText] | typing.Type[TextChoice], name: str):
@@ -447,8 +454,12 @@ class OptionsCreator(ThemedApp):
valid_keys = sorted(option.valid_keys)
if option.verify_item_name:
valid_keys += list(world.item_name_to_id.keys())
if option.convert_name_groups:
valid_keys += list(world.item_name_groups.keys())
if option.verify_location_name:
valid_keys += list(world.location_name_to_id.keys())
if option.convert_name_groups:
valid_keys += list(world.location_name_groups.keys())
if not issubclass(option, OptionCounter):
def apply_changes(button):
@@ -470,14 +481,6 @@ class OptionsCreator(ThemedApp):
dialog.scrollbox.layout.spacing = dp(5)
dialog.scrollbox.layout.padding = [0, dp(5), 0, 0]
if name not in self.options:
# convert from non-mutable to mutable
# We use list syntax even for sets, set behavior is enforced through GUI
if issubclass(option, OptionCounter):
self.options[name] = deepcopy(option.default)
else:
self.options[name] = sorted(option.default)
if issubclass(option, OptionCounter):
for value in sorted(self.options[name]):
dialog.add_set_item(value, self.options[name].get(value, None))
@@ -491,6 +494,15 @@ class OptionsCreator(ThemedApp):
def create_option_set_list_counter(self, option: typing.Type[OptionList] | typing.Type[OptionSet] |
typing.Type[OptionCounter], name: str, world: typing.Type[World]):
main_button = MDButton(MDButtonText(text="Edit"), on_release=lambda x: self.create_popup(option, name, world))
if name not in self.options:
# convert from non-mutable to mutable
# We use list syntax even for sets, set behavior is enforced through GUI
if issubclass(option, OptionCounter):
self.options[name] = deepcopy(option.default)
else:
self.options[name] = sorted(option.default)
return main_button
def create_option(self, option: typing.Type[Option], name: str, world: typing.Type[World]) -> Widget:

View File

@@ -1,6 +1,7 @@
from __future__ import annotations
import os
import sys
import time
import asyncio
import typing
import bsdiff4
@@ -15,6 +16,9 @@ from CommonClient import CommonContext, server_loop, \
gui_enabled, ClientCommandProcessor, logger, get_base_parser
from Utils import async_start
# Heartbeat for position sharing via bounces, in seconds
UNDERTALE_STATUS_INTERVAL = 30.0
UNDERTALE_ONLINE_TIMEOUT = 60.0
class UndertaleCommandProcessor(ClientCommandProcessor):
def __init__(self, ctx):
@@ -109,6 +113,11 @@ class UndertaleContext(CommonContext):
self.completed_routes = {"pacifist": 0, "genocide": 0, "neutral": 0}
# self.save_game_folder: files go in this path to pass data between us and the actual game
self.save_game_folder = os.path.expandvars(r"%localappdata%/UNDERTALE")
self.last_sent_position: typing.Optional[tuple] = None
self.last_room: typing.Optional[str] = None
self.last_status_write: float = 0.0
self.other_undertale_status: dict[int, dict] = {}
def patch_game(self):
with open(Utils.user_path("Undertale", "data.win"), "rb") as f:
@@ -219,6 +228,9 @@ async def process_undertale_cmd(ctx: UndertaleContext, cmd: str, args: dict):
await ctx.send_msgs([{"cmd": "SetNotify", "keys": [str(ctx.slot)+" RoutesDone neutral",
str(ctx.slot)+" RoutesDone pacifist",
str(ctx.slot)+" RoutesDone genocide"]}])
if any(info.game == "Undertale" and slot != ctx.slot
for slot, info in ctx.slot_info.items()):
ctx.set_notify("undertale_room_status")
if args["slot_data"]["only_flakes"]:
with open(os.path.join(ctx.save_game_folder, "GenoNoChest.flag"), "w") as f:
f.close()
@@ -263,6 +275,12 @@ async def process_undertale_cmd(ctx: UndertaleContext, cmd: str, args: dict):
if str(ctx.slot)+" RoutesDone pacifist" in args["keys"]:
if args["keys"][str(ctx.slot) + " RoutesDone pacifist"] is not None:
ctx.completed_routes["pacifist"] = args["keys"][str(ctx.slot)+" RoutesDone pacifist"]
if "undertale_room_status" in args["keys"] and args["keys"]["undertale_room_status"]:
status = args["keys"]["undertale_room_status"]
ctx.other_undertale_status = {
int(key): val for key, val in status.items()
if int(key) != ctx.slot
}
elif cmd == "SetReply":
if args["value"] is not None:
if str(ctx.slot)+" RoutesDone pacifist" == args["key"]:
@@ -271,17 +289,19 @@ async def process_undertale_cmd(ctx: UndertaleContext, cmd: str, args: dict):
ctx.completed_routes["genocide"] = args["value"]
elif str(ctx.slot)+" RoutesDone neutral" == args["key"]:
ctx.completed_routes["neutral"] = args["value"]
if args.get("key") == "undertale_room_status" and args.get("value"):
ctx.other_undertale_status = {
int(key): val for key, val in args["value"].items()
if int(key) != ctx.slot
}
elif cmd == "ReceivedItems":
start_index = args["index"]
if start_index == 0:
ctx.items_received = []
elif start_index != len(ctx.items_received):
sync_msg = [{"cmd": "Sync"}]
if ctx.locations_checked:
sync_msg.append({"cmd": "LocationChecks",
"locations": list(ctx.locations_checked)})
await ctx.send_msgs(sync_msg)
await ctx.check_locations(ctx.locations_checked)
await ctx.send_msgs([{"cmd": "Sync"}])
if start_index == len(ctx.items_received):
counter = -1
placedWeapon = 0
@@ -368,9 +388,8 @@ async def process_undertale_cmd(ctx: UndertaleContext, cmd: str, args: dict):
f.close()
elif cmd == "Bounced":
tags = args.get("tags", [])
if "Online" in tags:
data = args.get("data", {})
data = args.get("data", {})
if "x" in data and "room" in data:
if data["player"] != ctx.slot and data["player"] is not None:
filename = f"FRISK" + str(data["player"]) + ".playerspot"
with open(os.path.join(ctx.save_game_folder, filename), "w") as f:
@@ -381,21 +400,63 @@ async def process_undertale_cmd(ctx: UndertaleContext, cmd: str, args: dict):
async def multi_watcher(ctx: UndertaleContext):
while not ctx.exit_event.is_set():
path = ctx.save_game_folder
for root, dirs, files in os.walk(path):
for file in files:
if "spots.mine" in file and "Online" in ctx.tags:
with open(os.path.join(root, file), "r") as mine:
this_x = mine.readline()
this_y = mine.readline()
this_room = mine.readline()
this_sprite = mine.readline()
this_frame = mine.readline()
mine.close()
message = [{"cmd": "Bounce", "tags": ["Online"],
"data": {"player": ctx.slot, "x": this_x, "y": this_y, "room": this_room,
"spr": this_sprite, "frm": this_frame}}]
await ctx.send_msgs(message)
if "Online" in ctx.tags and any(
info.game == "Undertale" and slot != ctx.slot
for slot, info in ctx.slot_info.items()):
now = time.time()
path = ctx.save_game_folder
for root, dirs, files in os.walk(path):
for file in files:
if "spots.mine" in file:
with open(os.path.join(root, file), "r") as mine:
this_x = mine.readline()
this_y = mine.readline()
this_room = mine.readline()
this_sprite = mine.readline()
this_frame = mine.readline()
if this_room != ctx.last_room or \
now - ctx.last_status_write >= UNDERTALE_STATUS_INTERVAL:
ctx.last_room = this_room
ctx.last_status_write = now
await ctx.send_msgs([{
"cmd": "Set",
"key": "undertale_room_status",
"default": {},
"want_reply": False,
"operations": [{"operation": "update",
"value": {str(ctx.slot): {"room": this_room,
"time": now}}}]
}])
# If player was visible but timed out (heartbeat) or left the room, remove them.
for slot, entry in ctx.other_undertale_status.items():
if entry.get("room") != this_room or \
now - entry.get("time", now) > UNDERTALE_ONLINE_TIMEOUT:
playerspot = os.path.join(ctx.save_game_folder,
f"FRISK{slot}.playerspot")
if os.path.exists(playerspot):
os.remove(playerspot)
current_position = (this_x, this_y, this_room, this_sprite, this_frame)
if current_position == ctx.last_sent_position:
continue
# Empty status dict = no data yet → send to bootstrap.
online_in_room = any(
entry.get("room") == this_room and
now - entry.get("time", now) <= UNDERTALE_ONLINE_TIMEOUT
for entry in ctx.other_undertale_status.values()
)
if ctx.other_undertale_status and not online_in_room:
continue
message = [{"cmd": "Bounce", "games": ["Undertale"],
"data": {"player": ctx.slot, "x": this_x, "y": this_y,
"room": this_room, "spr": this_sprite,
"frm": this_frame}}]
await ctx.send_msgs(message)
ctx.last_sent_position = current_position
await asyncio.sleep(0.1)
@@ -409,10 +470,9 @@ async def game_watcher(ctx: UndertaleContext):
for file in files:
if ".item" in file:
os.remove(os.path.join(root, file))
sync_msg = [{"cmd": "Sync"}]
if ctx.locations_checked:
sync_msg.append({"cmd": "LocationChecks", "locations": list(ctx.locations_checked)})
await ctx.send_msgs(sync_msg)
await ctx.check_locations(ctx.locations_checked)
await ctx.send_msgs([{"cmd": "Sync"}])
ctx.syncing = False
if ctx.got_deathlink:
ctx.got_deathlink = False
@@ -447,7 +507,7 @@ async def game_watcher(ctx: UndertaleContext):
for l in lines:
sending = sending+[(int(l.rstrip('\n')))+12000]
finally:
await ctx.send_msgs([{"cmd": "LocationChecks", "locations": sending}])
await ctx.check_locations(sending)
if "victory" in file and str(ctx.route) in file:
victory = True
if ".playerspot" in file and "Online" not in ctx.tags:

View File

@@ -23,6 +23,7 @@ from time import sleep
from typing import BinaryIO, Coroutine, Optional, Set, Dict, Any, Union, TypeGuard
from yaml import load, load_all, dump
from pathspec import PathSpec, GitIgnoreSpec
from typing_extensions import deprecated
try:
from yaml import CLoader as UnsafeLoader, CSafeLoader as SafeLoader, CDumper as Dumper
@@ -315,6 +316,7 @@ def get_public_ipv6() -> str:
return ip
@deprecated("Utils.get_options() is deprecated. Use the settings API instead.")
def get_options() -> Settings:
deprecate("Utils.get_options() is deprecated. Use the settings API instead.")
return get_settings()
@@ -1003,6 +1005,7 @@ def async_start(co: Coroutine[None, None, typing.Any], name: Optional[str] = Non
def deprecate(message: str, add_stacklevels: int = 0):
"""also use typing_extensions.deprecated wherever you use this"""
if __debug__:
raise Exception(message)
warnings.warn(message, stacklevel=2 + add_stacklevels)
@@ -1067,6 +1070,7 @@ def _extend_freeze_support() -> None:
multiprocessing.freeze_support = multiprocessing.spawn.freeze_support = _freeze_support if is_frozen() else _noop
@deprecated("Use multiprocessing.freeze_support() instead")
def freeze_support() -> None:
"""This now only calls multiprocessing.freeze_support since we are patching freeze_support on module load."""
import multiprocessing

View File

@@ -2,10 +2,20 @@
from typing import List, Tuple
from flask import Blueprint
from flask_cors import CORS
from ..models import Seed, Slot
api_endpoints = Blueprint('api', __name__, url_prefix="/api")
cors = CORS(api_endpoints, resources={
r"/api/datapackage/*": {"origins": "*"},
r"/api/datapackage": {"origins": "*"},
r"/api/datapackage_checksum/*": {"origins": "*"},
r"/api/room_status/*": {"origins": "*"},
r"/api/tracker/*": {"origins": "*"},
r"/api/static_tracker/*": {"origins": "*"},
r"/api/slot_data_tracker/*": {"origins": "*"}
})
def get_players(seed: Seed) -> List[Tuple[str, str]]:

View File

@@ -6,6 +6,7 @@ waitress>=3.0.2
Flask-Caching>=2.3.0
Flask-Compress==1.18 # pkg_resources can't resolve the "backports.zstd" dependency of >1.18, breaking ModuleUpdate.py
Flask-Limiter>=3.12
Flask-Cors>=6.0.2
bokeh>=3.6.3
markupsafe>=3.0.2
setproctitle>=1.3.5

View File

@@ -770,6 +770,7 @@ class MyGameState(LogicMixin):
new_state.mygame_defeatable_enemies = {
player: enemies.copy() for player, enemies in self.mygame_defeatable_enemies.items()
}
return new_state
```
After doing this, you can now access `state.mygame_defeatable_enemies[player]` from your access rules.

View File

@@ -248,6 +248,7 @@ class WorldTestBase(unittest.TestCase):
with self.subTest("Game", game=self.game, seed=self.multiworld.seed):
distribute_items_restrictive(self.multiworld)
call_all(self.multiworld, "post_fill")
call_all(self.multiworld, "finalize_multiworld")
self.assertTrue(fulfills_accessibility(), "Collected all locations, but can't beat the game.")
placed_items = [loc.item for loc in self.multiworld.get_locations() if loc.item and loc.item.code]
self.assertLessEqual(len(self.multiworld.itempool), len(placed_items),

View File

@@ -88,6 +88,7 @@ class TestIDs(unittest.TestCase):
multiworld = setup_solo_multiworld(world_type)
distribute_items_restrictive(multiworld)
call_all(multiworld, "post_fill")
call_all(multiworld, "finalize_multiworld")
datapackage = world_type.get_data_package_data()
for item_group, item_names in datapackage["item_name_groups"].items():
self.assertIsInstance(item_group, str,

View File

@@ -46,6 +46,8 @@ class TestImplemented(unittest.TestCase):
with self.subTest(game=game_name, seed=multiworld.seed):
distribute_items_restrictive(multiworld)
call_all(multiworld, "post_fill")
call_all(multiworld, "finalize_multiworld")
call_all(multiworld, "pre_output")
for key, data in multiworld.worlds[1].fill_slot_data().items():
self.assertIsInstance(key, str, "keys in slot data must be a string")
convert_to_base_types(data) # only put base data types into slot data
@@ -93,6 +95,7 @@ class TestImplemented(unittest.TestCase):
with self.subTest(game=game_name, seed=multiworld.seed):
distribute_items_restrictive(multiworld)
call_all(multiworld, "post_fill")
call_all(multiworld, "finalize_multiworld")
# Note: `multiworld.get_spheres()` iterates a set of locations, so the order that locations are checked
# is nondeterministic and may vary between runs with the same seed.

View File

@@ -123,6 +123,7 @@ class TestBase(unittest.TestCase):
call_all(multiworld, "pre_fill")
distribute_items_restrictive(multiworld)
call_all(multiworld, "post_fill")
call_all(multiworld, "finalize_multiworld")
self.assertTrue(multiworld.can_beat_game(CollectionState(multiworld)), f"seed = {multiworld.seed}")
for game_name, world_type in AutoWorldRegister.world_types.items():

View File

@@ -61,6 +61,7 @@ class TestAllGamesMultiworld(MultiworldTestBase):
with self.subTest("filling multiworld", seed=self.multiworld.seed):
distribute_items_restrictive(self.multiworld)
call_all(self.multiworld, "post_fill")
call_all(self.multiworld, "finalize_multiworld")
self.assertTrue(self.fulfills_accessibility(), "Collected all locations, but can't beat the game")
@@ -78,4 +79,5 @@ class TestTwoPlayerMulti(MultiworldTestBase):
with self.subTest("filling multiworld", games=world_type.game, seed=self.multiworld.seed):
distribute_items_restrictive(self.multiworld)
call_all(self.multiworld, "post_fill")
call_all(self.multiworld, "finalize_multiworld")
self.assertTrue(self.fulfills_accessibility(), "Collected all locations, but can't beat the game")

View File

@@ -430,6 +430,23 @@ class World(metaclass=AutoWorldRegister):
This happens before progression balancing, so the items may not be in their final locations yet.
"""
def finalize_multiworld(self) -> None:
"""
Optional Method that is called after fill and progression balancing.
This is the last stage of generation where worlds may change logically relevant data,
such as item placements and connections. To not break assumptions,
only ever increase accessibility, never decrease it.
"""
pass
def pre_output(self):
"""
Optional method that is called before output generation.
Items and connections are not meant to be moved anymore,
anything that would affect logical spheres is forbidden at this point.
"""
pass
def generate_output(self, output_directory: str) -> None:
"""
This method gets called from a threadpool, do not use multiworld.random here.

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,16 @@
import collections
from BaseClasses import MultiWorld
from .Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
from .SubClasses import LTTPRegionType
def create_inverted_regions(world, player):
def create_inverted_regions(multiworld: MultiWorld, player: int):
world.regions += [
create_dw_region(world, player, 'Menu', None,
multiworld.regions += [
create_dw_region(multiworld, player, 'Menu', None,
['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
create_lw_region(world, player, 'Light World',
create_lw_region(multiworld, player, 'Light World',
['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest',
'Bombos Tablet'],
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam',
@@ -35,184 +36,184 @@ def create_inverted_regions(world, player):
'Hyrule Castle Entrance (South)', 'Secret Passage Outer Bushes',
'Bush Covered Lawn Outer Bushes',
'Potion Shop Outer Bushes', 'Graveyard Cave Outer Bushes', 'Bomb Hut Outer Bushes']),
create_lw_region(world, player, 'Bush Covered Lawn', None,
create_lw_region(multiworld, player, 'Bush Covered Lawn', None,
['Bush Covered House', 'Bush Covered Lawn Inner Bushes', 'Bush Covered Lawn Mirror Spot']),
create_lw_region(world, player, 'Bomb Hut Area', None,
create_lw_region(multiworld, player, 'Bomb Hut Area', None,
['Light World Bomb Hut', 'Bomb Hut Inner Bushes', 'Bomb Hut Mirror Spot']),
create_lw_region(world, player, 'Hyrule Castle Secret Entrance Area', None,
create_lw_region(multiworld, player, 'Hyrule Castle Secret Entrance Area', None,
['Hyrule Castle Secret Entrance Stairs', 'Secret Passage Inner Bushes']),
create_lw_region(world, player, 'Death Mountain Entrance', None,
create_lw_region(multiworld, player, 'Death Mountain Entrance', None,
['Old Man Cave (West)', 'Death Mountain Entrance Drop', 'Bumper Cave Entrance Mirror Spot']),
create_lw_region(world, player, 'Lake Hylia Central Island', None,
create_lw_region(multiworld, player, 'Lake Hylia Central Island', None,
['Capacity Upgrade', 'Lake Hylia Central Island Mirror Spot']),
create_cave_region(world, player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
create_cave_region(multiworld, player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
"Blind\'s Hideout - Left",
"Blind\'s Hideout - Right",
"Blind\'s Hideout - Far Left",
"Blind\'s Hideout - Far Right"]),
create_lw_region(world, player, 'Northeast Light World', None,
create_lw_region(multiworld, player, 'Northeast Light World', None,
['Zoras River', 'Waterfall of Wishing Cave', 'Potion Shop Outer Rock', 'Catfish Mirror Spot',
'Northeast Light World Warp']),
create_lw_region(world, player, 'Waterfall of Wishing Cave', None,
create_lw_region(multiworld, player, 'Waterfall of Wishing Cave', None,
['Waterfall of Wishing', 'Northeast Light World Return']),
create_lw_region(world, player, 'Potion Shop Area', None,
create_lw_region(multiworld, player, 'Potion Shop Area', None,
['Potion Shop', 'Potion Shop Inner Bushes', 'Potion Shop Inner Rock',
'Potion Shop Mirror Spot', 'Potion Shop River Drop']),
create_lw_region(world, player, 'Graveyard Cave Area', None,
create_lw_region(multiworld, player, 'Graveyard Cave Area', None,
['Graveyard Cave', 'Graveyard Cave Inner Bushes', 'Graveyard Cave Mirror Spot']),
create_lw_region(world, player, 'River', None, ['Light World Pier', 'Potion Shop Pier']),
create_cave_region(world, player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit',
create_lw_region(multiworld, player, 'River', None, ['Light World Pier', 'Potion Shop Pier']),
create_cave_region(multiworld, player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit',
['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
create_lw_region(world, player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
create_cave_region(world, player, 'Waterfall of Wishing', 'a cave with two chests',
create_lw_region(multiworld, player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
create_cave_region(multiworld, player, 'Waterfall of Wishing', 'a cave with two chests',
['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
create_lw_region(world, player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
create_cave_region(world, player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
create_cave_region(world, player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
create_cave_region(world, player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
create_cave_region(world, player, 'Inverted Links House', 'your house', ['Link\'s House'],
create_lw_region(multiworld, player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
create_cave_region(multiworld, player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
create_cave_region(multiworld, player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
create_cave_region(multiworld, player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
create_cave_region(multiworld, player, 'Inverted Links House', 'your house', ['Link\'s House'],
['Inverted Links House Exit']),
create_cave_region(world, player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
create_cave_region(world, player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
create_cave_region(world, player, 'Elder House', 'a connector', None,
create_cave_region(multiworld, player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
create_cave_region(multiworld, player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
create_cave_region(multiworld, player, 'Elder House', 'a connector', None,
['Elder House Exit (East)', 'Elder House Exit (West)']),
create_cave_region(world, player, 'Snitch Lady (East)', 'a boring house'),
create_cave_region(world, player, 'Snitch Lady (West)', 'a boring house'),
create_cave_region(world, player, 'Bush Covered House', 'the grass man'),
create_cave_region(world, player, 'Tavern (Front)', 'the tavern'),
create_cave_region(world, player, 'Light World Bomb Hut', 'a restock room'),
create_cave_region(world, player, 'Kakariko Shop', 'a common shop'),
create_cave_region(world, player, 'Fortune Teller (Light)', 'a fortune teller'),
create_cave_region(world, player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
create_cave_region(world, player, 'Lumberjack House', 'a boring house'),
create_cave_region(world, player, 'Bonk Fairy (Light)', 'a fairy fountain'),
create_cave_region(world, player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
create_cave_region(world, player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Swamp Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Chicken House', 'a house with a chest', ['Chicken House']),
create_cave_region(world, player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
create_cave_region(world, player, 'Sahasrahlas Hut', 'Sahasrahla',
create_cave_region(multiworld, player, 'Snitch Lady (East)', 'a boring house'),
create_cave_region(multiworld, player, 'Snitch Lady (West)', 'a boring house'),
create_cave_region(multiworld, player, 'Bush Covered House', 'the grass man'),
create_cave_region(multiworld, player, 'Tavern (Front)', 'the tavern'),
create_cave_region(multiworld, player, 'Light World Bomb Hut', 'a restock room'),
create_cave_region(multiworld, player, 'Kakariko Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Fortune Teller (Light)', 'a fortune teller'),
create_cave_region(multiworld, player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
create_cave_region(multiworld, player, 'Lumberjack House', 'a boring house'),
create_cave_region(multiworld, player, 'Bonk Fairy (Light)', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Swamp Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Chicken House', 'a house with a chest', ['Chicken House']),
create_cave_region(multiworld, player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
create_cave_region(multiworld, player, 'Sahasrahlas Hut', 'Sahasrahla',
['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right',
'Sahasrahla']),
create_cave_region(world, player, 'Kakariko Well (top)', 'a drop\'s exit',
create_cave_region(multiworld, player, 'Kakariko Well (top)', 'a drop\'s exit',
['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
create_cave_region(world, player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
create_cave_region(world, player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
create_lw_region(world, player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
create_cave_region(world, player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
create_cave_region(world, player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
create_cave_region(world, player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
create_lw_region(world, player, 'Hobo Bridge', ['Hobo']),
create_cave_region(world, player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'],
create_cave_region(multiworld, player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
create_cave_region(multiworld, player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
create_lw_region(multiworld, player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
create_cave_region(multiworld, player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
create_cave_region(multiworld, player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
create_cave_region(multiworld, player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
create_lw_region(multiworld, player, 'Hobo Bridge', ['Hobo']),
create_cave_region(multiworld, player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'],
['Lost Woods Hideout (top to bottom)']),
create_cave_region(world, player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None,
create_cave_region(multiworld, player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None,
['Lost Woods Hideout Exit']),
create_cave_region(world, player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'],
create_cave_region(multiworld, player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'],
['Lumberjack Tree (top to bottom)']),
create_cave_region(world, player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
create_cave_region(world, player, 'Cave 45', 'a cave with an item', ['Cave 45']),
create_cave_region(world, player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
create_cave_region(world, player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
create_cave_region(world, player, 'Long Fairy Cave', 'a fairy fountain'),
create_cave_region(world, player, 'Mini Moldorm Cave', 'a bounty of five items',
create_cave_region(multiworld, player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
create_cave_region(multiworld, player, 'Cave 45', 'a cave with an item', ['Cave 45']),
create_cave_region(multiworld, player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
create_cave_region(multiworld, player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
create_cave_region(multiworld, player, 'Long Fairy Cave', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Mini Moldorm Cave', 'a bounty of five items',
['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
create_cave_region(world, player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
create_cave_region(world, player, 'Good Bee Cave', 'a cold bee'),
create_cave_region(world, player, '20 Rupee Cave', 'a cave with some cash'),
create_cave_region(world, player, 'Cave Shop (Lake Hylia)', 'a common shop'),
create_cave_region(world, player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
create_cave_region(world, player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
create_cave_region(world, player, 'Library', 'the library', ['Library']),
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', ['Capacity Upgrade Shop']),
create_cave_region(world, player, 'Two Brothers House', 'a connector', None,
create_cave_region(multiworld, player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
create_cave_region(multiworld, player, 'Good Bee Cave', 'a cold bee'),
create_cave_region(multiworld, player, '20 Rupee Cave', 'a cave with some cash'),
create_cave_region(multiworld, player, 'Cave Shop (Lake Hylia)', 'a common shop'),
create_cave_region(multiworld, player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
create_cave_region(multiworld, player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
create_cave_region(multiworld, player, 'Library', 'the library', ['Library']),
create_cave_region(multiworld, player, 'Kakariko Gamble Game', 'a game of chance'),
create_cave_region(multiworld, player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
create_lw_region(multiworld, player, 'Lake Hylia Island', ['Lake Hylia Island']),
create_cave_region(multiworld, player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade Shop']),
create_cave_region(multiworld, 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'],
create_lw_region(multiworld, player, 'Maze Race Ledge', ['Maze Race'],
['Two Brothers House (West)', 'Maze Race Mirror Spot']),
create_cave_region(world, player, '50 Rupee Cave', 'a cave with some cash'),
create_lw_region(world, player, 'Desert Ledge', ['Desert Ledge'],
create_cave_region(multiworld, player, '50 Rupee Cave', 'a cave with some cash'),
create_lw_region(multiworld, player, 'Desert Ledge', ['Desert Ledge'],
['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)',
'Desert Ledge Drop']),
create_lw_region(world, player, 'Desert Palace Stairs', None,
create_lw_region(multiworld, player, 'Desert Palace Stairs', None,
['Desert Palace Entrance (South)', 'Desert Palace Stairs Mirror Spot']),
create_lw_region(world, player, 'Desert Palace Lone Stairs', None,
create_lw_region(multiworld, player, 'Desert Palace Lone Stairs', None,
['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
create_lw_region(world, player, 'Desert Palace Entrance (North) Spot', None,
create_lw_region(multiworld, player, 'Desert Palace Entrance (North) Spot', None,
['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks',
'Desert Palace North Mirror Spot']),
create_dungeon_region(world, player, 'Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
create_dungeon_region(multiworld, player, 'Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']),
create_dungeon_region(world, player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
create_dungeon_region(world, player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
create_dungeon_region(world, player, 'Desert Palace North', 'Desert Palace',
create_dungeon_region(multiworld, player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
create_dungeon_region(multiworld, player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
create_dungeon_region(multiworld, player, 'Desert Palace North', 'Desert Palace',
['Desert Palace - Desert Tiles 1 Pot Key', 'Desert Palace - Beamos Hall Pot Key',
'Desert Palace - Desert Tiles 2 Pot Key',
'Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
create_dungeon_region(world, player, 'Eastern Palace', 'Eastern Palace',
create_dungeon_region(multiworld, player, 'Eastern Palace', 'Eastern Palace',
['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest',
'Eastern Palace - Cannonball Chest',
'Eastern Palace - Dark Square Pot Key', 'Eastern Palace - Dark Eyegore Key Drop',
'Eastern Palace - Big Key Chest',
'Eastern Palace - Map Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize'],
['Eastern Palace Exit']),
create_lw_region(world, player, 'Master Sword Meadow', ['Master Sword Pedestal']),
create_cave_region(world, player, 'Lost Woods Gamble', 'a game of chance'),
create_lw_region(world, player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower', 'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']),
create_dungeon_region(world, player, 'Hyrule Castle', 'Hyrule Castle',
create_lw_region(multiworld, player, 'Master Sword Meadow', ['Master Sword Pedestal']),
create_cave_region(multiworld, player, 'Lost Woods Gamble', 'a game of chance'),
create_lw_region(multiworld, player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower', 'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']),
create_dungeon_region(multiworld, player, 'Hyrule Castle', 'Hyrule Castle',
['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
'Hyrule Castle - Zelda\'s Chest',
'Hyrule Castle - Map Guard Key Drop', 'Hyrule Castle - Boomerang Guard Key Drop',
'Hyrule Castle - Big Key Drop'],
['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', 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',
create_dungeon_region(multiworld, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
create_dungeon_region(multiworld, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross', 'Sewers - Key Rat Key Drop'], ['Sewers Door']),
create_dungeon_region(multiworld, player, 'Sewers', 'a drop\'s exit', None, ['Sanctuary Push Door', 'Sewers Back Door', 'Sewers Secret Room']),
create_dungeon_region(multiworld, 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),
create_cave_region(world, player, 'Old Man Cave', 'a connector', ['Old Man'],
create_dungeon_region(multiworld, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
create_dungeon_region(multiworld, 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(multiworld, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
create_cave_region(multiworld, player, 'Old Man Cave', 'a connector', ['Old Man'],
['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
create_cave_region(world, player, 'Old Man House', 'a connector', None,
create_cave_region(multiworld, player, 'Old Man House', 'a connector', None,
['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
create_cave_region(world, player, 'Old Man House Back', 'a connector', None,
create_cave_region(multiworld, player, 'Old Man House Back', 'a connector', None,
['Old Man House Exit (Top)', 'Old Man House Back to Front']),
create_lw_region(world, player, 'Death Mountain', None,
create_lw_region(multiworld, player, 'Death Mountain', None,
['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)',
'Death Mountain Return Cave (East)', 'Spectacle Rock Cave',
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)',
'Death Mountain Mirror Spot']),
create_cave_region(world, player, 'Death Mountain Return Cave', 'a connector', None,
create_cave_region(multiworld, player, 'Death Mountain Return Cave', 'a connector', None,
['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
create_lw_region(world, player, 'Death Mountain Return Ledge', None,
create_lw_region(multiworld, player, 'Death Mountain Return Ledge', None,
['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)',
'Bumper Cave Ledge Mirror Spot']),
create_cave_region(world, player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'],
create_cave_region(multiworld, player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'],
['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
create_cave_region(world, player, 'Spectacle Rock Cave (Bottom)', 'a connector', None,
create_cave_region(multiworld, player, 'Spectacle Rock Cave (Bottom)', 'a connector', None,
['Spectacle Rock Cave Exit']),
create_cave_region(world, player, 'Spectacle Rock Cave (Peak)', 'a connector', None,
create_cave_region(multiworld, player, 'Spectacle Rock Cave (Peak)', 'a connector', None,
['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
create_lw_region(world, player, 'East Death Mountain (Bottom)', None,
create_lw_region(multiworld, player, 'East Death Mountain (Bottom)', None,
['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)',
'East Death Mountain Mirror Spot (Bottom)', 'Hookshot Fairy',
'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
create_cave_region(world, player, 'Hookshot Fairy', 'fairies deep in a cave'),
create_cave_region(world, player, 'Paradox Cave Front', 'a connector', None,
create_cave_region(multiworld, player, 'Hookshot Fairy', 'fairies deep in a cave'),
create_cave_region(multiworld, player, 'Paradox Cave Front', 'a connector', None,
['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)',
'Light World Death Mountain Shop']),
create_cave_region(world, player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
create_cave_region(multiworld, player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
'Paradox Cave Lower - Left',
'Paradox Cave Lower - Right',
'Paradox Cave Lower - Far Right',
@@ -220,273 +221,273 @@ def create_inverted_regions(world, player):
'Paradox Cave Upper - Left',
'Paradox Cave Upper - Right'],
['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']),
create_cave_region(world, player, 'Paradox Cave', 'a connector', None,
create_cave_region(multiworld, player, 'Paradox Cave', 'a connector', None,
['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
create_cave_region(world, player, 'Light World Death Mountain Shop', 'a common shop'),
create_lw_region(world, player, 'East Death Mountain (Top)', ['Floating Island'],
create_cave_region(multiworld, player, 'Light World Death Mountain Shop', 'a common shop'),
create_lw_region(multiworld, player, 'East Death Mountain (Top)', ['Floating Island'],
['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access',
'East Death Mountain Drop', 'East Death Mountain Mirror Spot (Top)',
'Fairy Ascension Ledge Access', 'Mimic Cave Ledge Access',
'Floating Island Mirror Spot']),
create_lw_region(world, player, 'Spiral Cave Ledge', None,
create_lw_region(multiworld, player, 'Spiral Cave Ledge', None,
['Spiral Cave', 'Spiral Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (West)']),
create_lw_region(world, player, 'Mimic Cave Ledge', None,
create_lw_region(multiworld, player, 'Mimic Cave Ledge', None,
['Mimic Cave', 'Mimic Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (East)']),
create_cave_region(world, player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'],
create_cave_region(multiworld, player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'],
['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
create_cave_region(world, player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
create_lw_region(world, player, 'Fairy Ascension Plateau', None,
create_cave_region(multiworld, player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
create_lw_region(multiworld, player, 'Fairy Ascension Plateau', None,
['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
create_cave_region(world, player, 'Fairy Ascension Cave (Bottom)', 'a connector', None,
create_cave_region(multiworld, player, 'Fairy Ascension Cave (Bottom)', 'a connector', None,
['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']),
create_cave_region(world, player, 'Fairy Ascension Cave (Drop)', 'a connector', None,
create_cave_region(multiworld, player, 'Fairy Ascension Cave (Drop)', 'a connector', None,
['Fairy Ascension Cave Pots']),
create_cave_region(world, player, 'Fairy Ascension Cave (Top)', 'a connector', None,
create_cave_region(multiworld, player, 'Fairy Ascension Cave (Top)', 'a connector', None,
['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
create_lw_region(world, player, 'Fairy Ascension Ledge', None,
create_lw_region(multiworld, player, 'Fairy Ascension Ledge', None,
['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)', 'Laser Bridge Mirror Spot']),
create_lw_region(world, player, 'Death Mountain (Top)', ['Ether Tablet', 'Spectacle Rock'],
create_lw_region(multiworld, player, 'Death Mountain (Top)', ['Ether Tablet', 'Spectacle Rock'],
['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop',
'Death Mountain (Top) Mirror Spot']),
create_dw_region(world, player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'],
create_dw_region(multiworld, player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'],
['Bumper Cave Ledge Drop', 'Bumper Cave (Top)']),
create_dungeon_region(world, player, 'Tower of Hera (Bottom)', 'Tower of Hera', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
create_dungeon_region(world, player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']),
create_dungeon_region(world, player, 'Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']),
create_dungeon_region(multiworld, player, 'Tower of Hera (Bottom)', 'Tower of Hera', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
create_dungeon_region(multiworld, player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']),
create_dungeon_region(multiworld, player, 'Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']),
create_dw_region(world, player, 'East Dark World', ['Pyramid'],
create_dw_region(multiworld, player, 'East Dark World', ['Pyramid'],
['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness',
'Dark Lake Hylia Drop (East)',
'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint',
'Northeast Dark World Broken Bridge Pass', 'East Dark World Teleporter', 'EDW Flute']),
create_dw_region(world, player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
create_dw_region(world, player, 'Northeast Dark World', None,
create_dw_region(multiworld, player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
create_dw_region(multiworld, player, 'Northeast Dark World', None,
['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass',
'NEDW Flute', 'Dark Lake Hylia Teleporter', 'Catfish Entrance Rock']),
create_cave_region(world, player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region(world, player, 'East Dark World Hint', 'a storyteller'),
create_dw_region(world, player, 'South Dark World', ['Stumpy', 'Digging Game'],
create_cave_region(multiworld, player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region(multiworld, player, 'East Dark World Hint', 'a storyteller'),
create_dw_region(multiworld, player, 'South Dark World', ['Stumpy', 'Digging Game'],
['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock',
'East Dark World Bridge', 'Inverted Links House', 'Archery Game', 'Bonk Fairy (Dark)',
'Dark Lake Hylia Shop', 'South Dark World Teleporter', 'Post Aga Teleporter', 'SDW Flute']),
create_cave_region(world, player, 'Inverted Big Bomb Shop', 'the bomb shop'),
create_cave_region(world, player, 'Archery Game', 'a game of skill'),
create_dw_region(world, player, 'Dark Lake Hylia', None,
create_cave_region(multiworld, player, 'Inverted Big Bomb Shop', 'the bomb shop'),
create_cave_region(multiworld, player, 'Archery Game', 'a game of skill'),
create_dw_region(multiworld, player, 'Dark Lake Hylia', None,
['East Dark World Pier', 'Dark Lake Hylia Ledge Pier', 'Ice Palace Missing Wall']),
create_dw_region(world, player, 'Dark Lake Hylia Central Island', None,
create_dw_region(multiworld, player, 'Dark Lake Hylia Central Island', None,
['Dark Lake Hylia Shallows', 'Ice Palace', 'Dark Lake Hylia Central Island Teleporter']),
create_dw_region(world, player, 'Dark Lake Hylia Ledge', None,
create_dw_region(multiworld, player, 'Dark Lake Hylia Ledge', None,
['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint',
'Dark Lake Hylia Ledge Spike Cave', 'DLHL Flute']),
create_cave_region(world, player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
create_cave_region(world, player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
create_cave_region(world, player, 'Hype Cave', 'a bounty of five items',
create_cave_region(multiworld, player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
create_cave_region(multiworld, player, 'Hype Cave', 'a bounty of five items',
['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
create_dw_region(world, player, 'West Dark World', ['Frog', 'Flute Activation Spot'],
create_dw_region(multiworld, player, 'West Dark World', ['Frog', 'Flute Activation Spot'],
['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House',
'Chest Game', 'Thieves Town', 'Bumper Cave Entrance Rock',
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks',
'Red Shield Shop', 'Inverted Dark Sanctuary', 'Fortune Teller (Dark)',
'Dark World Lumberjack Shop',
'West Dark World Teleporter', 'WDW Flute']),
create_dw_region(world, player, 'Dark Grassy Lawn', None,
create_dw_region(multiworld, player, 'Dark Grassy Lawn', None,
['Grassy Lawn Pegs', 'Village of Outcasts Shop', 'Dark Grassy Lawn Flute']),
create_dw_region(world, player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'],
create_dw_region(multiworld, player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'],
['Dark World Hammer Peg Cave', 'Peg Area Rocks', 'Hammer Peg Area Flute']),
create_dw_region(world, player, 'Bumper Cave Entrance', None,
create_dw_region(multiworld, player, 'Bumper Cave Entrance', None,
['Bumper Cave (Bottom)', 'Bumper Cave Entrance Drop']),
create_cave_region(world, player, 'Fortune Teller (Dark)', 'a fortune teller'),
create_cave_region(world, player, 'Village of Outcasts Shop', 'a common shop'),
create_cave_region(world, player, 'Dark Lake Hylia Shop', 'a common shop'),
create_cave_region(world, player, 'Dark World Lumberjack Shop', 'a common shop'),
create_cave_region(world, player, 'Dark World Potion Shop', 'a common shop'),
create_cave_region(world, player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
create_cave_region(world, player, 'Pyramid Fairy', 'a cave with two chests',
create_cave_region(multiworld, player, 'Fortune Teller (Dark)', 'a fortune teller'),
create_cave_region(multiworld, player, 'Village of Outcasts Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark World Lumberjack Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark World Potion Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
create_cave_region(multiworld, player, 'Pyramid Fairy', 'a cave with two chests',
['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
create_cave_region(world, player, 'Brewery', 'a house with a chest', ['Brewery']),
create_cave_region(world, player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
create_cave_region(world, player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
create_cave_region(world, player, 'Red Shield Shop', 'the rare shop'),
create_cave_region(world, player, 'Inverted Dark Sanctuary', 'a storyteller', None,
create_cave_region(multiworld, player, 'Brewery', 'a house with a chest', ['Brewery']),
create_cave_region(multiworld, player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
create_cave_region(multiworld, player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
create_cave_region(multiworld, player, 'Red Shield Shop', 'the rare shop'),
create_cave_region(multiworld, player, 'Inverted Dark Sanctuary', 'a storyteller', None,
['Inverted Dark Sanctuary Exit']),
create_cave_region(world, player, 'Bumper Cave', 'a connector', None,
create_cave_region(multiworld, player, 'Bumper Cave', 'a connector', None,
['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
create_dw_region(world, player, 'Skull Woods Forest', None,
create_dw_region(multiworld, player, 'Skull Woods Forest', None,
['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)',
'Skull Woods First Section Hole (North)',
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
create_dw_region(world, player, 'Skull Woods Forest (West)', None,
create_dw_region(multiworld, player, 'Skull Woods Forest (West)', None,
['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)',
'Skull Woods Final Section']),
create_dw_region(world, player, 'Dark Desert', None,
create_dw_region(multiworld, player, 'Dark Desert', None,
['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy', 'DD Flute']),
create_dw_region(world, player, 'Dark Desert Ledge', None, ['Dark Desert Drop', 'Dark Desert Teleporter']),
create_cave_region(world, player, 'Mire Shed', 'a cave with two chests',
create_dw_region(multiworld, player, 'Dark Desert Ledge', None, ['Dark Desert Drop', 'Dark Desert Teleporter']),
create_cave_region(multiworld, player, 'Mire Shed', 'a cave with two chests',
['Mire Shed - Left', 'Mire Shed - Right']),
create_cave_region(world, player, 'Dark Desert Hint', 'a storyteller'),
create_dw_region(world, player, 'Dark Death Mountain', None,
create_cave_region(multiworld, player, 'Dark Desert Hint', 'a storyteller'),
create_dw_region(multiworld, player, 'Dark Death Mountain', None,
['Dark Death Mountain Drop (East)', 'Inverted Agahnims Tower', 'Superbunny Cave (Top)',
'Hookshot Cave', 'Turtle Rock',
'Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)',
'Turtle Rock Tail Drop', 'DDM Flute']),
create_dw_region(world, player, 'Dark Death Mountain Ledge', None,
create_dw_region(multiworld, player, 'Dark Death Mountain Ledge', None,
['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)']),
create_dw_region(world, player, 'Turtle Rock (Top)', None,
create_dw_region(multiworld, player, 'Turtle Rock (Top)', None,
['Dark Death Mountain Teleporter (East)', 'Turtle Rock Drop']),
create_dw_region(world, player, 'Dark Death Mountain Isolated Ledge', None,
create_dw_region(multiworld, player, 'Dark Death Mountain Isolated Ledge', None,
['Turtle Rock Isolated Ledge Entrance']),
create_dw_region(world, player, 'Dark Death Mountain (East Bottom)', None,
create_dw_region(multiworld, player, 'Dark Death Mountain (East Bottom)', None,
['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)',
'Dark Death Mountain Teleporter (East Bottom)', 'EDDM Flute']),
create_cave_region(world, player, 'Superbunny Cave (Top)', 'a connector',
create_cave_region(multiworld, player, 'Superbunny Cave (Top)', 'a connector',
['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
create_cave_region(world, player, 'Superbunny Cave (Bottom)', 'a connector', None,
create_cave_region(multiworld, player, 'Superbunny Cave (Bottom)', 'a connector', None,
['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
create_cave_region(world, player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
create_cave_region(world, player, 'Hookshot Cave', 'a connector',
create_cave_region(multiworld, player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
create_cave_region(multiworld, 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 Bomb Wall (South)']),
create_cave_region(world, player, 'Hookshot Cave (Upper)', 'a connector', None, ['Hookshot Cave Exit (North)',
create_cave_region(multiworld, 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,
create_dw_region(multiworld, 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']),
create_cave_region(multiworld, player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
create_dungeon_region(world, player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
create_dungeon_region(world, player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
create_dungeon_region(world, player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest', 'Swamp Palace - Pot Row Pot Key',
create_dungeon_region(multiworld, player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
create_dungeon_region(multiworld, player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
create_dungeon_region(multiworld, player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest', 'Swamp Palace - Pot Row Pot Key',
'Swamp Palace - Trench 1 Pot Key'], ['Swamp Palace (Center)']),
create_dungeon_region(world, player, 'Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest', 'Swamp Palace - Hookshot Pot Key',
create_dungeon_region(multiworld, player, 'Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest', 'Swamp Palace - Hookshot Pot Key',
'Swamp Palace - Trench 2 Pot Key'], ['Swamp Palace (North)', 'Swamp Palace (West)']),
create_dungeon_region(world, player, 'Swamp Palace (West)', 'Swamp Palace', ['Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest']),
create_dungeon_region(world, player, 'Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
create_dungeon_region(multiworld, player, 'Swamp Palace (West)', 'Swamp Palace', ['Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest']),
create_dungeon_region(multiworld, player, 'Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
'Swamp Palace - Waterway Pot Key', 'Swamp Palace - Waterfall Room',
'Swamp Palace - Boss', 'Swamp Palace - Prize']),
create_dungeon_region(world, player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest',
create_dungeon_region(multiworld, player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest',
'Thieves\' Town - Map Chest',
'Thieves\' Town - Compass Chest',
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
create_dungeon_region(world, player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
create_dungeon_region(multiworld, player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
'Thieves\' Town - Big Chest',
'Thieves\' Town - Hallway Pot Key',
'Thieves\' Town - Spike Switch Pot Key',
'Thieves\' Town - Blind\'s Cell'],
['Blind Fight']),
create_dungeon_region(world, player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
create_dungeon_region(world, player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
create_dungeon_region(world, player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
create_dungeon_region(world, player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
create_dungeon_region(world, player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
create_dungeon_region(world, player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
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 - 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',
create_dungeon_region(multiworld, player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
create_dungeon_region(multiworld, player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
create_dungeon_region(multiworld, 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(multiworld, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
create_dungeon_region(multiworld, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop', 'Skull Woods - Boss', 'Skull Woods - Prize']),
create_dungeon_region(multiworld, 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(multiworld, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Palace (Main)']),
create_dungeon_region(multiworld, 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)']),
create_dungeon_region(world, player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
create_dungeon_region(world, player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest', 'Ice Palace - Hammer Block Key Drop']),
create_dungeon_region(world, player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
create_dungeon_region(world, player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
create_dungeon_region(world, player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
create_dungeon_region(multiworld, player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
create_dungeon_region(multiworld, player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest', 'Ice Palace - Hammer Block Key Drop']),
create_dungeon_region(multiworld, player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
create_dungeon_region(multiworld, player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
create_dungeon_region(multiworld, player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest',
'Misery Mire - Spikes Pot Key', 'Misery Mire - Fishbone Pot Key',
'Misery Mire - Conveyor Crystal Key Drop'], ['Misery Mire (West)', 'Misery Mire Big Key Door']),
create_dungeon_region(world, player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
create_dungeon_region(world, player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
create_dungeon_region(world, player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
create_dungeon_region(world, player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
create_dungeon_region(world, player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
create_dungeon_region(multiworld, player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
create_dungeon_region(multiworld, player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
create_dungeon_region(multiworld, player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
'Turtle Rock - Roller Room - Right'],
['Turtle Rock Entrance to Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
create_dungeon_region(world, player, 'Turtle Rock (Pokey Room)', 'Turtle Rock', ['Turtle Rock - Pokey 1 Key Drop'], ['Turtle Rock (Pokey Room) (North)', 'Turtle Rock (Pokey Room) (South)']),
create_dungeon_region(world, player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'],
create_dungeon_region(multiworld, player, 'Turtle Rock (Pokey Room)', 'Turtle Rock', ['Turtle Rock - Pokey 1 Key Drop'], ['Turtle Rock (Pokey Room) (North)', 'Turtle Rock (Pokey Room) (South)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'],
['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
create_dungeon_region(world, player, 'Turtle Rock (Second Section)', 'Turtle Rock',
create_dungeon_region(multiworld, player, 'Turtle Rock (Second Section)', 'Turtle Rock',
['Turtle Rock - Big Key Chest', 'Turtle Rock - Pokey 2 Key Drop'],
['Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door',
'Turtle Rock Second Section Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Second Section Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Second Section from Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
create_dungeon_region(world, player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
create_dungeon_region(world, player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
create_dungeon_region(world, player, 'Turtle Rock (Eye Bridge Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Isolated Ledge Exit', 'Turtle Rock Eye Bridge from Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
create_dungeon_region(multiworld, player, 'Turtle Rock (Second Section Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Second Section from Bomb Wall']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Eye Bridge Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Isolated Ledge Exit', 'Turtle Rock Eye Bridge from Bomb Wall']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Eye Bridge Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
create_dungeon_region(world, player, 'Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
create_dungeon_region(world, player, 'Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
create_dungeon_region(multiworld, player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']),
create_dungeon_region(world, player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']),
create_dungeon_region(world, player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
create_dungeon_region(world, player, 'Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
create_dungeon_region(multiworld, player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']),
create_dungeon_region(world, player, 'Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
create_dungeon_region(world, player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']),
create_dungeon_region(world, player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
create_dungeon_region(world, player, 'Inverted Ganons Tower (Entrance)', 'Ganon\'s Tower',
create_dungeon_region(multiworld, player, 'Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
create_dungeon_region(multiworld, player, 'Inverted Ganons Tower (Entrance)', 'Ganon\'s Tower',
['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left',
'Ganons Tower - Hope Room - Right', 'Ganons Tower - Conveyor Cross Pot Key'],
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door',
'Inverted Ganons Tower Exit']),
create_dungeon_region(world, player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'],
create_dungeon_region(multiworld, player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'],
['Ganons Tower (Tile Room) Key Door']),
create_dungeon_region(world, player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower',
create_dungeon_region(multiworld, player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower',
['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'],
['Ganons Tower (Bottom) (East)']),
create_dungeon_region(world, player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower',
create_dungeon_region(multiworld, player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower',
['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right',
'Ganons Tower - Double Switch Pot Key'],
['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']),
create_dungeon_region(world, player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
create_dungeon_region(world, player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower',
create_dungeon_region(multiworld, player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower',
['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
create_dungeon_region(world, player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower',
create_dungeon_region(multiworld, player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower',
['Ganons Tower - Randomizer Room - Top Left',
'Ganons Tower - Randomizer Room - Top Right',
'Ganons Tower - Randomizer Room - Bottom Left',
'Ganons Tower - Randomizer Room - Bottom Right'],
['Ganons Tower (Bottom) (West)']),
create_dungeon_region(world, player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower',
create_dungeon_region(multiworld, player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower',
['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']),
create_dungeon_region(world, player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']),
create_dungeon_region(world, player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower',
create_dungeon_region(multiworld, player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower',
['Ganons Tower - Mini Helmasaur Room - Left',
'Ganons Tower - Mini Helmasaur Room - Right',
'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Mini Helmasaur Key Drop'],
['Ganons Tower Moldorm Door']),
create_dungeon_region(world, player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']),
create_dungeon_region(world, player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_cave_region(world, player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
create_cave_region(world, player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
create_dw_region(world, player, 'Pyramid Ledge', None, ['Pyramid Drop']), # houlihan room exits here in inverted
create_dungeon_region(multiworld, player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_cave_region(multiworld, player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
create_cave_region(multiworld, player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
create_dw_region(multiworld, player, 'Pyramid Ledge', None, ['Pyramid Drop']), # houlihan room exits here in inverted
# to simplify flute connections
create_cave_region(world, player, 'The Sky', 'A Dark Sky', None,
create_cave_region(multiworld, player, 'The Sky', 'A Dark Sky', None,
['DDM Landing', 'NEDW Landing', 'WDW Landing', 'SDW Landing', 'EDW Landing', 'DD Landing',
'DLHL Landing']),
create_lw_region(world, player, 'Desert Northern Cliffs'),
create_lw_region(world, player, 'Death Mountain Bunny Descent Area')
create_lw_region(multiworld, player, 'Desert Northern Cliffs'),
create_lw_region(multiworld, player, 'Death Mountain Bunny Descent Area')
]
def mark_dark_world_regions(world, player):
def mark_dark_world_regions(multiworld: MultiWorld, player: int):
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
# That is ok. the bunny logic will check for this case and incorporate special rules.
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.DarkWorld)
queue = collections.deque(region for region in multiworld.get_regions(player) if region.type == LTTPRegionType.DarkWorld)
seen = set(queue)
while queue:
current = queue.popleft()
@@ -499,7 +500,7 @@ def mark_dark_world_regions(world, player):
seen.add(exit.connected_region)
queue.append(exit.connected_region)
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.LightWorld)
queue = collections.deque(region for region in multiworld.get_regions(player) if region.type == LTTPRegionType.LightWorld)
seen = set(queue)
while queue:
current = queue.popleft()

View File

@@ -1,8 +1,9 @@
from collections import namedtuple
import logging
from BaseClasses import ItemClassification
from BaseClasses import ItemClassification, MultiWorld
from Options import OptionError
from typing import TYPE_CHECKING
from .SubClasses import ALttPLocation, LTTPRegion, LTTPRegionType
from .Shops import TakeAny, total_shop_slots, set_up_shops, shop_table_by_location, ShopType
@@ -14,6 +15,9 @@ from .Options import small_key_shuffle, compass_shuffle, big_key_shuffle, map_sh
from .StateHelpers import has_triforce_pieces, has_melee_weapon
from .Regions import key_drop_data
if TYPE_CHECKING:
from . import ALTTPWorld
# This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
# Some basic items that various modes require are placed here, including pendants and crystals. Medallion requirements for the two relevant entrances are also decided.
@@ -222,7 +226,7 @@ items_reduction_table = (
)
def generate_itempool(world):
def generate_itempool(world: "ALTTPWorld"):
player: int = world.player
multiworld = world.multiworld
@@ -531,7 +535,7 @@ take_any_locations_inverted.sort()
take_any_locations.sort()
def set_up_take_anys(multiworld, world, player):
def set_up_take_anys(multiworld: MultiWorld, world: "ALTTPWorld", player: int):
# these are references, do not modify these lists in-place
if world.options.mode == 'inverted':
take_any_locs = take_any_locations_inverted
@@ -585,15 +589,15 @@ def set_up_take_anys(multiworld, world, player):
location.place_locked_item(item_factory("Boss Heart Container", world))
def get_pool_core(world, player: int):
shuffle = world.worlds[player].options.entrance_shuffle.current_key
difficulty = world.worlds[player].options.item_pool.current_key
timer = world.worlds[player].options.timer.current_key
goal = world.worlds[player].options.goal.current_key
mode = world.worlds[player].options.mode.current_key
swordless = world.worlds[player].options.swordless
retro_bow = world.worlds[player].options.retro_bow
logic = world.worlds[player].options.glitches_required
def get_pool_core(multiworld: MultiWorld, player: int):
shuffle = multiworld.worlds[player].options.entrance_shuffle.current_key
difficulty = multiworld.worlds[player].options.item_pool.current_key
timer = multiworld.worlds[player].options.timer.current_key
goal = multiworld.worlds[player].options.goal.current_key
mode = multiworld.worlds[player].options.mode.current_key
swordless = multiworld.worlds[player].options.swordless
retro_bow = multiworld.worlds[player].options.retro_bow
logic = multiworld.worlds[player].options.glitches_required
pool = []
placed_items = {}
@@ -610,13 +614,13 @@ def get_pool_core(world, player: int):
placed_items[loc] = item
# provide boots to major glitch dependent seeds
if logic.current_key in {'overworld_glitches', 'hybrid_major_glitches', 'no_logic'} and world.worlds[player].options.glitch_boots:
if logic.current_key in {'overworld_glitches', 'hybrid_major_glitches', 'no_logic'} and multiworld.worlds[player].options.glitch_boots:
precollected_items.append('Pegasus Boots')
pool.remove('Pegasus Boots')
pool.append('Rupees (20)')
want_progressives = world.worlds[player].options.progressive.want_progressives
want_progressives = multiworld.worlds[player].options.progressive.want_progressives
if want_progressives(world.random):
if want_progressives(multiworld.random):
pool.extend(diff.progressiveglove)
else:
pool.extend(diff.basicglove)
@@ -640,27 +644,27 @@ def get_pool_core(world, player: int):
thisbottle = None
for _ in range(diff.bottle_count):
if not diff.same_bottle or not thisbottle:
thisbottle = world.random.choice(diff.bottles)
thisbottle = multiworld.random.choice(diff.bottles)
pool.append(thisbottle)
if want_progressives(world.random):
if want_progressives(multiworld.random):
pool.extend(diff.progressiveshield)
else:
pool.extend(diff.basicshield)
if want_progressives(world.random):
if want_progressives(multiworld.random):
pool.extend(diff.progressivearmor)
else:
pool.extend(diff.basicarmor)
if want_progressives(world.random):
if want_progressives(multiworld.random):
pool.extend(diff.progressivemagic)
else:
pool.extend(diff.basicmagic)
if want_progressives(world.random):
if want_progressives(multiworld.random):
pool.extend(diff.progressivebow)
world.worlds[player].has_progressive_bows = True
multiworld.worlds[player].has_progressive_bows = True
elif (swordless or logic == 'no_glitches'):
swordless_bows = ['Bow', 'Silver Bow']
if difficulty == "easy":
@@ -672,7 +676,7 @@ def get_pool_core(world, player: int):
if swordless:
pool.extend(diff.swordless)
else:
progressive_swords = want_progressives(world.random)
progressive_swords = want_progressives(multiworld.random)
pool.extend(diff.progressivesword if progressive_swords else diff.basicsword)
extraitems = total_items_to_place - len(pool) - len(placed_items)
@@ -688,29 +692,29 @@ def get_pool_core(world, player: int):
additional_pieces_to_place = 0
if 'triforce_hunt' in goal:
if world.worlds[player].options.triforce_pieces_mode.value == TriforcePiecesMode.option_extra:
treasure_hunt_total = (world.worlds[player].options.triforce_pieces_required.value
+ world.worlds[player].options.triforce_pieces_extra.value)
elif world.worlds[player].options.triforce_pieces_mode.value == TriforcePiecesMode.option_percentage:
percentage = float(world.worlds[player].options.triforce_pieces_percentage.value) / 100
treasure_hunt_total = int(round(world.worlds[player].options.triforce_pieces_required.value * percentage, 0))
if multiworld.worlds[player].options.triforce_pieces_mode.value == TriforcePiecesMode.option_extra:
treasure_hunt_total = (multiworld.worlds[player].options.triforce_pieces_required.value
+ multiworld.worlds[player].options.triforce_pieces_extra.value)
elif multiworld.worlds[player].options.triforce_pieces_mode.value == TriforcePiecesMode.option_percentage:
percentage = float(multiworld.worlds[player].options.triforce_pieces_percentage.value) / 100
treasure_hunt_total = int(round(multiworld.worlds[player].options.triforce_pieces_required.value * percentage, 0))
else: # available
treasure_hunt_total = world.worlds[player].options.triforce_pieces_available.value
treasure_hunt_total = multiworld.worlds[player].options.triforce_pieces_available.value
triforce_pieces = min(90, max(treasure_hunt_total, world.worlds[player].options.triforce_pieces_required.value))
triforce_pieces = min(90, max(treasure_hunt_total, multiworld.worlds[player].options.triforce_pieces_required.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_required = world.worlds[player].options.triforce_pieces_required.value
treasure_hunt_required = multiworld.worlds[player].options.triforce_pieces_required.value
for extra in diff.extras:
if extraitems >= len(extra):
pool.extend(extra)
extraitems -= len(extra)
elif extraitems > 0:
pool.extend(world.random.sample(extra, extraitems))
pool.extend(multiworld.random.sample(extra, extraitems))
break
else:
break
@@ -729,25 +733,25 @@ def get_pool_core(world, player: int):
else:
break
if world.worlds[player].options.small_key_shuffle == small_key_shuffle.option_universal:
if multiworld.worlds[player].options.small_key_shuffle == small_key_shuffle.option_universal:
pool.extend(diff.universal_keys)
if mode == 'standard':
if world.worlds[player].options.key_drop_shuffle:
if multiworld.worlds[player].options.key_drop_shuffle:
key_locations = ['Secret Passage', 'Hyrule Castle - Map Guard Key Drop']
key_location = world.random.choice(key_locations)
key_location = multiworld.random.choice(key_locations)
key_locations.remove(key_location)
place_item(key_location, "Small Key (Universal)")
key_locations += ['Hyrule Castle - Boomerang Guard Key Drop', 'Hyrule Castle - Boomerang Chest',
'Hyrule Castle - Map Chest']
key_location = world.random.choice(key_locations)
key_location = multiworld.random.choice(key_locations)
key_locations.remove(key_location)
place_item(key_location, "Small Key (Universal)")
key_locations += ['Hyrule Castle - Big Key Drop', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']
key_location = world.random.choice(key_locations)
key_location = multiworld.random.choice(key_locations)
key_locations.remove(key_location)
place_item(key_location, "Small Key (Universal)")
key_locations += ['Sewers - Key Rat Key Drop']
key_location = world.random.choice(key_locations)
key_location = multiworld.random.choice(key_locations)
place_item(key_location, "Small Key (Universal)")
pool = pool[:-3]

View File

@@ -1,24 +1,24 @@
import typing
from BaseClasses import ItemClassification as IC
from BaseClasses import MultiWorld, ItemClassification as IC
from worlds.AutoWorld import World
def GetBeemizerItem(world, player: int, item):
def GetBeemizerItem(multiworld: MultiWorld, player: int, item):
item_name = item if isinstance(item, str) else item.name
if item_name not in trap_replaceable or player in world.groups:
if item_name not in trap_replaceable or player in multiworld.groups:
return item
# first roll - replaceable item should be replaced, within beemizer_total_chance
if not world.worlds[player].options.beemizer_total_chance or world.random.random() > (world.worlds[player].options.beemizer_total_chance / 100):
if not multiworld.worlds[player].options.beemizer_total_chance or multiworld.random.random() > (multiworld.worlds[player].options.beemizer_total_chance / 100):
return item
# second roll - bee replacement should be trap, within beemizer_trap_chance
if not world.worlds[player].options.beemizer_trap_chance or world.random.random() > (world.worlds[player].options.beemizer_trap_chance / 100):
return "Bee" if isinstance(item, str) else world.create_item("Bee", player)
if not multiworld.worlds[player].options.beemizer_trap_chance or multiworld.random.random() > (multiworld.worlds[player].options.beemizer_trap_chance / 100):
return "Bee" if isinstance(item, str) else multiworld.create_item("Bee", player)
else:
return "Bee Trap" if isinstance(item, str) else world.create_item("Bee Trap", player)
return "Bee Trap" if isinstance(item, str) else multiworld.create_item("Bee Trap", player)
def item_factory(items: typing.Union[str, typing.Iterable[str]], world: World):

View File

@@ -154,13 +154,13 @@ class OpenPyramid(Choice):
alias_true = option_open
alias_false = option_closed
def to_bool(self, world: MultiWorld, player: int) -> bool:
def to_bool(self, multiworld: MultiWorld, player: int) -> bool:
if self.value == self.option_goal:
return world.worlds[player].options.goal.current_key in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'}
return multiworld.worlds[player].options.goal.current_key in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'}
elif self.value == self.option_auto:
return world.worlds[player].options.goal.current_key in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'} \
and (world.worlds[player].options.entrance_shuffle.current_key in {'vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed'} or not
world.shuffle_ganon)
return multiworld.worlds[player].options.goal.current_key in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'} \
and (multiworld.worlds[player].options.entrance_shuffle.current_key in {'vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed'} or not
multiworld.shuffle_ganon)
elif self.value == self.option_open:
return True
else:

View File

@@ -2,6 +2,7 @@
Helper functions to deliver entrance/exit/region sets to OWG rules.
"""
from BaseClasses import MultiWorld
from .StateHelpers import can_lift_heavy_rocks, can_boots_clip_lw, can_boots_clip_dw, can_get_glitched_speed_dw
@@ -200,7 +201,7 @@ def get_mirror_offset_spots_dw():
yield ('Dark Death Mountain Offset Mirror', 'Dark Death Mountain (West Bottom)', 'East Dark World')
def get_mirror_offset_spots_lw(player):
def get_mirror_offset_spots_lw(player: int):
"""
Mirror shenanigans placing a mirror portal with a broken camera
"""
@@ -218,54 +219,54 @@ def get_invalid_bunny_revival_dungeons():
yield 'Sanctuary'
def overworld_glitch_connections(world, player):
def overworld_glitch_connections(multiworld: MultiWorld, player: int):
# Boots-accessible locations.
create_owg_connections(player, world, get_boots_clip_exits_lw(world.worlds[player].options.mode == 'inverted'))
create_owg_connections(player, world, get_boots_clip_exits_dw(world.worlds[player].options.mode == 'inverted', player))
create_owg_connections(player, multiworld, get_boots_clip_exits_lw(multiworld.worlds[player].options.mode == 'inverted'))
create_owg_connections(player, multiworld, get_boots_clip_exits_dw(multiworld.worlds[player].options.mode == 'inverted', player))
# Glitched speed drops.
create_owg_connections(player, world, get_glitched_speed_drops_dw(world.worlds[player].options.mode == 'inverted'))
create_owg_connections(player, multiworld, get_glitched_speed_drops_dw(multiworld.worlds[player].options.mode == 'inverted'))
# Mirror clip spots.
if world.worlds[player].options.mode != 'inverted':
create_owg_connections(player, world, get_mirror_clip_spots_dw())
create_owg_connections(player, world, get_mirror_offset_spots_dw())
if multiworld.worlds[player].options.mode != 'inverted':
create_owg_connections(player, multiworld, get_mirror_clip_spots_dw())
create_owg_connections(player, multiworld, get_mirror_offset_spots_dw())
else:
create_owg_connections(player, world, get_mirror_offset_spots_lw(player))
create_owg_connections(player, multiworld, get_mirror_offset_spots_lw(player))
def overworld_glitches_rules(world, player):
def overworld_glitches_rules(multiworld: MultiWorld, player: int):
# Boots-accessible locations.
set_owg_connection_rules(player, world, get_boots_clip_exits_lw(world.worlds[player].options.mode == 'inverted'), lambda state: can_boots_clip_lw(state, player))
set_owg_connection_rules(player, world, get_boots_clip_exits_dw(world.worlds[player].options.mode == 'inverted', player), lambda state: can_boots_clip_dw(state, player))
set_owg_connection_rules(player, multiworld, get_boots_clip_exits_lw(multiworld.worlds[player].options.mode == 'inverted'), lambda state: can_boots_clip_lw(state, player))
set_owg_connection_rules(player, multiworld, get_boots_clip_exits_dw(multiworld.worlds[player].options.mode == 'inverted', player), lambda state: can_boots_clip_dw(state, player))
# Glitched speed drops.
set_owg_connection_rules(player, world, get_glitched_speed_drops_dw(world.worlds[player].options.mode == 'inverted'), lambda state: can_get_glitched_speed_dw(state, player))
set_owg_connection_rules(player, multiworld, get_glitched_speed_drops_dw(multiworld.worlds[player].options.mode == 'inverted'), lambda state: can_get_glitched_speed_dw(state, player))
# Dark Death Mountain Ledge Clip Spot also accessible with mirror.
if world.worlds[player].options.mode != 'inverted':
add_alternate_rule(world.get_entrance('Dark Death Mountain Ledge Clip Spot', player), lambda state: state.has('Magic Mirror', player))
if multiworld.worlds[player].options.mode != 'inverted':
add_alternate_rule(multiworld.get_entrance('Dark Death Mountain Ledge Clip Spot', player), lambda state: state.has('Magic Mirror', player))
# Mirror clip spots.
if world.worlds[player].options.mode != 'inverted':
set_owg_connection_rules(player, world, get_mirror_clip_spots_dw(), lambda state: state.has('Magic Mirror', player))
set_owg_connection_rules(player, world, get_mirror_offset_spots_dw(), lambda state: state.has('Magic Mirror', player) and can_boots_clip_lw(state, player))
if multiworld.worlds[player].options.mode != 'inverted':
set_owg_connection_rules(player, multiworld, get_mirror_clip_spots_dw(), lambda state: state.has('Magic Mirror', player))
set_owg_connection_rules(player, multiworld, get_mirror_offset_spots_dw(), lambda state: state.has('Magic Mirror', player) and can_boots_clip_lw(state, player))
else:
set_owg_connection_rules(player, world, get_mirror_offset_spots_lw(player), lambda state: state.has('Magic Mirror', player) and can_boots_clip_dw(state, player))
set_owg_connection_rules(player, multiworld, get_mirror_offset_spots_lw(player), lambda state: state.has('Magic Mirror', player) and can_boots_clip_dw(state, player))
# Regions that require the boots and some other stuff.
if world.worlds[player].options.mode != 'inverted':
world.get_entrance('Turtle Rock Teleporter', player).access_rule = lambda state: (can_boots_clip_lw(state, player) or can_lift_heavy_rocks(state, player)) and state.has('Hammer', player)
add_alternate_rule(world.get_entrance('Waterfall of Wishing', player), lambda state: state.has('Moon Pearl', player) or state.has('Pegasus Boots', player))
if multiworld.worlds[player].options.mode != 'inverted':
multiworld.get_entrance('Turtle Rock Teleporter', player).access_rule = lambda state: (can_boots_clip_lw(state, player) or can_lift_heavy_rocks(state, player)) and state.has('Hammer', player)
add_alternate_rule(multiworld.get_entrance('Waterfall of Wishing', player), lambda state: state.has('Moon Pearl', player) or state.has('Pegasus Boots', player))
else:
add_alternate_rule(world.get_entrance('Waterfall of Wishing Cave', player), lambda state: state.has('Moon Pearl', player))
add_alternate_rule(multiworld.get_entrance('Waterfall of Wishing Cave', player), lambda state: state.has('Moon Pearl', player))
world.get_entrance('Dark Desert Teleporter', player).access_rule = lambda state: (state.has('Flute', player) or state.has('Pegasus Boots', player)) and can_lift_heavy_rocks(state, player)
add_alternate_rule(world.get_entrance('Catfish Exit Rock', player), lambda state: can_boots_clip_dw(state, player))
add_alternate_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: can_boots_clip_dw(state, player))
multiworld.get_entrance('Dark Desert Teleporter', player).access_rule = lambda state: (state.has('Flute', player) or state.has('Pegasus Boots', player)) and can_lift_heavy_rocks(state, player)
add_alternate_rule(multiworld.get_entrance('Catfish Exit Rock', player), lambda state: can_boots_clip_dw(state, player))
add_alternate_rule(multiworld.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: can_boots_clip_dw(state, player))
# Zora's Ledge via waterwalk setup.
add_alternate_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Pegasus Boots', player))
add_alternate_rule(multiworld.get_location('Zora\'s Ledge', player), lambda state: state.has('Pegasus Boots', player))
def add_alternate_rule(entrance, rule):
@@ -273,22 +274,22 @@ def add_alternate_rule(entrance, rule):
entrance.access_rule = lambda state: old_rule(state) or rule(state)
def create_no_logic_connections(player, world, connections):
def create_no_logic_connections(player: int, multiworld: MultiWorld, connections):
for entrance, parent_region, target_region, *rule_override in connections:
parent = world.get_region(parent_region, player)
target = world.get_region(target_region, player)
parent = multiworld.get_region(parent_region, player)
target = multiworld.get_region(target_region, player)
parent.connect(target, entrance)
def create_owg_connections(player, world, connections):
def create_owg_connections(player: int, multiworld: MultiWorld, connections):
for entrance, parent_region, target_region, *rule_override in connections:
parent = world.get_region(parent_region, player)
target = world.get_region(target_region, player)
parent = multiworld.get_region(parent_region, player)
target = multiworld.get_region(target_region, player)
parent.connect(target, entrance)
def set_owg_connection_rules(player, world, connections, default_rule):
def set_owg_connection_rules(player: int, multiworld: MultiWorld, connections, default_rule):
for entrance, _, _, *rule_override in connections:
connection = world.get_entrance(entrance, player)
connection = multiworld.get_entrance(entrance, player)
rule = rule_override[0] if len(rule_override) > 0 else default_rule
connection.access_rule = rule

View File

@@ -9,11 +9,11 @@ def is_main_entrance(entrance: LTTPEntrance) -> bool:
return entrance.parent_region.type in {LTTPRegionType.DarkWorld, LTTPRegionType.LightWorld} if entrance.parent_region.type else True
def create_regions(world, player):
def create_regions(multiworld: MultiWorld, player: int):
world.regions += [
create_lw_region(world, player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q']),
create_lw_region(world, player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure',
multiworld.regions += [
create_lw_region(multiworld, player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q']),
create_lw_region(multiworld, player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure',
'Purple Chest', 'Flute Activation Spot'],
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam',
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
@@ -24,122 +24,122 @@ def create_regions(world, player):
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', 'Waterfall of Wishing', 'Hyrule Castle Main Gate',
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']),
create_lw_region(world, player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']),
create_lw_region(world, player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']),
create_cave_region(world, player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
create_lw_region(multiworld, player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']),
create_lw_region(multiworld, player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']),
create_cave_region(multiworld, player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
"Blind\'s Hideout - Left",
"Blind\'s Hideout - Right",
"Blind\'s Hideout - Far Left",
"Blind\'s Hideout - Far Right"]),
create_cave_region(world, player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
create_lw_region(world, player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
create_cave_region(world, player, 'Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
create_lw_region(world, player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
create_cave_region(world, player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
create_cave_region(world, player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
create_cave_region(world, player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
create_cave_region(world, player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']),
create_cave_region(world, player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
create_cave_region(world, player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
create_cave_region(world, player, 'Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']),
create_cave_region(world, player, 'Snitch Lady (East)', 'a boring house'),
create_cave_region(world, player, 'Snitch Lady (West)', 'a boring house'),
create_cave_region(world, player, 'Bush Covered House', 'the grass man'),
create_cave_region(world, player, 'Tavern (Front)', 'the tavern'),
create_cave_region(world, player, 'Light World Bomb Hut', 'a restock room'),
create_cave_region(world, player, 'Kakariko Shop', 'a common shop'),
create_cave_region(world, player, 'Fortune Teller (Light)', 'a fortune teller'),
create_cave_region(world, player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
create_cave_region(world, player, 'Lumberjack House', 'a boring house'),
create_cave_region(world, player, 'Bonk Fairy (Light)', 'a fairy fountain'),
create_cave_region(world, player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
create_cave_region(world, player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Swamp Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
create_cave_region(world, player, 'Chicken House', 'a house with a chest', ['Chicken House']),
create_cave_region(world, player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
create_cave_region(world, player, 'Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']),
create_cave_region(world, player, 'Kakariko Well (top)', 'a drop\'s exit', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
create_cave_region(multiworld, player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
create_lw_region(multiworld, player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
create_cave_region(multiworld, player, 'Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
create_lw_region(multiworld, player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
create_cave_region(multiworld, player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
create_cave_region(multiworld, player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
create_cave_region(multiworld, player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
create_cave_region(multiworld, player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']),
create_cave_region(multiworld, player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
create_cave_region(multiworld, player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
create_cave_region(multiworld, player, 'Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']),
create_cave_region(multiworld, player, 'Snitch Lady (East)', 'a boring house'),
create_cave_region(multiworld, player, 'Snitch Lady (West)', 'a boring house'),
create_cave_region(multiworld, player, 'Bush Covered House', 'the grass man'),
create_cave_region(multiworld, player, 'Tavern (Front)', 'the tavern'),
create_cave_region(multiworld, player, 'Light World Bomb Hut', 'a restock room'),
create_cave_region(multiworld, player, 'Kakariko Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Fortune Teller (Light)', 'a fortune teller'),
create_cave_region(multiworld, player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
create_cave_region(multiworld, player, 'Lumberjack House', 'a boring house'),
create_cave_region(multiworld, player, 'Bonk Fairy (Light)', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Swamp Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Chicken House', 'a house with a chest', ['Chicken House']),
create_cave_region(multiworld, player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
create_cave_region(multiworld, player, 'Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']),
create_cave_region(multiworld, player, 'Kakariko Well (top)', 'a drop\'s exit', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
create_cave_region(world, player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
create_cave_region(world, player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
create_lw_region(world, player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
create_cave_region(world, player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
create_cave_region(world, player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
create_cave_region(world, player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
create_lw_region(world, player, 'Hobo Bridge', ['Hobo']),
create_cave_region(world, player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']),
create_cave_region(world, player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']),
create_cave_region(world, player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']),
create_cave_region(world, player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
create_lw_region(world, player, 'Cave 45 Ledge', None, ['Cave 45']),
create_cave_region(world, player, 'Cave 45', 'a cave with an item', ['Cave 45']),
create_lw_region(world, player, 'Graveyard Ledge', None, ['Graveyard Cave']),
create_cave_region(world, player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
create_cave_region(world, player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
create_cave_region(world, player, 'Long Fairy Cave', 'a fairy fountain'),
create_cave_region(world, player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
create_cave_region(multiworld, player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
create_cave_region(multiworld, player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
create_lw_region(multiworld, player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
create_cave_region(multiworld, player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
create_cave_region(multiworld, player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
create_cave_region(multiworld, player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
create_lw_region(multiworld, player, 'Hobo Bridge', ['Hobo']),
create_cave_region(multiworld, player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']),
create_cave_region(multiworld, player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']),
create_cave_region(multiworld, player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']),
create_cave_region(multiworld, player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
create_lw_region(multiworld, player, 'Cave 45 Ledge', None, ['Cave 45']),
create_cave_region(multiworld, player, 'Cave 45', 'a cave with an item', ['Cave 45']),
create_lw_region(multiworld, player, 'Graveyard Ledge', None, ['Graveyard Cave']),
create_cave_region(multiworld, player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
create_cave_region(multiworld, player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
create_cave_region(multiworld, player, 'Long Fairy Cave', 'a fairy fountain'),
create_cave_region(multiworld, player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
create_cave_region(world, player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
create_cave_region(world, player, 'Good Bee Cave', 'a cold bee'),
create_cave_region(world, player, '20 Rupee Cave', 'a cave with some cash'),
create_cave_region(world, player, 'Cave Shop (Lake Hylia)', 'a common shop'),
create_cave_region(world, player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
create_cave_region(world, player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
create_cave_region(world, player, 'Library', 'the library', ['Library']),
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', ['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'),
create_lw_region(world, player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']),
create_lw_region(world, player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']),
create_lw_region(world, player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']),
create_lw_region(world, player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
create_lw_region(world, player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']),
create_dungeon_region(world, player, 'Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
create_cave_region(multiworld, player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
create_cave_region(multiworld, player, 'Good Bee Cave', 'a cold bee'),
create_cave_region(multiworld, player, '20 Rupee Cave', 'a cave with some cash'),
create_cave_region(multiworld, player, 'Cave Shop (Lake Hylia)', 'a common shop'),
create_cave_region(multiworld, player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
create_cave_region(multiworld, player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
create_cave_region(multiworld, player, 'Library', 'the library', ['Library']),
create_cave_region(multiworld, player, 'Kakariko Gamble Game', 'a game of chance'),
create_cave_region(multiworld, player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
create_lw_region(multiworld, player, 'Lake Hylia Island', ['Lake Hylia Island']),
create_cave_region(multiworld, player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade Shop']),
create_cave_region(multiworld, player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
create_lw_region(multiworld, player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']),
create_cave_region(multiworld, player, '50 Rupee Cave', 'a cave with some cash'),
create_lw_region(multiworld, player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']),
create_lw_region(multiworld, player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']),
create_lw_region(multiworld, player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']),
create_lw_region(multiworld, player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
create_lw_region(multiworld, player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']),
create_dungeon_region(multiworld, player, 'Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']),
create_dungeon_region(world, player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
create_dungeon_region(world, player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
create_dungeon_region(world, player, 'Desert Palace North', 'Desert Palace', ['Desert Palace - Desert Tiles 1 Pot Key', 'Desert Palace - Beamos Hall Pot Key', 'Desert Palace - Desert Tiles 2 Pot Key',
create_dungeon_region(multiworld, player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
create_dungeon_region(multiworld, player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
create_dungeon_region(multiworld, player, 'Desert Palace North', 'Desert Palace', ['Desert Palace - Desert Tiles 1 Pot Key', 'Desert Palace - Beamos Hall Pot Key', 'Desert Palace - Desert Tiles 2 Pot Key',
'Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
create_dungeon_region(world, player, 'Eastern Palace', 'Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest',
create_dungeon_region(multiworld, player, 'Eastern Palace', 'Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest',
'Eastern Palace - Dark Square Pot Key', 'Eastern Palace - Dark Eyegore Key Drop', 'Eastern Palace - Big Key Chest',
'Eastern Palace - Map Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize'], ['Eastern Palace Exit']),
create_lw_region(world, player, 'Master Sword Meadow', ['Master Sword Pedestal']),
create_cave_region(world, player, 'Lost Woods Gamble', 'a game of chance'),
create_lw_region(world, player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']),
create_lw_region(world, player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']),
create_dungeon_region(world, player, 'Hyrule Castle', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest',
create_lw_region(multiworld, player, 'Master Sword Meadow', ['Master Sword Pedestal']),
create_cave_region(multiworld, player, 'Lost Woods Gamble', 'a game of chance'),
create_lw_region(multiworld, player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']),
create_lw_region(multiworld, player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']),
create_dungeon_region(multiworld, player, 'Hyrule Castle', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest',
'Hyrule Castle - Map Guard Key Drop', 'Hyrule Castle - Boomerang Guard Key Drop', 'Hyrule Castle - Big Key Drop'],
['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', 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',
create_dungeon_region(multiworld, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
create_dungeon_region(multiworld, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross', 'Sewers - Key Rat Key Drop'], ['Sewers Door']),
create_dungeon_region(multiworld, player, 'Sewers', 'a drop\'s exit', None, ['Sanctuary Push Door', 'Sewers Back Door', 'Sewers Secret Room']),
create_dungeon_region(multiworld, 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),
create_cave_region(world, player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
create_cave_region(world, player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
create_cave_region(world, player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
create_lw_region(world, player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
create_cave_region(world, player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
create_lw_region(world, player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']),
create_cave_region(world, player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
create_cave_region(world, player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
create_cave_region(world, player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
create_lw_region(world, player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
create_cave_region(world, player, 'Hookshot Fairy', 'fairies deep in a cave'),
create_cave_region(world, player, 'Paradox Cave Front', 'a connector', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)', 'Light World Death Mountain Shop']),
create_cave_region(world, player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
create_dungeon_region(multiworld, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
create_dungeon_region(multiworld, 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(multiworld, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
create_cave_region(multiworld, player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
create_cave_region(multiworld, player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
create_cave_region(multiworld, player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
create_lw_region(multiworld, player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
create_cave_region(multiworld, player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
create_lw_region(multiworld, player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']),
create_cave_region(multiworld, player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
create_cave_region(multiworld, player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
create_cave_region(multiworld, player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
create_lw_region(multiworld, player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
create_cave_region(multiworld, player, 'Hookshot Fairy', 'fairies deep in a cave'),
create_cave_region(multiworld, player, 'Paradox Cave Front', 'a connector', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)', 'Light World Death Mountain Shop']),
create_cave_region(multiworld, player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
'Paradox Cave Lower - Left',
'Paradox Cave Lower - Right',
'Paradox Cave Lower - Far Right',
@@ -147,267 +147,267 @@ def create_regions(world, player):
'Paradox Cave Upper - Left',
'Paradox Cave Upper - Right'],
['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']),
create_cave_region(world, player, 'Paradox Cave', 'a connector', None,
create_cave_region(multiworld, player, 'Paradox Cave', 'a connector', None,
['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
create_cave_region(world, player, 'Light World Death Mountain Shop', 'a common shop'),
create_lw_region(world, player, 'East Death Mountain (Top)', None,
create_cave_region(multiworld, player, 'Light World Death Mountain Shop', 'a common shop'),
create_lw_region(multiworld, player, 'East Death Mountain (Top)', None,
['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access',
'East Death Mountain Drop', 'Turtle Rock Teleporter', 'Fairy Ascension Ledge']),
create_lw_region(world, player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']),
create_cave_region(world, player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'],
create_lw_region(multiworld, player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']),
create_cave_region(multiworld, player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'],
['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
create_cave_region(world, player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
create_lw_region(world, player, 'Fairy Ascension Plateau', None,
create_cave_region(multiworld, player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
create_lw_region(multiworld, player, 'Fairy Ascension Plateau', None,
['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
create_cave_region(world, player, 'Fairy Ascension Cave (Bottom)', 'a connector', None,
create_cave_region(multiworld, player, 'Fairy Ascension Cave (Bottom)', 'a connector', None,
['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']),
create_cave_region(world, player, 'Fairy Ascension Cave (Drop)', 'a connector', None,
create_cave_region(multiworld, player, 'Fairy Ascension Cave (Drop)', 'a connector', None,
['Fairy Ascension Cave Pots']),
create_cave_region(world, player, 'Fairy Ascension Cave (Top)', 'a connector', None,
create_cave_region(multiworld, player, 'Fairy Ascension Cave (Top)', 'a connector', None,
['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
create_lw_region(world, player, 'Fairy Ascension Ledge', None,
create_lw_region(multiworld, player, 'Fairy Ascension Ledge', None,
['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']),
create_lw_region(world, player, 'Death Mountain (Top)', ['Ether Tablet'],
create_lw_region(multiworld, player, 'Death Mountain (Top)', ['Ether Tablet'],
['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']),
create_lw_region(world, player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']),
create_dungeon_region(world, player, 'Tower of Hera (Bottom)', 'Tower of Hera',
create_lw_region(multiworld, player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']),
create_dungeon_region(multiworld, player, 'Tower of Hera (Bottom)', 'Tower of Hera',
['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'],
['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
create_dungeon_region(world, player, 'Tower of Hera (Basement)', 'Tower of Hera',
create_dungeon_region(multiworld, player, 'Tower of Hera (Basement)', 'Tower of Hera',
['Tower of Hera - Big Key Chest']),
create_dungeon_region(world, player, 'Tower of Hera (Top)', 'Tower of Hera',
create_dungeon_region(multiworld, player, 'Tower of Hera (Top)', 'Tower of Hera',
['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss',
'Tower of Hera - Prize']),
create_dw_region(world, player, 'East Dark World', ['Pyramid'],
create_dw_region(multiworld, player, 'East Dark World', ['Pyramid'],
['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness',
'Dark Lake Hylia Drop (East)',
'Hyrule Castle Ledge Mirror Spot', 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint',
'East Dark World Hint', 'Pyramid Hole', 'Northeast Dark World Broken Bridge Pass', ]),
create_dw_region(world, player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
create_dw_region(world, player, 'Northeast Dark World', None,
create_dw_region(multiworld, player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
create_dw_region(multiworld, player, 'Northeast Dark World', None,
['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass',
'Catfish Entrance Rock', 'Dark Lake Hylia Teleporter']),
create_cave_region(world, player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region(world, player, 'East Dark World Hint', 'a storyteller'),
create_dw_region(world, player, 'South Dark World', ['Stumpy', 'Digging Game'],
create_cave_region(multiworld, player, 'Palace of Darkness Hint', 'a storyteller'),
create_cave_region(multiworld, player, 'East Dark World Hint', 'a storyteller'),
create_dw_region(multiworld, player, 'South Dark World', ['Stumpy', 'Digging Game'],
['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock',
'Maze Race Mirror Spot',
'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game',
'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop',
'Bombos Tablet Mirror Spot']),
create_lw_region(world, player, 'Bombos Tablet Ledge', ['Bombos Tablet']),
create_cave_region(world, player, 'Big Bomb Shop', 'the bomb shop'),
create_cave_region(world, player, 'Archery Game', 'a game of skill'),
create_dw_region(world, player, 'Dark Lake Hylia', None,
create_lw_region(multiworld, player, 'Bombos Tablet Ledge', ['Bombos Tablet']),
create_cave_region(multiworld, player, 'Big Bomb Shop', 'the bomb shop'),
create_cave_region(multiworld, player, 'Archery Game', 'a game of skill'),
create_dw_region(multiworld, player, 'Dark Lake Hylia', None,
['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']),
create_dw_region(world, player, 'Dark Lake Hylia Central Island', None,
create_dw_region(multiworld, player, 'Dark Lake Hylia Central Island', None,
['Ice Palace', 'Lake Hylia Central Island Mirror Spot']),
create_dw_region(world, player, 'Dark Lake Hylia Ledge', None,
create_dw_region(multiworld, player, 'Dark Lake Hylia Ledge', None,
['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint',
'Dark Lake Hylia Ledge Spike Cave']),
create_cave_region(world, player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
create_cave_region(world, player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
create_cave_region(world, player, 'Hype Cave', 'a bounty of five items',
create_cave_region(multiworld, player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
create_cave_region(multiworld, player, 'Hype Cave', 'a bounty of five items',
['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
create_dw_region(world, player, 'West Dark World', ['Frog'],
create_dw_region(multiworld, player, 'West Dark World', ['Frog'],
['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House',
'Chest Game', 'Thieves Town', 'Graveyard Ledge Mirror Spot', 'Kings Grave Mirror Spot',
'Bumper Cave Entrance Rock',
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks',
'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)',
'Dark World Lumberjack Shop']),
create_dw_region(world, player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Village of Outcasts Shop']),
create_dw_region(world, player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'],
create_dw_region(multiworld, player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Village of Outcasts Shop']),
create_dw_region(multiworld, player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'],
['Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Peg Area Rocks']),
create_dw_region(world, player, 'Bumper Cave Entrance', None,
create_dw_region(multiworld, player, 'Bumper Cave Entrance', None,
['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']),
create_cave_region(world, player, 'Fortune Teller (Dark)', 'a fortune teller'),
create_cave_region(world, player, 'Village of Outcasts Shop', 'a common shop'),
create_cave_region(world, player, 'Dark Lake Hylia Shop', 'a common shop'),
create_cave_region(world, player, 'Dark World Lumberjack Shop', 'a common shop'),
create_cave_region(world, player, 'Dark World Potion Shop', 'a common shop'),
create_cave_region(world, player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
create_cave_region(world, player, 'Pyramid Fairy', 'a cave with two chests',
create_cave_region(multiworld, player, 'Fortune Teller (Dark)', 'a fortune teller'),
create_cave_region(multiworld, player, 'Village of Outcasts Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark Lake Hylia Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark World Lumberjack Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark World Potion Shop', 'a common shop'),
create_cave_region(multiworld, player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
create_cave_region(multiworld, player, 'Pyramid Fairy', 'a cave with two chests',
['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
create_cave_region(world, player, 'Brewery', 'a house with a chest', ['Brewery']),
create_cave_region(world, player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
create_cave_region(world, player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
create_cave_region(world, player, 'Red Shield Shop', 'the rare shop'),
create_cave_region(world, player, 'Dark Sanctuary Hint', 'a storyteller'),
create_cave_region(world, player, 'Bumper Cave', 'a connector', None,
create_cave_region(multiworld, player, 'Brewery', 'a house with a chest', ['Brewery']),
create_cave_region(multiworld, player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
create_cave_region(multiworld, player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
create_cave_region(multiworld, player, 'Red Shield Shop', 'the rare shop'),
create_cave_region(multiworld, player, 'Dark Sanctuary Hint', 'a storyteller'),
create_cave_region(multiworld, player, 'Bumper Cave', 'a connector', None,
['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
create_dw_region(world, player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'],
create_dw_region(multiworld, player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'],
['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']),
create_dw_region(world, player, 'Skull Woods Forest', None,
create_dw_region(multiworld, player, 'Skull Woods Forest', None,
['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)',
'Skull Woods First Section Hole (North)',
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
create_dw_region(world, player, 'Skull Woods Forest (West)', None,
create_dw_region(multiworld, player, 'Skull Woods Forest (West)', None,
['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)',
'Skull Woods Final Section']),
create_dw_region(world, player, 'Dark Desert', None,
create_dw_region(multiworld, player, 'Dark Desert', None,
['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot',
'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot',
'Desert Palace Entrance (North) Mirror Spot', 'Dark Desert Hint', 'Dark Desert Fairy']),
create_cave_region(world, player, 'Mire Shed', 'a cave with two chests',
create_cave_region(multiworld, player, 'Mire Shed', 'a cave with two chests',
['Mire Shed - Left', 'Mire Shed - Right']),
create_cave_region(world, player, 'Dark Desert Hint', 'a storyteller'),
create_dw_region(world, player, 'Dark Death Mountain (West Bottom)', None,
create_cave_region(multiworld, player, 'Dark Desert Hint', 'a storyteller'),
create_dw_region(multiworld, player, 'Dark Death Mountain (West Bottom)', None,
['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
create_dw_region(world, player, 'Dark Death Mountain (Top)', None,
create_dw_region(multiworld, player, 'Dark Death Mountain (Top)', None,
['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower',
'Superbunny Cave (Top)',
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
create_dw_region(world, player, 'Dark Death Mountain Ledge', None,
create_dw_region(multiworld, player, 'Dark Death Mountain Ledge', None,
['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)',
'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
create_dw_region(world, player, 'Dark Death Mountain Isolated Ledge', None,
create_dw_region(multiworld, player, 'Dark Death Mountain Isolated Ledge', None,
['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
create_dw_region(world, player, 'Dark Death Mountain (East Bottom)', None,
create_dw_region(multiworld, player, 'Dark Death Mountain (East Bottom)', None,
['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)',
'Fairy Ascension Mirror Spot']),
create_cave_region(world, player, 'Superbunny Cave (Top)', 'a connector',
create_cave_region(multiworld, player, 'Superbunny Cave (Top)', 'a connector',
['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
create_cave_region(world, player, 'Superbunny Cave (Bottom)', 'a connector', None,
create_cave_region(multiworld, player, 'Superbunny Cave (Bottom)', 'a connector', None,
['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
create_cave_region(world, player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
create_cave_region(world, player, 'Hookshot Cave', 'a connector',
create_cave_region(multiworld, player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
create_cave_region(multiworld, 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 Bomb Wall (South)']),
create_cave_region(world, player, 'Hookshot Cave (Upper)', 'a connector', None, ['Hookshot Cave Exit (North)',
create_cave_region(multiworld, 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,
create_dw_region(multiworld, 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']),
create_dw_region(world, player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']),
create_lw_region(world, player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
create_cave_region(world, player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
create_lw_region(multiworld, player, 'Death Mountain Floating Island (Light World)', ['Floating Island']),
create_dw_region(multiworld, player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']),
create_lw_region(multiworld, player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
create_cave_region(multiworld, player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
create_dungeon_region(world, player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
create_dungeon_region(world, player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
create_dungeon_region(world, player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest', 'Swamp Palace - Pot Row Pot Key',
create_dungeon_region(multiworld, player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
create_dungeon_region(multiworld, player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
create_dungeon_region(multiworld, player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest', 'Swamp Palace - Pot Row Pot Key',
'Swamp Palace - Trench 1 Pot Key'], ['Swamp Palace (Center)']),
create_dungeon_region(world, player, 'Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest', 'Swamp Palace - Hookshot Pot Key',
create_dungeon_region(multiworld, player, 'Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest', 'Swamp Palace - Hookshot Pot Key',
'Swamp Palace - Trench 2 Pot Key'], ['Swamp Palace (North)', 'Swamp Palace (West)']),
create_dungeon_region(world, player, 'Swamp Palace (West)', 'Swamp Palace', ['Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest']),
create_dungeon_region(world, player, 'Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
create_dungeon_region(multiworld, player, 'Swamp Palace (West)', 'Swamp Palace', ['Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest']),
create_dungeon_region(multiworld, player, 'Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
'Swamp Palace - Waterway Pot Key', 'Swamp Palace - Waterfall Room',
'Swamp Palace - Boss', 'Swamp Palace - Prize']),
create_dungeon_region(world, player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest',
create_dungeon_region(multiworld, player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest',
'Thieves\' Town - Map Chest',
'Thieves\' Town - Compass Chest',
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
create_dungeon_region(world, player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
create_dungeon_region(multiworld, player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
'Thieves\' Town - Big Chest',
'Thieves\' Town - Hallway Pot Key',
'Thieves\' Town - Spike Switch Pot Key',
'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']),
create_dungeon_region(world, player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
create_dungeon_region(world, player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
create_dungeon_region(world, player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
create_dungeon_region(world, player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
create_dungeon_region(world, player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
create_dungeon_region(world, player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
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 - 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',
create_dungeon_region(multiworld, player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
create_dungeon_region(multiworld, player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
create_dungeon_region(multiworld, player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
create_dungeon_region(multiworld, 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(multiworld, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
create_dungeon_region(multiworld, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop', 'Skull Woods - Boss', 'Skull Woods - Prize']),
create_dungeon_region(multiworld, 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(multiworld, player, 'Ice Palace (Second Section)', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Palace (Main)']),
create_dungeon_region(multiworld, 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)']),
create_dungeon_region(world, player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
create_dungeon_region(world, player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest', 'Ice Palace - Hammer Block Key Drop']),
create_dungeon_region(world, player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
create_dungeon_region(world, player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
create_dungeon_region(world, player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
create_dungeon_region(multiworld, player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
create_dungeon_region(multiworld, player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest', 'Ice Palace - Hammer Block Key Drop']),
create_dungeon_region(multiworld, player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
create_dungeon_region(multiworld, player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
create_dungeon_region(multiworld, player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest',
'Misery Mire - Spikes Pot Key', 'Misery Mire - Fishbone Pot Key',
'Misery Mire - Conveyor Crystal Key Drop'], ['Misery Mire (West)', 'Misery Mire Big Key Door']),
create_dungeon_region(world, player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
create_dungeon_region(world, player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
create_dungeon_region(world, player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
create_dungeon_region(world, player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
create_dungeon_region(world, player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
create_dungeon_region(multiworld, player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
create_dungeon_region(multiworld, player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
create_dungeon_region(multiworld, player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
'Turtle Rock - Roller Room - Right'],
['Turtle Rock Entrance to Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
create_dungeon_region(world, player, 'Turtle Rock (Pokey Room)', 'Turtle Rock', ['Turtle Rock - Pokey 1 Key Drop'], ['Turtle Rock (Pokey Room) (North)', 'Turtle Rock (Pokey Room) (South)']),
create_dungeon_region(world, player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
create_dungeon_region(world, player, 'Turtle Rock (Second Section)', 'Turtle Rock', ['Turtle Rock - Big Key Chest', 'Turtle Rock - Pokey 2 Key Drop'], ['Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door', 'Turtle Rock Second Section Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Second Section Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Second Section from Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
create_dungeon_region(world, player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
create_dungeon_region(world, player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
create_dungeon_region(world, player, 'Turtle Rock (Eye Bridge Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Isolated Ledge Exit', 'Turtle Rock Eye Bridge from Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
create_dungeon_region(multiworld, player, 'Turtle Rock (Pokey Room)', 'Turtle Rock', ['Turtle Rock - Pokey 1 Key Drop'], ['Turtle Rock (Pokey Room) (North)', 'Turtle Rock (Pokey Room) (South)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Second Section)', 'Turtle Rock', ['Turtle Rock - Big Key Chest', 'Turtle Rock - Pokey 2 Key Drop'], ['Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door', 'Turtle Rock Second Section Bomb Wall']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Second Section Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Second Section from Bomb Wall']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Eye Bridge Bomb Wall)', 'Turtle Rock', None, ['Turtle Rock Isolated Ledge Exit', 'Turtle Rock Eye Bridge from Bomb Wall']),
create_dungeon_region(multiworld, player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Eye Bridge Bomb Wall']),
create_dungeon_region(world, player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
create_dungeon_region(world, player, 'Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
create_dungeon_region(world, player, 'Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
create_dungeon_region(multiworld, player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']),
create_dungeon_region(world, player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']),
create_dungeon_region(world, player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
create_dungeon_region(world, player, 'Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
create_dungeon_region(multiworld, player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']),
create_dungeon_region(world, player, 'Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
create_dungeon_region(world, player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']),
create_dungeon_region(world, player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
create_dungeon_region(world, player, 'Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left',
create_dungeon_region(multiworld, player, 'Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']),
create_dungeon_region(multiworld, player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left',
'Ganons Tower - Hope Room - Right', 'Ganons Tower - Conveyor Cross Pot Key'],
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']),
create_dungeon_region(world, player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
create_dungeon_region(world, player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower', ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right',
create_dungeon_region(multiworld, player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower', ['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'],
['Ganons Tower (Bottom) (East)']),
create_dungeon_region(world, player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
create_dungeon_region(multiworld, player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right',
'Ganons Tower - Double Switch Pot Key'],
['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']),
create_dungeon_region(world, player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
create_dungeon_region(world, player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
create_dungeon_region(world, player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right',
create_dungeon_region(multiworld, player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right',
'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'],
['Ganons Tower (Bottom) (West)']),
create_dungeon_region(world, player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left',
create_dungeon_region(multiworld, player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower', ['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']),
create_dungeon_region(world, player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']),
create_dungeon_region(world, player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right',
create_dungeon_region(multiworld, player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']),
create_dungeon_region(multiworld, player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right',
'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Mini Helmasaur Key Drop'], ['Ganons Tower Moldorm Door']),
create_dungeon_region(world, player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']),
create_dungeon_region(world, player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_cave_region(world, player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
create_cave_region(world, player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
create_dw_region(world, player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']),
create_lw_region(world, player, 'Desert Northern Cliffs'),
create_dw_region(world, player, 'Dark Death Mountain Bunny Descent Area')
create_dungeon_region(multiworld, player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']),
create_dungeon_region(multiworld, player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
create_cave_region(multiworld, player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
create_cave_region(multiworld, player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
create_dw_region(multiworld, player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']),
create_lw_region(multiworld, player, 'Desert Northern Cliffs'),
create_dw_region(multiworld, player, 'Dark Death Mountain Bunny Descent Area')
]
def create_lw_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
return _create_region(world, player, name, LTTPRegionType.LightWorld, 'Light World', locations, exits)
def create_lw_region(multiworld: MultiWorld, player: int, name: str, locations=None, exits=None):
return _create_region(multiworld, player, name, LTTPRegionType.LightWorld, 'Light World', locations, exits)
def create_dw_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
return _create_region(world, player, name, LTTPRegionType.DarkWorld, 'Dark World', locations, exits)
def create_dw_region(multiworld: MultiWorld, player: int, name: str, locations=None, exits=None):
return _create_region(multiworld, player, name, LTTPRegionType.DarkWorld, 'Dark World', locations, exits)
def create_cave_region(world: MultiWorld, player: int, name: str, hint: str, locations=None, exits=None):
return _create_region(world, player, name, LTTPRegionType.Cave, hint, locations, exits)
def create_cave_region(multiworld: MultiWorld, player: int, name: str, hint: str, locations=None, exits=None):
return _create_region(multiworld, player, name, LTTPRegionType.Cave, hint, locations, exits)
def create_dungeon_region(world: MultiWorld, player: int, name: str, hint: str, locations=None, exits=None):
return _create_region(world, player, name, LTTPRegionType.Dungeon, hint, locations, exits)
def create_dungeon_region(multiworld: MultiWorld, player: int, name: str, hint: str, locations=None, exits=None):
return _create_region(multiworld, player, name, LTTPRegionType.Dungeon, hint, locations, exits)
def _create_region(world: MultiWorld, player: int, name: str, type: LTTPRegionType, hint: str, locations=None,
def _create_region(multiworld: MultiWorld, player: int, name: str, type: LTTPRegionType, hint: str, locations=None,
exits=None):
from .SubClasses import ALttPLocation
ret = LTTPRegion(name, type, hint, player, world)
ret = LTTPRegion(name, type, hint, player, multiworld)
if exits:
for exit in exits:
ret.create_exit(exit)
@@ -422,10 +422,10 @@ def _create_region(world: MultiWorld, player: int, name: str, type: LTTPRegionTy
return ret
def mark_light_world_regions(world, player: int):
def mark_light_world_regions(multiworld: MultiWorld, player: int):
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
# That is ok. the bunny logic will check for this case and incorporate special rules.
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.LightWorld)
queue = collections.deque(region for region in multiworld.get_regions(player) if region.type == LTTPRegionType.LightWorld)
seen = set(queue)
while queue:
current = queue.popleft()
@@ -438,7 +438,7 @@ def mark_light_world_regions(world, player: int):
seen.add(exit.connected_region)
queue.append(exit.connected_region)
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.DarkWorld)
queue = collections.deque(region for region in multiworld.get_regions(player) if region.type == LTTPRegionType.DarkWorld)
seen = set(queue)
while queue:
current = queue.popleft()

View File

@@ -19,7 +19,7 @@ import subprocess
import threading
import concurrent.futures
import bsdiff4
from typing import Collection, Optional, List, SupportsIndex
from typing import Collection, Optional, List, SupportsIndex, TYPE_CHECKING
from BaseClasses import CollectionState, Region, Location, MultiWorld
from Utils import local_path, user_path, int16_as_bytes, int32_as_bytes, snes_to_pc, is_frozen, parse_yaml, read_snes_rom
@@ -39,6 +39,9 @@ from .Items import item_table, item_name_groups, progression_items
from .EntranceShuffle import door_addresses
from .Options import small_key_shuffle
if TYPE_CHECKING:
from . import ALTTPWorld
try:
from maseya import z3pr
from maseya.z3pr.palette_randomizer import build_offset_collections
@@ -792,13 +795,13 @@ def get_nonnative_item_sprite(code: int) -> int:
# https://discord.com/channels/731205301247803413/827141303330406408/852102450822905886
def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
local_random = world.worlds[player].random
local_world = world.worlds[player]
def patch_rom(multiworld: MultiWorld, rom: LocalRom, player: int, enemized: bool):
local_random = multiworld.worlds[player].random
local_world = multiworld.worlds[player]
# patch items
for location in world.get_locations(player):
for location in multiworld.get_locations(player):
if location.address is None or location.shop_slot is not None:
continue
@@ -852,7 +855,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_byte(0x155C9, local_random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle
# patch entrance/exits/holes
for region in world.get_regions(player):
for region in multiworld.get_regions(player):
for exit in region.exits:
if exit.target is not None:
if isinstance(exit.addresses, tuple):
@@ -885,7 +888,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_int16(0x15DB5 + 2 * offset, 0x0640)
elif room_id == 0x00d6 and local_world.fix_trock_exit:
rom.write_int16(0x15DB5 + 2 * offset, 0x0134)
elif room_id == 0x000c and world.shuffle_ganon: # fix ganons tower exit point
elif room_id == 0x000c and multiworld.shuffle_ganon: # fix ganons tower exit point
rom.write_int16(0x15DB5 + 2 * offset, 0x00A4)
else:
rom.write_int16(0x15DB5 + 2 * offset, link_y)
@@ -905,9 +908,9 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# patch door table
rom.write_byte(0xDBB73 + exit.addresses, exit.target)
if local_world.options.mode == 'inverted':
patch_shuffled_dark_sanc(world, rom, player)
patch_shuffled_dark_sanc(multiworld, rom, player)
write_custom_shops(rom, world, player)
write_custom_shops(rom, multiworld, player)
def credits_digit(num):
# top: $54 is 1, 55 2, etc , so 57=4, 5C=9
@@ -981,11 +984,11 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
if local_world.options.mode in ['open', 'inverted']:
rom.write_byte(0x180032, 0x01) # open mode
if local_world.options.mode == 'inverted':
set_inverted_mode(world, player, rom)
set_inverted_mode(multiworld, player, rom)
elif local_world.options.mode == 'standard':
rom.write_byte(0x180032, 0x00) # standard mode
uncle_location = world.get_location('Link\'s Uncle', player)
uncle_location = multiworld.get_location('Link\'s Uncle', player)
if uncle_location.item is None or uncle_location.item.name not in ['Master Sword', 'Tempered Sword',
'Fighter Sword', 'Golden Sword',
'Progressive Sword']:
@@ -1280,7 +1283,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# set up goals for treasure hunt
rom.write_int16(0x180163, max(0, local_world.treasure_hunt_required -
sum(1 for item in world.precollected_items[player] if item.name == "Triforce Piece")))
sum(1 for item in multiworld.precollected_items[player] if item.name == "Triforce Piece")))
rom.write_bytes(0x180165, [0x0E, 0x28]) # Triforce Piece Sprite
rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled)
@@ -1309,7 +1312,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest
rom.write_byte(0x50599, 0x00) # disable below ganon chest
rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest
rom.write_byte(0x18008B, 0x01 if local_world.options.open_pyramid.to_bool(world, player) else 0x00) # pre-open Pyramid Hole
rom.write_byte(0x18008B, 0x01 if local_world.options.open_pyramid.to_bool(multiworld, player) else 0x00) # pre-open Pyramid Hole
rom.write_byte(0x18008C, 0x01 if local_world.options.crystals_needed_for_gt == 0 else 0x00) # GT pre-opened if crystal requirement is 0
rom.write_byte(0xF5D73, 0xF0) # bees are catchable
rom.write_byte(0xF5F10, 0xF0) # bees are catchable
@@ -1327,7 +1330,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
starting_max_bombs = 0 if local_world.options.bombless_start else 10
starting_max_arrows = 30
startingstate = CollectionState(world)
startingstate = CollectionState(multiworld)
if startingstate.has('Silver Bow', player):
equip[0x340] = 1
@@ -1375,7 +1378,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
equip[0x37B] = 1
equip[0x36E] = 0x80
for item in world.precollected_items[player]:
for item in multiworld.precollected_items[player]:
if item.name in {'Bow', 'Silver Bow', 'Silver Arrows', 'Progressive Bow', 'Progressive Bow (Alt)',
'Titans Mitts', 'Power Glove', 'Progressive Glove',
@@ -1590,7 +1593,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
}
def get_reveal_bytes(itemName):
locations = world.find_item_locations(itemName, player)
locations = multiworld.find_item_locations(itemName, player)
if len(locations) < 1:
return 0x0000
location = locations[0]
@@ -1667,7 +1670,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_byte(0x18004C, 0x01)
# set correct flag for hera basement item
hera_basement = world.get_location('Tower of Hera - Basement Cage', player)
hera_basement = multiworld.get_location('Tower of Hera - Basement Cage', player)
if hera_basement.item is not None and hera_basement.item.name == 'Small Key (Tower of Hera)' and hera_basement.item.player == player:
rom.write_byte(0x4E3BB, 0xE4)
else:
@@ -1684,12 +1687,12 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
rom.write_byte(0xFEE41, 0x2A) # bombable exit
if local_world.options.tile_shuffle:
tile_set = TileSet.get_random_tile_set(world.worlds[player].random)
tile_set = TileSet.get_random_tile_set(multiworld.worlds[player].random)
rom.write_byte(0x4BA21, tile_set.get_speed())
rom.write_byte(0x4BA1D, tile_set.get_len())
rom.write_bytes(0x4BA2A, tile_set.get_bytes())
write_strings(rom, world, player)
write_strings(rom, multiworld, player)
# remote items flag, does not currently work
rom.write_byte(0x18637C, 0)
@@ -1697,14 +1700,14 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
# set rom name
# 21 bytes
from Utils import __version__
rom.name = bytearray(f'AP{__version__.replace(".", "")[0:3]}_{player}_{world.seed:11}\0', 'utf8')[:21]
rom.name = bytearray(f'AP{__version__.replace(".", "")[0:3]}_{player}_{multiworld.seed:11}\0', 'utf8')[:21]
rom.name.extend([0] * (21 - len(rom.name)))
rom.write_bytes(0x7FC0, rom.name)
# set player names
encoded_players = world.players + len(world.groups)
encoded_players = multiworld.players + len(multiworld.groups)
for p in range(1, min(encoded_players, ROM_PLAYER_LIMIT) + 1):
rom.write_bytes(0x195FFC + ((p - 1) * 32), hud_format_text(world.player_name[p]))
rom.write_bytes(0x195FFC + ((p - 1) * 32), hud_format_text(multiworld.player_name[p]))
if encoded_players > ROM_PLAYER_LIMIT:
rom.write_bytes(0x195FFC + ((ROM_PLAYER_LIMIT - 1) * 32), hud_format_text("Archipelago"))
@@ -1723,9 +1726,9 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
return rom
def patch_race_rom(rom, world, player):
def patch_race_rom(rom: LocalRom, multiworld: MultiWorld, player: int):
rom.write_bytes(0x180213, [0x01, 0x00]) # Tournament Seed
rom.encrypt(world, player)
rom.encrypt(multiworld, player)
def get_price_data(price: int, price_type: int) -> List[int]:
@@ -1738,8 +1741,8 @@ def get_price_data(price: int, price_type: int) -> List[int]:
return int16_as_bytes(price)
def write_custom_shops(rom, world, player):
shops = sorted([shop for shop in world.worlds[player].shops if shop.custom], key=lambda shop: shop.sram_offset)
def write_custom_shops(rom: LocalRom, multiworld: MultiWorld, player: int):
shops = sorted([shop for shop in multiworld.worlds[player].shops if shop.custom], key=lambda shop: shop.sram_offset)
shop_data = bytearray()
items_data = bytearray()
@@ -1758,9 +1761,9 @@ def write_custom_shops(rom, world, player):
slot = 0 if shop.type == ShopType.TakeAny else index
if item is None:
break
if world.worlds[player].options.shop_item_slots or shop.type == ShopType.TakeAny:
count_shop = (shop.region.name != 'Potion Shop' or world.worlds[player].options.include_witch_hut) and \
(shop.region.name != 'Capacity Upgrade' or world.worlds[player].options.shuffle_capacity_upgrades)
if multiworld.worlds[player].options.shop_item_slots or shop.type == ShopType.TakeAny:
count_shop = (shop.region.name != 'Potion Shop' or multiworld.worlds[player].options.include_witch_hut) and \
(shop.region.name != 'Capacity Upgrade' or multiworld.worlds[player].options.shuffle_capacity_upgrades)
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
@@ -1773,11 +1776,11 @@ def write_custom_shops(rom, world, player):
price_data = get_price_data(item['price'], item["price_type"])
replacement_price_data = get_price_data(item['replacement_price'], item['replacement_price_type'])
slot = 0 if shop.type == ShopType.TakeAny else index
if item['player'] and world.game[item['player']] != "A Link to the Past": # item not native to ALTTP
item_code = get_nonnative_item_sprite(world.worlds[item['player']].item_name_to_id[item['item']])
if item['player'] and multiworld.game[item['player']] != "A Link to the Past": # item not native to ALTTP
item_code = get_nonnative_item_sprite(multiworld.worlds[item['player']].item_name_to_id[item['item']])
else:
item_code = item_table[item["item"]].item_code
if item['item'] == 'Single Arrow' and item['player'] == 0 and world.worlds[player].options.retro_bow:
if item['item'] == 'Single Arrow' and item['player'] == 0 and multiworld.worlds[player].options.retro_bow:
rom.write_byte(0x186500 + shop.sram_offset + slot, arrow_mask)
item_data = [shop_id, item_code] + price_data + \
@@ -1790,12 +1793,12 @@ def write_custom_shops(rom, world, player):
items_data.extend([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
rom.write_bytes(0x184900, items_data)
if world.worlds[player].options.retro_bow:
if multiworld.worlds[player].options.retro_bow:
retro_shop_slots.append(0xFF)
rom.write_bytes(0x186540, retro_shop_slots)
def hud_format_text(text):
def hud_format_text(text: str):
output = bytes()
for char in text.lower():
if 'a' <= char <= 'z':
@@ -1812,7 +1815,7 @@ def hud_format_text(text):
output += b'\x7f\x00'
return output[:32]
def apply_oof_sfx(rom, oof: str):
def apply_oof_sfx(rom: LocalRom, oof: str):
with open(oof, 'rb') as stream:
oof_bytes = bytearray(stream.read())
@@ -1862,9 +1865,10 @@ def apply_oof_sfx(rom, oof: str):
rom.write_bytes(0x13000D, [0x00, 0x00, 0x00, 0x08])
def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, oof: str, palettes_options,
world=None, player=1, allow_random_on_event=False, reduceflashing=False,
triforcehud: str = None, deathlink: bool = False, allowcollect: bool = False):
def apply_rom_settings(rom: LocalRom, beep: str, color: str, quickswap: bool, menuspeed: str, music: bool, sprite: str,
oof: str, palettes_options: dict[str, str], world: "ALTTPWorld | None" = None, player: int = 1,
allow_random_on_event: bool = False, reduceflashing: bool = False, triforcehud: str = None,
deathlink: bool = False, allowcollect: bool = False):
local_random = random if not world else world.worlds[player].random
disable_music: bool = not music
# enable instant item menu
@@ -1948,7 +1952,7 @@ def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, spri
rom.write_byte(0x180167, triforce_flag)
if z3pr:
def buildAndRandomize(option_name, mode):
def buildAndRandomize(option_name: str, mode: str):
options = {
option_name: True
}
@@ -2012,7 +2016,7 @@ def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, spri
rom.write_crc()
def restore_maseya_colors(rom, offsets_array):
def restore_maseya_colors(rom: LocalRom, offsets_array: list[list[int]]):
if not rom.orig_buffer:
return
for offsetC in offsets_array:
@@ -2020,7 +2024,7 @@ def restore_maseya_colors(rom, offsets_array):
rom.write_bytes(address, rom.orig_buffer[address:address + 2])
def set_color(rom, address, color, shade):
def set_color(rom: LocalRom, address: int, color: tuple[int, int, int], shade: int):
r = round(min(color[0], 0xFF) * pow(0.8, shade) * 0x1F / 0xFF)
g = round(min(color[1], 0xFF) * pow(0.8, shade) * 0x1F / 0xFF)
b = round(min(color[2], 0xFF) * pow(0.8, shade) * 0x1F / 0xFF)
@@ -2028,7 +2032,7 @@ def set_color(rom, address, color, shade):
rom.write_bytes(address, ((b << 10) | (g << 5) | (r << 0)).to_bytes(2, byteorder='little', signed=False))
def default_ow_palettes(rom):
def default_ow_palettes(rom: LocalRom):
if not rom.orig_buffer:
return
rom.write_bytes(0xDE604, rom.orig_buffer[0xDE604:0xDEBB4])
@@ -2037,7 +2041,7 @@ def default_ow_palettes(rom):
rom.write_bytes(address, rom.orig_buffer[address:address + 2])
def randomize_ow_palettes(rom, local_random):
def randomize_ow_palettes(rom: LocalRom, local_random: random.Random):
grass, grass2, grass3, dirt, dirt2, water, clouds, dwdirt, \
dwgrass, dwwater, dwdmdirt, dwdmgrass, dwdmclouds1, dwdmclouds2 = [[local_random.randint(60, 215) for _ in range(3)]
for _ in range(14)]
@@ -2113,7 +2117,7 @@ def randomize_ow_palettes(rom, local_random):
set_color(rom, address, color, shade)
def blackout_ow_palettes(rom):
def blackout_ow_palettes(rom: LocalRom):
rom.write_bytes(0xDE604, [0] * 0xC4)
for i in range(0xDE6C8, 0xDE86C, 70):
rom.write_bytes(i, [0] * 64)
@@ -2124,13 +2128,13 @@ def blackout_ow_palettes(rom):
rom.write_bytes(address, [0, 0])
def default_uw_palettes(rom):
def default_uw_palettes(rom: LocalRom):
if not rom.orig_buffer:
return
rom.write_bytes(0xDD734, rom.orig_buffer[0xDD734:0xDE544])
def randomize_uw_palettes(rom, local_random):
def randomize_uw_palettes(rom: LocalRom, local_random: random.Random):
for dungeon in range(20):
wall, pot, chest, floor1, floor2, floor3 = [[local_random.randint(60, 240) for _ in range(3)] for _ in range(6)]
@@ -2177,7 +2181,7 @@ def randomize_uw_palettes(rom, local_random):
set_color(rom, 0x0DD796 + (0xB4 * dungeon), floor3, 4)
def blackout_uw_palettes(rom):
def blackout_uw_palettes(rom: LocalRom):
for i in range(0xDD734, 0xDE544, 180):
rom.write_bytes(i, [0] * 38)
rom.write_bytes(i + 44, [0] * 76)
@@ -2188,25 +2192,25 @@ def get_hash_string(hash):
return ", ".join([hash_alphabet[code & 0x1F] for code in hash])
def write_string_to_rom(rom, target, string):
def write_string_to_rom(rom: LocalRom, target: str, string: str):
address, maxbytes = text_addresses[target]
rom.write_bytes(address, MultiByteTextMapper.convert(string, maxbytes))
def write_strings(rom, world, player):
def write_strings(rom: LocalRom, multiworld: MultiWorld, player: int):
from . import ALTTPWorld
local_random = world.worlds[player].random
w: ALTTPWorld = world.worlds[player]
local_random = multiworld.worlds[player].random
w: ALTTPWorld = multiworld.worlds[player]
tt = TextTable()
tt.removeUnwantedText()
# Let's keep this guy's text accurate to the shuffle setting.
if world.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle 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.'
if world.worlds[player].options.mode == 'inverted':
if multiworld.worlds[player].options.mode == 'inverted':
tt['sign_village_of_outcasts'] = 'attention\nferal ducks sighted\nhiding in statues\n\nflute players beware\n'
def hint_text(dest, ped_hint=False):
@@ -2218,45 +2222,45 @@ def write_strings(rom, world, player):
hint = dest.hint_text
if dest.player != player:
if ped_hint:
hint += f" for {world.player_name[dest.player]}!"
hint += f" for {multiworld.player_name[dest.player]}!"
elif isinstance(dest, (Region, Location)):
hint += f" in {world.player_name[dest.player]}'s world"
hint += f" in {multiworld.player_name[dest.player]}'s world"
else:
hint += f" for {world.player_name[dest.player]}"
hint += f" for {multiworld.player_name[dest.player]}"
return hint
if world.worlds[player].options.scams.gives_king_zora_hint:
if multiworld.worlds[player].options.scams.gives_king_zora_hint:
# Zora hint
zora_location = world.get_location("King Zora", player)
zora_location = multiworld.get_location("King Zora", player)
tt['zora_tells_cost'] = f"You got 500 rupees to buy {hint_text(zora_location.item)}" \
f"\n ≥ Duh\n Oh carp\n{{CHOICE}}"
if world.worlds[player].options.scams.gives_bottle_merchant_hint:
if multiworld.worlds[player].options.scams.gives_bottle_merchant_hint:
# Bottle Vendor hint
vendor_location = world.get_location("Bottle Merchant", player)
vendor_location = multiworld.get_location("Bottle Merchant", player)
tt['bottle_vendor_choice'] = f"I gots {hint_text(vendor_location.item)}\nYous gots 100 rupees?" \
f"\n ≥ I want\n no way!\n{{CHOICE}}"
# First we write hints about entrances, some from the inconvenient list others from all reasonable entrances.
if world.worlds[player].options.hints:
if world.worlds[player].options.hints.value >= 2:
if world.worlds[player].options.hints == "full":
if multiworld.worlds[player].options.hints:
if multiworld.worlds[player].options.hints.value >= 2:
if multiworld.worlds[player].options.hints == "full":
tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles have hints!'
else:
tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles can have hints!'
hint_locations = HintLocations.copy()
local_random.shuffle(hint_locations)
all_entrances = list(world.get_entrances(player))
all_entrances = list(multiworld.get_entrances(player))
local_random.shuffle(all_entrances)
# First we take care of the one inconvenient dungeon in the appropriately simple shuffles.
entrances_to_hint = {}
entrances_to_hint.update(InconvenientDungeonEntrances)
if world.shuffle_ganon:
if world.worlds[player].options.mode == 'inverted':
if multiworld.shuffle_ganon:
if multiworld.worlds[player].options.mode == 'inverted':
entrances_to_hint.update({'Inverted Ganons Tower': 'The sealed castle door'})
else:
entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'})
if world.worlds[player].options.entrance_shuffle in ['simple', 'restricted']:
if multiworld.worlds[player].options.entrance_shuffle 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(
@@ -2266,9 +2270,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.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
hint_count = 0
elif world.worlds[player].options.entrance_shuffle in ['simple', 'restricted']:
elif multiworld.worlds[player].options.entrance_shuffle in ['simple', 'restricted']:
hint_count = 2
else:
hint_count = 4
@@ -2285,31 +2289,31 @@ def write_strings(rom, world, player):
# Next we handle hints for randomly selected other entrances,
# curating the selection intelligently based on shuffle.
if world.worlds[player].options.entrance_shuffle not in ['simple', 'restricted']:
if multiworld.worlds[player].options.entrance_shuffle not in ['simple', 'restricted']:
entrances_to_hint.update(ConnectorEntrances)
entrances_to_hint.update(DungeonEntrances)
if world.worlds[player].options.mode == 'inverted':
if multiworld.worlds[player].options.mode == '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.worlds[player].options.entrance_shuffle == 'restricted':
elif multiworld.worlds[player].options.entrance_shuffle == 'restricted':
entrances_to_hint.update(ConnectorEntrances)
entrances_to_hint.update(OtherEntrances)
if world.worlds[player].options.mode == 'inverted':
if multiworld.worlds[player].options.mode == 'inverted':
entrances_to_hint.update({'Inverted Dark Sanctuary': 'The dark sanctuary cave'})
entrances_to_hint.update({'Inverted Big Bomb Shop': 'The old hero\'s dark home'})
entrances_to_hint.update({'Inverted Links House': 'The old hero\'s light home'})
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.worlds[player].options.entrance_shuffle != 'insanity':
if multiworld.worlds[player].options.entrance_shuffle != 'insanity':
entrances_to_hint.update(InsanityEntrances)
if world.shuffle_ganon:
if world.worlds[player].options.mode == 'inverted':
if multiworld.shuffle_ganon:
if multiworld.worlds[player].options.mode == '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.worlds[player].options.entrance_shuffle not in ['vanilla', 'dungeons_simple', 'dungeons_full',
hint_count = 4 if multiworld.worlds[player].options.entrance_shuffle not in ['vanilla', 'dungeons_simple', 'dungeons_full',
'dungeons_crossed'] else 0
for entrance in all_entrances:
if entrance.name in entrances_to_hint:
@@ -2324,77 +2328,77 @@ 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.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
locations_to_hint.extend(InconvenientVanillaLocations)
local_random.shuffle(locations_to_hint)
hint_count = 3 if world.worlds[player].options.entrance_shuffle not in ['vanilla', 'dungeons_simple', 'dungeons_full',
hint_count = 3 if multiworld.worlds[player].options.entrance_shuffle 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):
first_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item)
second_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item)
first_item = hint_text(multiworld.get_location('Swamp Palace - West Chest', player).item)
second_item = hint_text(multiworld.get_location('Swamp Palace - Big Key Chest', player).item)
else:
second_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item)
first_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item)
second_item = hint_text(multiworld.get_location('Swamp Palace - West Chest', player).item)
first_item = hint_text(multiworld.get_location('Swamp Palace - Big Key Chest', player).item)
this_hint = ('The westmost chests in Swamp Palace contain ' + first_item + ' and ' + second_item + '.')
tt[hint_locations.pop(0)] = this_hint
elif location == 'Mire Left':
if local_random.randint(0, 1):
first_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item)
second_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item)
first_item = hint_text(multiworld.get_location('Misery Mire - Compass Chest', player).item)
second_item = hint_text(multiworld.get_location('Misery Mire - Big Key Chest', player).item)
else:
second_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item)
first_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item)
second_item = hint_text(multiworld.get_location('Misery Mire - Compass Chest', player).item)
first_item = hint_text(multiworld.get_location('Misery Mire - Big Key Chest', player).item)
this_hint = ('The westmost chests in Misery Mire contain ' + first_item + ' and ' + second_item + '.')
tt[hint_locations.pop(0)] = this_hint
elif location == 'Tower of Hera - Big Key Chest':
this_hint = 'Waiting in the Tower of Hera basement leads to ' + hint_text(
world.get_location(location, player).item) + '.'
multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
elif location == 'Ganons Tower - Big Chest':
this_hint = 'The big chest in Ganon\'s Tower contains ' + hint_text(
world.get_location(location, player).item) + '.'
multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
elif location == 'Thieves\' Town - Big Chest':
this_hint = 'The big chest in Thieves\' Town contains ' + hint_text(
world.get_location(location, player).item) + '.'
multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
elif location == 'Ice Palace - Big Chest':
this_hint = 'The big chest in Ice Palace contains ' + hint_text(
world.get_location(location, player).item) + '.'
multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
elif location == 'Eastern Palace - Big Key Chest':
this_hint = 'The antifairy guarded chest in Eastern Palace contains ' + hint_text(
world.get_location(location, player).item) + '.'
multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
elif location == 'Sahasrahla':
this_hint = 'Sahasrahla seeks a green pendant for ' + hint_text(
world.get_location(location, player).item) + '.'
multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
elif location == 'Graveyard Cave':
this_hint = 'The cave north of the graveyard contains ' + hint_text(
world.get_location(location, player).item) + '.'
multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
else:
this_hint = location + ' contains ' + hint_text(world.get_location(location, player).item) + '.'
this_hint = location + ' contains ' + hint_text(multiworld.get_location(location, player).item) + '.'
tt[hint_locations.pop(0)] = this_hint
# Lastly we write hints to show where certain interesting items are.
items_to_hint = RelevantItems.copy()
if world.worlds[player].options.small_key_shuffle.hints_useful:
if multiworld.worlds[player].options.small_key_shuffle.hints_useful:
items_to_hint |= item_name_groups["Small Keys"]
if world.worlds[player].options.big_key_shuffle.hints_useful:
if multiworld.worlds[player].options.big_key_shuffle.hints_useful:
items_to_hint |= item_name_groups["Big Keys"]
if world.worlds[player].options.hints == "full":
if multiworld.worlds[player].options.hints == "full":
hint_count = len(hint_locations) # fill all remaining hint locations with Item hints.
else:
hint_count = 5 if world.worlds[player].options.entrance_shuffle not in ['vanilla', 'dungeons_simple', 'dungeons_full',
hint_count = 5 if multiworld.worlds[player].options.entrance_shuffle 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)
locations = multiworld.find_items_in_locations(items_to_hint, player, True)
local_random.shuffle(locations)
# make locked locations less likely to appear as hint,
# chances are the lock means the player already knows.
@@ -2414,15 +2418,15 @@ def write_strings(rom, world, player):
# We still need the older hints of course. Those are done here.
silverarrows = world.find_item_locations('Silver Bow', player, True)
silverarrows = multiworld.find_item_locations('Silver Bow', player, True)
local_random.shuffle(silverarrows)
silverarrow_hint = (
' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!'
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 (w.difficulty_requirements.progressive_bow_limit >= 2 or (
world.worlds[player].options.swordless or world.worlds[player].options.glitches_required == 'no_glitches')):
prog_bow_locs = world.find_item_locations('Progressive Bow', player, True)
if multiworld.worlds[player].has_progressive_bows and (w.difficulty_requirements.progressive_bow_limit >= 2 or (
multiworld.worlds[player].options.swordless or multiworld.worlds[player].options.glitches_required == 'no_glitches')):
prog_bow_locs = multiworld.find_item_locations('Progressive Bow', player, True)
local_random.shuffle(prog_bow_locs)
found_bow = False
found_bow_alt = False
@@ -2437,34 +2441,34 @@ def write_strings(rom, world, player):
silverarrow_hint = (' %s?' % hint_text(bow_loc).replace('Ganon\'s', 'my'))
tt[target] = 'Did you find the silver arrows%s' % silverarrow_hint
crystal5 = world.find_item('Crystal 5', player)
crystal6 = world.find_item('Crystal 6', player)
crystal5 = multiworld.find_item('Crystal 5', player)
crystal6 = multiworld.find_item('Crystal 6', player)
tt['bomb_shop'] = 'Big Bomb?\nMy supply is blocked until you clear %s and %s.' % (
crystal5.hint_text, crystal6.hint_text)
greenpendant = world.find_item('Green Pendant', player)
greenpendant = multiworld.find_item('Green Pendant', player)
tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant.hint_text
if world.worlds[player].options.crystals_needed_for_gt == 1:
if multiworld.worlds[player].options.crystals_needed_for_gt == 1:
tt['sign_ganons_tower'] = 'You need a crystal to enter.'
else:
tt['sign_ganons_tower'] = f'You need {world.worlds[player].options.crystals_needed_for_gt} crystals to enter.'
tt['sign_ganons_tower'] = f'You need {multiworld.worlds[player].options.crystals_needed_for_gt} crystals to enter.'
if world.worlds[player].options.goal == 'bosses':
if multiworld.worlds[player].options.goal == 'bosses':
tt['sign_ganon'] = 'You need to kill all bosses, Ganon last.'
elif world.worlds[player].options.goal == 'ganon_pedestal':
elif multiworld.worlds[player].options.goal == 'ganon_pedestal':
tt['sign_ganon'] = 'You need to pull the pedestal to defeat Ganon.'
elif world.worlds[player].options.goal == "ganon":
if world.worlds[player].options.crystals_needed_for_ganon == 1:
elif multiworld.worlds[player].options.goal == "ganon":
if multiworld.worlds[player].options.crystals_needed_for_ganon == 1:
tt['sign_ganon'] = 'You need a crystal to beat Ganon and have beaten Agahnim atop Ganons Tower.'
else:
tt['sign_ganon'] = f'You need {world.worlds[player].options.crystals_needed_for_ganon} crystals to beat Ganon and ' \
tt['sign_ganon'] = f'You need {multiworld.worlds[player].options.crystals_needed_for_ganon} crystals to beat Ganon and ' \
f'have beaten Agahnim atop Ganons Tower'
else:
if world.worlds[player].options.crystals_needed_for_ganon == 1:
if multiworld.worlds[player].options.crystals_needed_for_ganon == 1:
tt['sign_ganon'] = 'You need a crystal to beat Ganon.'
else:
tt['sign_ganon'] = f'You need {world.worlds[player].options.crystals_needed_for_ganon} crystals to beat Ganon.'
tt['sign_ganon'] = f'You need {multiworld.worlds[player].options.crystals_needed_for_ganon} crystals to beat Ganon.'
tt['uncle_leaving_text'] = Uncle_texts[local_random.randint(0, len(Uncle_texts) - 1)]
tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[local_random.randint(0, len(Triforce_texts) - 1)]
@@ -2475,12 +2479,12 @@ def write_strings(rom, world, player):
tt['blind_by_the_light'] = Blind_texts[local_random.randint(0, len(Blind_texts) - 1)]
triforce_pieces_required = max(0, w.treasure_hunt_required -
sum(1 for item in world.precollected_items[player] if item.name == "Triforce Piece"))
sum(1 for item in multiworld.precollected_items[player] if item.name == "Triforce Piece"))
if world.worlds[player].options.goal in ['triforce_hunt', 'local_triforce_hunt']:
if multiworld.worlds[player].options.goal 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.worlds[player].options.goal == 'triforce_hunt' and world.players > 1:
if multiworld.worlds[player].options.goal == 'triforce_hunt' and multiworld.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!'
@@ -2494,7 +2498,7 @@ def write_strings(rom, world, player):
"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\n%d Triforce piece out of %d, I can reassemble it." % \
(triforce_pieces_required, w.treasure_hunt_total)
elif world.worlds[player].options.goal in ['pedestal']:
elif multiworld.worlds[player].options.goal in ['pedestal']:
tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.'
tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.'
tt['sign_ganon'] = 'You need to get to the pedestal... Ganon is invincible!'
@@ -2503,44 +2507,44 @@ 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 triforce_pieces_required > 1:
if world.worlds[player].options.goal == 'ganon_triforce_hunt' and world.players > 1:
if multiworld.worlds[player].options.goal == 'ganon_triforce_hunt' and multiworld.players > 1:
tt['sign_ganon'] = 'You need to find %d Triforce pieces out of %d with your friends to defeat Ganon.' % \
(triforce_pieces_required, w.treasure_hunt_total)
elif world.worlds[player].options.goal in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
elif multiworld.worlds[player].options.goal in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
tt['sign_ganon'] = 'You need to find %d Triforce pieces out of %d to defeat Ganon.' % \
(triforce_pieces_required, w.treasure_hunt_total)
else:
if world.worlds[player].options.goal == 'ganon_triforce_hunt' and world.players > 1:
if multiworld.worlds[player].options.goal == 'ganon_triforce_hunt' and multiworld.players > 1:
tt['sign_ganon'] = 'You need to find %d Triforce piece out of %d with your friends to defeat Ganon.' % \
(triforce_pieces_required, w.treasure_hunt_total)
elif world.worlds[player].options.goal in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
elif multiworld.worlds[player].options.goal in ['ganon_triforce_hunt', 'local_ganon_triforce_hunt']:
tt['sign_ganon'] = 'You need to find %d Triforce piece out of %d to defeat Ganon.' % \
(triforce_pieces_required, w.treasure_hunt_total)
tt['kakariko_tavern_fisherman'] = TavernMan_texts[local_random.randint(0, len(TavernMan_texts) - 1)]
pedestalitem = world.get_location('Master Sword Pedestal', player).item
pedestalitem = multiworld.get_location('Master Sword Pedestal', player).item
pedestal_text = 'Some Hot Air' if pedestalitem is None else hint_text(pedestalitem,
True) if pedestalitem.pedestal_hint_text is not None else 'Unknown Item'
tt['mastersword_pedestal_translated'] = pedestal_text
pedestal_credit_text = 'and the Hot Air' if pedestalitem is None else \
w.pedestal_credit_texts.get(pedestalitem.code, 'and the Unknown Item')
etheritem = world.get_location('Ether Tablet', player).item
etheritem = multiworld.get_location('Ether Tablet', player).item
ether_text = 'Some Hot Air' if etheritem is None else hint_text(etheritem,
True) if etheritem.pedestal_hint_text is not None else 'Unknown Item'
tt['tablet_ether_book'] = ether_text
bombositem = world.get_location('Bombos Tablet', player).item
bombositem = multiworld.get_location('Bombos Tablet', player).item
bombos_text = 'Some Hot Air' if bombositem is None else hint_text(bombositem,
True) if bombositem.pedestal_hint_text is not None else 'Unknown Item'
tt['tablet_bombos_book'] = bombos_text
# inverted spawn menu changes
if world.worlds[player].options.mode == 'inverted':
if multiworld.worlds[player].options.mode == 'inverted':
tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n{CHOICE3}"
tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n Mountain Cave\n{CHOICE2}"
for at, text, _ in world.worlds[player].options.plando_texts:
for at, text, _ in multiworld.worlds[player].options.plando_texts:
if at not in tt:
raise Exception(f"No text target \"{at}\" found.")
@@ -2551,22 +2555,22 @@ def write_strings(rom, world, player):
credits = Credits()
sickkiditem = world.get_location('Sick Kid', player).item
sickkiditem = multiworld.get_location('Sick Kid', player).item
sickkiditem_text = local_random.choice(SickKid_texts) \
if sickkiditem is None or sickkiditem.code not in w.sickkid_credit_texts \
else w.sickkid_credit_texts[sickkiditem.code]
zoraitem = world.get_location('King Zora', player).item
zoraitem = multiworld.get_location('King Zora', player).item
zoraitem_text = local_random.choice(Zora_texts) \
if zoraitem is None or zoraitem.code not in w.zora_credit_texts \
else w.zora_credit_texts[zoraitem.code]
magicshopitem = world.get_location('Potion Shop', player).item
magicshopitem = multiworld.get_location('Potion Shop', player).item
magicshopitem_text = local_random.choice(MagicShop_texts) \
if magicshopitem is None or magicshopitem.code not in w.magicshop_credit_texts \
else w.magicshop_credit_texts[magicshopitem.code]
fluteboyitem = world.get_location('Flute Spot', player).item
fluteboyitem = multiworld.get_location('Flute Spot', player).item
fluteboyitem_text = local_random.choice(FluteBoy_texts) \
if fluteboyitem is None or fluteboyitem.code not in w.fluteboy_credit_texts \
else w.fluteboy_credit_texts[fluteboyitem.code]
@@ -2595,7 +2599,7 @@ def write_strings(rom, world, player):
rom.write_bytes(0x76CC0, [byte for p in pointers for byte in [p & 0xFF, p >> 8 & 0xFF]])
def set_inverted_mode(world, player, rom):
def set_inverted_mode(multiworld: MultiWorld, player: int, rom: LocalRom):
rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals
rom.write_byte(snes_to_pc(0x02B34D), 0xF0)
rom.write_byte(snes_to_pc(0x06DB78), 0x8B)
@@ -2613,12 +2617,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.worlds[player].options.entrance_shuffle == 'vanilla':
if multiworld.worlds[player].options.entrance_shuffle == '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.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle 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)
@@ -2676,7 +2680,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.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_full', 'dungeons_simple', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle 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])
@@ -2739,7 +2743,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.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle 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)
@@ -2776,10 +2780,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.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle 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.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
if multiworld.worlds[player].options.entrance_shuffle 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)

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ from Utils import int16_as_bytes
from worlds.generic.Rules import add_rule
from BaseClasses import CollectionState
from BaseClasses import CollectionState, Item, MultiWorld
from .SubClasses import ALttPLocation
from .Items import item_name_groups
@@ -159,7 +159,7 @@ shop_class_mapping = {ShopType.UpgradeShop: UpgradeShop,
ShopType.TakeAny: TakeAny}
def push_shop_inventories(multiworld):
def push_shop_inventories(multiworld: MultiWorld):
all_shops = []
for world in multiworld.get_game_worlds(ALttPLocation.game):
all_shops.extend(world.shops)
@@ -183,7 +183,7 @@ def push_shop_inventories(multiworld):
world.pushed_shop_inventories.set()
def create_shops(multiworld, player: int):
def create_shops(multiworld: MultiWorld, player: int):
from .Options import RandomizeShopInventories
player_shop_table = shop_table.copy()
if multiworld.worlds[player].options.include_witch_hut:
@@ -306,7 +306,7 @@ shop_generation_types = {
}
def set_up_shops(multiworld, player: int):
def set_up_shops(multiworld: MultiWorld, player: int):
from .Options import small_key_shuffle
# TODO: move hard+ mode changes for shields here, utilizing the new shops
@@ -408,7 +408,7 @@ price_rate_display = {
}
def get_price_modifier(item) -> float:
def get_price_modifier(item: Item) -> float:
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']):
@@ -428,7 +428,7 @@ def get_price_modifier(item) -> float:
return 0.25
def get_price(multiworld, item, player: int, price_type=None):
def get_price(multiworld: MultiWorld, item: Item, player: int, price_type=None):
"""Converts a raw Rupee price into a special price type"""
from .Options import small_key_shuffle
if price_type:

View File

@@ -1,3 +1,4 @@
from BaseClasses import MultiWorld, CollectionState
from worlds.generic.Rules import set_rule, add_rule
from .StateHelpers import can_bomb_clip, has_sword, has_beam_sword, has_fire_source, can_melt_things, has_misery_mire_medallion
from .SubClasses import LTTPEntrance
@@ -5,27 +6,27 @@ from .SubClasses import LTTPEntrance
# We actually need the logic to properly "mark" these regions as Light or Dark world.
# Therefore we need to make these connections during the normal link_entrances stage, rather than during set_rules.
def underworld_glitch_connections(world, player):
specrock = world.get_region('Spectacle Rock Cave (Bottom)', player)
mire = world.get_region('Misery Mire (West)', player)
def underworld_glitch_connections(multiworld: MultiWorld, player: int):
specrock = multiworld.get_region('Spectacle Rock Cave (Bottom)', player)
mire = multiworld.get_region('Misery Mire (West)', player)
kikiskip = specrock.create_exit('Kiki Skip')
mire_to_hera = mire.create_exit('Mire to Hera Clip')
mire_to_swamp = mire.create_exit('Hera to Swamp Clip')
if world.worlds[player].fix_fake_world:
kikiskip.connect(world.get_entrance('Palace of Darkness Exit', player).connected_region)
mire_to_hera.connect(world.get_entrance('Tower of Hera Exit', player).connected_region)
mire_to_swamp.connect(world.get_entrance('Swamp Palace Exit', player).connected_region)
if multiworld.worlds[player].fix_fake_world:
kikiskip.connect(multiworld.get_entrance('Palace of Darkness Exit', player).connected_region)
mire_to_hera.connect(multiworld.get_entrance('Tower of Hera Exit', player).connected_region)
mire_to_swamp.connect(multiworld.get_entrance('Swamp Palace Exit', player).connected_region)
else:
kikiskip.connect(world.get_region('Palace of Darkness (Entrance)', player))
mire_to_hera.connect(world.get_region('Tower of Hera (Bottom)', player))
mire_to_swamp.connect(world.get_region('Swamp Palace (Entrance)', player))
kikiskip.connect(multiworld.get_region('Palace of Darkness (Entrance)', player))
mire_to_hera.connect(multiworld.get_region('Tower of Hera (Bottom)', player))
mire_to_swamp.connect(multiworld.get_region('Swamp Palace (Entrance)', player))
# For some entrances, we need to fake having pearl, because we're in fake DW/LW.
# This creates a copy of the input state that has Moon Pearl.
def fake_pearl_state(state, player):
def fake_pearl_state(state: CollectionState, player: int):
if state.has('Moon Pearl', player):
return state
fake_state = state.copy()
@@ -35,11 +36,11 @@ def fake_pearl_state(state, player):
# Sets the rules on where we can actually go using this clip.
# Behavior differs based on what type of ER shuffle we're playing.
def dungeon_reentry_rules(world, player, clip: LTTPEntrance, dungeon_region: str, dungeon_exit: str):
fix_dungeon_exits = world.worlds[player].fix_palaceofdarkness_exit
fix_fake_worlds = world.worlds[player].fix_fake_world
def dungeon_reentry_rules(multiworld: MultiWorld, player: int, clip: LTTPEntrance, dungeon_region: str, dungeon_exit: str):
fix_dungeon_exits = multiworld.worlds[player].fix_palaceofdarkness_exit
fix_fake_worlds = multiworld.worlds[player].fix_fake_world
dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0]
dungeon_entrance = [r for r in multiworld.get_region(dungeon_region, player).entrances if r.name != clip.name][0]
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':
@@ -49,64 +50,64 @@ def dungeon_reentry_rules(world, player, clip: LTTPEntrance, dungeon_region: str
elif dungeon_entrance.name == 'Agahnims Tower':
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))
add_rule(multiworld.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
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))
add_rule(multiworld.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
# 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.
def underworld_glitches_rules(world, player):
def underworld_glitches_rules(multiworld: MultiWorld, player: int):
# Ice Palace Entrance Clip
# This is the easiest one since it's a simple internal clip.
# Need to also add melting to freezor chest since it's otherwise assumed.
# Also can pick up the first jelly key from behind.
add_rule(world.get_entrance('Ice Palace (Main)', player), lambda state: can_bomb_clip(state, world.get_region('Ice Palace (Entrance)', player), player), combine='or')
add_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: can_melt_things(state, player))
add_rule(world.get_location('Ice Palace - Jelly Key Drop', player), lambda state: can_bomb_clip(state, world.get_region('Ice Palace (Entrance)', player), player), combine='or')
add_rule(multiworld.get_entrance('Ice Palace (Main)', player), lambda state: can_bomb_clip(state, multiworld.get_region('Ice Palace (Entrance)', player), player), combine='or')
add_rule(multiworld.get_location('Ice Palace - Freezor Chest', player), lambda state: can_melt_things(state, player))
add_rule(multiworld.get_location('Ice Palace - Jelly Key Drop', player), lambda state: can_bomb_clip(state, multiworld.get_region('Ice Palace (Entrance)', player), player), combine='or')
# Kiki Skip
kikiskip = world.get_entrance('Kiki Skip', player)
kikiskip = multiworld.get_entrance('Kiki Skip', player)
set_rule(kikiskip, lambda state: can_bomb_clip(state, kikiskip.parent_region, player))
dungeon_reentry_rules(world, player, kikiskip, 'Palace of Darkness (Entrance)', 'Palace of Darkness Exit')
dungeon_reentry_rules(multiworld, player, kikiskip, 'Palace of Darkness (Entrance)', 'Palace of Darkness Exit')
# Mire -> Hera -> Swamp
# Using mire keys on other dungeon doors
mire = world.get_region('Misery Mire (West)', player)
mire = multiworld.get_region('Misery Mire (West)', player)
mire_clip = lambda state: state.can_reach('Misery Mire (West)', 'Region', player) and can_bomb_clip(state, mire, player) and has_fire_source(state, player)
hera_clip = lambda state: state.can_reach('Tower of Hera (Top)', 'Region', player) and can_bomb_clip(state, world.get_region('Tower of Hera (Top)', player), player)
add_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: mire_clip(state) and state.has('Big Key (Misery Mire)', player), combine='or')
add_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: mire_clip(state), combine='or')
add_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: mire_clip(state) or hera_clip(state), combine='or')
hera_clip = lambda state: state.can_reach('Tower of Hera (Top)', 'Region', player) and can_bomb_clip(state, multiworld.get_region('Tower of Hera (Top)', player), player)
add_rule(multiworld.get_entrance('Tower of Hera Big Key Door', player), lambda state: mire_clip(state) and state.has('Big Key (Misery Mire)', player), combine='or')
add_rule(multiworld.get_entrance('Swamp Palace Small Key Door', player), lambda state: mire_clip(state), combine='or')
add_rule(multiworld.get_entrance('Swamp Palace (Center)', player), lambda state: mire_clip(state) or hera_clip(state), combine='or')
# Build the rule for SP moat.
# We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT.
# First we require a certain type of entrance shuffle, then build the rule from its pieces.
if not world.worlds[player].swamp_patch_required:
if world.worlds[player].options.entrance_shuffle in ['vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed']:
if not multiworld.worlds[player].swamp_patch_required:
if multiworld.worlds[player].options.entrance_shuffle 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))
}
inverted = world.worlds[player].options.mode == 'inverted'
inverted = multiworld.worlds[player].options.mode == 'inverted'
hera_rule = lambda state: (state.has('Moon Pearl', player) or not inverted) and \
rule_map.get(world.get_entrance('Tower of Hera', player).connected_region.name, lambda state: False)(state)
rule_map.get(multiworld.get_entrance('Tower of Hera', player).connected_region.name, lambda state: False)(state)
gt_rule = lambda state: (state.has('Moon Pearl', player) or inverted) and \
rule_map.get(world.get_entrance(('Ganons Tower' if not inverted else 'Inverted Ganons Tower'), player).connected_region.name, lambda state: False)(state)
rule_map.get(multiworld.get_entrance(('Ganons Tower' if not inverted else 'Inverted Ganons Tower'), player).connected_region.name, lambda state: False)(state)
mirrorless_moat_rule = lambda state: state.can_reach('Old Man S&Q', 'Entrance', player) and mire_clip(state) and (hera_rule(state) or gt_rule(state))
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player) or mirrorless_moat_rule(state))
add_rule(multiworld.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player) or mirrorless_moat_rule(state))
else:
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player))
add_rule(multiworld.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player))
# Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys
mire_to_hera = world.get_entrance('Mire to Hera Clip', player)
mire_to_swamp = world.get_entrance('Hera to Swamp Clip', player)
mire_to_hera = multiworld.get_entrance('Mire to Hera Clip', player)
mire_to_swamp = multiworld.get_entrance('Hera to Swamp Clip', player)
set_rule(mire_to_hera, mire_clip)
set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has('Flippers', player))
dungeon_reentry_rules(world, player, mire_to_hera, 'Tower of Hera (Bottom)', 'Tower of Hera Exit')
dungeon_reentry_rules(world, player, mire_to_swamp, 'Swamp Palace (Entrance)', 'Swamp Palace Exit')
dungeon_reentry_rules(multiworld, player, mire_to_hera, 'Tower of Hera (Bottom)', 'Tower of Hera Exit')
dungeon_reentry_rules(multiworld, player, mire_to_swamp, 'Swamp Palace (Entrance)', 'Swamp Palace Exit')

View File

@@ -0,0 +1,4 @@
{
"game": "The Messenger",
"authors": ["alwaysintreble"]
}

View File

@@ -28,6 +28,8 @@ def create_yes_no_popup(title: str, text: str, callback: Callable[[str], None])
def launch_game(*args) -> None:
"""Check the game installation, then launch it"""
prompt: ButtonsPrompt | None = None
def courier_installed() -> bool:
"""Check if Courier is installed"""
assembly_path = os.path.join(game_folder, "TheMessenger_Data", "Managed", "Assembly-CSharp.dll")
@@ -190,7 +192,7 @@ def launch_game(*args) -> None:
def launch(answer: str | None = None) -> None:
"""Launch the game."""
nonlocal args
nonlocal args, prompt
if prompt:
prompt.dismiss()
@@ -256,3 +258,5 @@ def launch_game(*args) -> None:
prompt = create_yes_no_popup("Launch Game",
"Mod installed and up to date. Would you like to launch the game now?",
launch)
else:
launch()

View File

@@ -1,7 +1,7 @@
from typing import TYPE_CHECKING
from BaseClasses import CollectionState
from worlds.generic.Rules import CollectionRule, add_rule, allow_self_locking_items
from BaseClasses import CollectionState, CollectionRule
from worlds.generic.Rules import add_rule, allow_self_locking_items
from .constants import NOTES, PHOBEKINS
from .options import MessengerAccessibility